-
Notifications
You must be signed in to change notification settings - Fork 40
test: ✅ Parity body tests #986
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f8cbcf5
05e4411
d605f45
48b108e
3ddfb2f
d3d7138
6936673
ff9e2ed
41b63b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,12 +37,40 @@ type fixture struct { | |
| path string | ||
| reqBody []byte | ||
|
|
||
| // upstreamStatus / upstreamBody are what the httptest backend serves | ||
| // on the proxy path, and what the parity harness synthesizes into the | ||
| // extproc ResponseHeaders/ResponseBody messages. Zero status skips | ||
| // the response phase entirely (deny-at-request scenarios). | ||
| upstreamStatus int | ||
| upstreamBody []byte | ||
| // upstreamStatus / upstreamBody / upstreamContentType are what the | ||
| // httptest backend serves on the proxy path, and what the parity | ||
| // harness synthesizes into the extproc ResponseHeaders/ResponseBody | ||
| // messages. Zero status skips the response phase entirely (deny-at- | ||
| // request scenarios). Empty content-type defaults to application/json. | ||
| upstreamStatus int | ||
| upstreamBody []byte | ||
| upstreamContentType string | ||
|
|
||
| // pipelineRefusedPreRun asserts the listener refused before the | ||
| // pipeline (e.g. body overflow). Default false: every listener must | ||
| // record. Combine with expectedWireStatus to pin the wire code. | ||
| pipelineRefusedPreRun bool | ||
|
|
||
| // expectedWireStatus, when non-zero, is asserted against every | ||
| // listener's wire status. Only meaningful together with | ||
| // pipelineRefusedPreRun — on the success path extproc has no HTTP | ||
| // transport and reports 0. | ||
| expectedWireStatus int | ||
|
evaline-ju marked this conversation as resolved.
|
||
|
|
||
| // expectedPluginEvents anchors correctness — maps each expected | ||
| // SessionEvent.Plugins key to its exact JSON. Empty means "don't | ||
| // assert content beyond the pairwise diff." Fixtures that want | ||
| // bug-catching (not just drift-catching) fill this in. | ||
| expectedPluginEvents map[string]string | ||
| } | ||
|
|
||
| // contentType returns the fixture's response content-type or a sensible | ||
| // default. Kept as a helper so both driver paths stay compact. | ||
| func (f fixture) contentType() string { | ||
| if f.upstreamContentType != "" { | ||
| return f.upstreamContentType | ||
| } | ||
| return "application/json" | ||
| } | ||
|
|
||
| // buildSpyPipeline routes construction through plugins.BuildWithDeps | ||
|
|
@@ -63,6 +91,12 @@ func spyEntry(name string, cfg spyConfig) config.PluginEntry { | |
| // (Host casing, timestamps, RequestID, Duration, TLS, Identity) are | ||
| // excluded — expanding coverage there is a follow-up fixture pass. | ||
| type observation struct { | ||
| // PipelineRan is false when the listener rejected the request before | ||
| // the pipeline (e.g. request body too large). Overflow fixtures then | ||
| // assert wire status only and skip session-event comparisons. | ||
| PipelineRan bool | ||
| WireStatus int // captured from the transport, not from the session event | ||
|
|
||
| Phase string | ||
| StatusCode int | ||
| Error *errorSummary | ||
|
|
@@ -251,7 +285,7 @@ func runExtproc(t *testing.T, f fixture, wantPhase pipeline.SessionPhase) *obser | |
| ResponseHeaders: &extprocv3.HttpHeaders{ | ||
| Headers: makeHeaders( | ||
| ":status", fmt.Sprintf("%d", f.upstreamStatus), | ||
| "content-type", "application/json", | ||
| "content-type", f.contentType(), | ||
| "content-length", fmt.Sprintf("%d", len(f.upstreamBody)), | ||
| ), | ||
| }, | ||
|
|
@@ -284,7 +318,41 @@ func runExtproc(t *testing.T, f fixture, wantPhase pipeline.SessionPhase) *obser | |
| } | ||
| } | ||
|
|
||
| return observe(t, store, f.direction, wantPhase) | ||
| return finalizeObservation(t, f, observe(t, store, f.direction, wantPhase), extprocWireStatus(stream)) | ||
| } | ||
|
|
||
| // extprocWireStatus reads the HTTP status from an ImmediateResponse if | ||
| // one was sent, else 0. Pipeline-ran is inferred from observe(): a | ||
| // session event exists iff the pipeline reached the recording site. | ||
| func extprocWireStatus(stream *mockStream) int { | ||
| for _, r := range stream.responses { | ||
| if imm := r.GetImmediateResponse(); imm != nil && imm.Status != nil { | ||
| return int(imm.Status.Code) | ||
| } | ||
| } | ||
| return 0 | ||
| } | ||
|
|
||
| // finalizeObservation stamps PipelineRan + WireStatus onto an | ||
| // observation. pipelineRefusedPreRun is a strict expectation: the | ||
| // listener MUST refuse before the pipeline. A missing event when the | ||
| // fixture didn't opt in is a bug; an event present when it did is | ||
| // also a bug (the listener silently stopped enforcing the cap). | ||
| func finalizeObservation(t *testing.T, f fixture, obs *observation, wireStatus int) *observation { | ||
| t.Helper() | ||
| if obs == nil { | ||
| if !f.pipelineRefusedPreRun { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. must-fix — So That is exactly the shared-drop failure mode Two small changes close it:
obs.PipelineRan = true
if f.pipelineRefusedPreRun {
t.Errorf("fixture %q expected the listener to refuse before the pipeline, but an event was recorded", f.name)
}
|
||
| t.Errorf("no session event recorded for fixture %q; set pipelineRefusedPreRun=true if expected", f.name) | ||
| return nil | ||
| } | ||
| return &observation{PipelineRan: false, WireStatus: wireStatus} | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
| if f.pipelineRefusedPreRun { | ||
| t.Errorf("fixture %q expected the listener to refuse before the pipeline, but an event was recorded (wireStatus=%d)", f.name, wireStatus) | ||
| } | ||
| obs.PipelineRan = true | ||
| obs.WireStatus = wireStatus | ||
| return obs | ||
| } | ||
|
|
||
| // --- reverseproxy driver ------------------------------------------------- | ||
|
|
@@ -306,7 +374,7 @@ func runReverseProxy(t *testing.T, f fixture, wantPhase pipeline.SessionPhase) * | |
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
| w.Header().Set("Content-Type", "application/json") | ||
| w.Header().Set("Content-Type", f.contentType()) | ||
| w.WriteHeader(f.upstreamStatus) | ||
| _, _ = w.Write(f.upstreamBody) | ||
| })) | ||
|
|
@@ -352,7 +420,7 @@ func runReverseProxy(t *testing.T, f fixture, wantPhase pipeline.SessionPhase) * | |
| t.Errorf("reverseproxy: fixture %q asked for deny but upstream was reached", f.name) | ||
| } | ||
|
|
||
| return observe(t, store, pipeline.Inbound, wantPhase) | ||
| return finalizeObservation(t, f, observe(t, store, pipeline.Inbound, wantPhase), resp.StatusCode) | ||
| } | ||
|
|
||
| // --- forwardproxy driver ------------------------------------------------- | ||
|
|
@@ -374,7 +442,7 @@ func runForwardProxy(t *testing.T, f fixture, wantPhase pipeline.SessionPhase) * | |
| w.WriteHeader(http.StatusInternalServerError) | ||
| return | ||
| } | ||
| w.Header().Set("Content-Type", "application/json") | ||
| w.Header().Set("Content-Type", f.contentType()) | ||
| w.WriteHeader(f.upstreamStatus) | ||
| _, _ = w.Write(f.upstreamBody) | ||
| })) | ||
|
|
@@ -425,7 +493,7 @@ func runForwardProxy(t *testing.T, f fixture, wantPhase pipeline.SessionPhase) * | |
| t.Errorf("forwardproxy: fixture %q asked for deny but upstream was reached", f.name) | ||
| } | ||
|
|
||
| return observe(t, store, pipeline.Outbound, wantPhase) | ||
| return finalizeObservation(t, f, observe(t, store, pipeline.Outbound, wantPhase), resp.StatusCode) | ||
| } | ||
|
|
||
| // --- construction-only helpers ------------------------------------------- | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note (not a defect — please just make sure this doesn't get lost)
This line and its five siblings are the fix for a currently-broken
main, not test cleanup incidental to the parity work.maintipbafeceb0is red:Go CI (authlib)→ Lint fails with#677 (
5d56f48f) changed the signature toNewUpstreamClient(extraRootsPEM []byte, insecure bool)and updated onlytlsbridge/upstream_test.goandcmd/authbridge-proxy/main.go. It missed the five call sites here plus one intunnelreason_integration_test.go. No build tag on these files, so it's a straight compile failure in theforwardproxytest package.mainhas been red since69b48de6.The
falsevalues are right — they preserve the pre-#677 verifying behavior at every site, includingTestBridge_UnverifiableUpstream_FallsOpenToTunnel, which needs verification enabled for its premise to hold.Two asks:
ff9e2edinto its ownfix:PR somaingoes green immediately instead of waiting on review of the parity work. If you'd rather keep it here, that's fine too — but then this PR should be treated as merge-blocking-urgent.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 PR body mention updated