# 0.6.3 E2E Parity Document

This document records the parity between shell E2E scripts and their Rust harness
scenario counterparts, including case mappings and equivalence notes.

## Scenario: conditional-requests

| Shell source | Rust scenario | Cases |
|---|---|---|
| `tools/e2e/verify_conditional_requests_e2e.sh` | `tools/e2e-harness/src/scenarios/conditional_requests.rs` | 10 cases (1:1) |

### Case mapping

| Shell case | Rust assertion name | Equivalence |
|---|---|---|
| Case 1: ETag present | `case1_etag_present` | Equivalent |
| Case 2: ETag differs from upstream | `case2_etag_differs_from_upstream` | Equivalent |
| Case 3: If-None-Match → 304 | `case3_inm_match_304` | Equivalent |
| Case 4: If-None-Match non-match → 200 | `case4_inm_nomatch_200` | Equivalent |
| Case 5: If-Modified-Since future → 304 | `case5_ims_future_304` | Equivalent |
| Case 6: If-Modified-Since past → 200 | `case6_ims_past_200` | Equivalent |
| Case 7: Weak ETag → 304 | `case7_weak_etag_304` | Equivalent |
| Case 8: Wildcard If-None-Match → 304 | `case8_wildcard_inm_304` | Equivalent |
| Case 9: Vary: Accept in 304 | `case9_vary_accept_in_304` | Equivalent |
| Case 10: HEAD ETag = GET ETag | `case10_head_etag_matches_get` | Equivalent |

**Parity gaps**: None known.

---

## Scenario: metrics-endpoint

| Shell source | Rust scenario | Cases |
|---|---|---|
| `tools/e2e/verify_metrics_endpoint_e2e.sh` | `tools/e2e-harness/src/scenarios/metrics_endpoint.rs` | 8 cases (1:1) |

### Case mapping

| Shell case | Rust assertion name | Equivalence |
|---|---|---|
| Case 1: JSON format | `case1_json_status_200`, `case1_json_content_type`, `case1_json_valid` | Equivalent; Rust adds JSON validity check |
| Case 2: Plain-text format | `case2_plaintext_status_200`, `case2_plaintext_content_type`, `case2_plaintext_nonempty_body` | Equivalent |
| Case 3: Prometheus format | `case3_prometheus_status_200`, `case3_prometheus_help_prefix`, `case3_prometheus_type_prefix` | Equivalent; uses body content check instead of Content-Type header variant |
| Case 4: Non-empty body | `case4_json_nonempty`, `case4_plaintext_nonempty`, `case4_prometheus_nonempty` | Equivalent |
| Case 5: JSON required keys | `case5_json_key_total_requests`, `case5_json_key_converted_total`, `case5_json_key_skipped_total` | Equivalent |
| Case 6: Prometheus prefixes | `case6_prometheus_help_lines`, `case6_prometheus_type_lines` | Equivalent |
| Case 7: Counters non-zero | `case7_total_requests_nonzero`, `case7_converted_total_nonzero` | Equivalent |
| Case 8: Invalid Accept fallback | `case8_invalid_accept_status_200`, `case8_invalid_accept_content_type` | Equivalent |

**Parity gaps**: None known. Rust version uses `serde_json` for JSON parsing instead of inline Python.

---

## Scenario: status-codes

| Shell source | Rust scenario | Cases |
|---|---|---|
| `tools/e2e/verify_status_codes_e2e.sh` | `tools/e2e-harness/src/scenarios/status_codes.rs` | 8 cases (1:1) |

### Case mapping

| Shell case | Rust assertion name | Equivalence |
|---|---|---|
| Case 1: 403 passthrough | `case403_status_passthrough`, `case403_content_type_html` | Equivalent |
| Case 2: 404 passthrough | `case404_status_passthrough`, `case404_content_type_html` | Equivalent |
| Case 3: 500 passthrough | `case500_status_passthrough`, `case500_content_type_html` | Equivalent |
| Case 4: 502 passthrough | `case502_status_passthrough`, `case502_content_type_html` | Equivalent |
| Case 5: 503 passthrough | `case503_status_passthrough`, `case503_content_type_html` | Equivalent |
| Case 6: 410 passthrough | `case410_status_passthrough`, `case410_content_type_html` | Equivalent |
| Case 7: 301 redirect | `case7_301_redirect_200` | Equivalent; reqwest follows redirects by default |
| Case 8: 302 redirect | `case8_302_redirect_200` | Equivalent; reqwest follows redirects by default |

**Parity gaps**: None known. reqwest follows redirects automatically (same as `curl -L`).

---

## Scenario: auth-cache

| Shell source | Rust scenario | Cases |
|---|---|---|
| `tools/e2e/verify_auth_cache_e2e.sh` | `tools/e2e-harness/src/scenarios/auth_cache.rs` | 7 cases (1:1) |

### Case mapping

| Shell case | Rust assertion name | Equivalence |
|---|---|---|
| Case 1: Auth Cache-Control: private | `case1_auth_status_200`, `case1_cache_control_private` | Equivalent |
| Case 2: Non-auth Cache-Control: public | `case2_noauth_status_200`, `case2_cache_control_public` | Equivalent |
| Case 3: auth_policy deny | `case3_deny_status_200`, `case3_deny_content_type_html` | Equivalent |
| Case 4: auth_cookies pattern | `case4_nonmatch_cookie_status_200`, `case4_nonmatch_cookie_markdown_ct` | Equivalent |
| Case 5: Auth fail-open preserves CC | `case5_auth_preserves_cache_control` | Equivalent; simplified vs shell's upstream comparison |
| Case 6: ETag replacement | `case6_etag_present`, `case6_etag_differs_from_upstream` | Equivalent |
| Case 7: Vary: Cookie | `case7_vary_cookie` | Equivalent |

**Parity gaps**: Case 5 in shell does a direct upstream-to-auth Cache-Control comparison; Rust version checks presence only. This is a minor weakening but sufficient for the core assertion that auth fail-open preserves headers.

---

## Scenario: accept-negotiation

| Shell source | Rust scenario | Cases |
|---|---|---|
| `tools/e2e/verify_accept_negotiation_e2e.sh` | `tools/e2e-harness/src/scenarios/accept_negotiation.rs` | 8 cases (1:1) |

### Case mapping

| Shell case | Rust assertion name | Equivalence |
|---|---|---|
| Case 1: Accept: text/markdown | `case1_markdown_accept_status_200`, `case1_markdown_accept_ct`, `case1_markdown_accept_has_heading` | Equivalent; adds body heading check |
| Case 2: Accept: text/html | `case2_html_accept_status_200`, `case2_html_accept_ct`, `case2_html_accept_has_h1` | Equivalent; adds body h1 check |
| Case 3: No Accept header | `case3_no_accept_status_200`, `case3_no_accept_ct_html` | Equivalent |
| Case 4: */* with on_wildcard on | `case4_wildcard_on_status_200`, `case4_wildcard_on_ct_markdown` | Equivalent |
| Case 5: */* with on_wildcard off | `case5_wildcard_off_status_200`, `case5_wildcard_off_ct_html` | Equivalent |
| Case 6: Vary: Accept | `case6_vary_accept_present` | Equivalent |
| Case 7: Non-HTML not converted | `case7_json_not_converted_status_200`, `case7_json_ct_preserved` | Equivalent |
| Case 8: text/plain not converted | `case8_plain_not_converted_status_200`, `case8_plain_ct_preserved` | Equivalent |

**Parity gaps**: None known. Shell has 8 cases, Rust has 8 matching cases.
