Skip to content

test(router) Proof of SSE buffer issues#2765

Open
dbinnersley wants to merge 1 commit into
wundergraph:mainfrom
dbinnersley:sse-buffer-issue-proof
Open

test(router) Proof of SSE buffer issues#2765
dbinnersley wants to merge 1 commit into
wundergraph:mainfrom
dbinnersley:sse-buffer-issue-proof

Conversation

@dbinnersley
Copy link
Copy Markdown

@dbinnersley dbinnersley commented Apr 14, 2026

Summary by CodeRabbit

  • Tests
    • Added regression tests for GraphQL SSE subscriptions, ensuring payloads remain stable across multiple reads and validating proper subscription completion and closure handling.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/docs-website.
  • I have read the Contributors Guide.

Open Source AI Manifesto

This project follows the principles of the Open Source AI Manifesto. Please ensure your contribution aligns with its principles.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

Walkthrough

Added a new Go test file introducing regression tests for GraphQL SSE subscription payload stability. Tests verify that received payloads remain unchanged after subsequent read operations and during burst message sequences using a custom subscription updater with httptest SSE server.

Changes

Cohort / File(s) Summary
GraphQL SSE Regression Tests
router/core/graphql_sse_regression_test.go
New test file with custom aliasingSubscriptionUpdater for capturing SSE payloads and lifecycle events. Two regression tests verify payload stability: one for single payloads after reads, one for burst payloads (64 events) using httptest SSE server with deterministic event: next and event: complete frames.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'test(router) Proof of SSE buffer issues' directly corresponds to the main change: adding regression tests for SSE buffer stability. The title accurately summarizes the primary objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
router/core/graphql_sse_regression_test.go (1)

84-171: Extract common SSE test wiring to reduce duplication.

Both tests repeat nearly identical server/client setup and completion waits; extracting a shared helper will make future SSE regression additions cheaper and less error-prone.

Also applies to: 173-269

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router/core/graphql_sse_regression_test.go` around lines 84 - 171, The test
repeats SSE server and client setup and wait logic (in
TestGraphQLSubscriptionClient_SSESingleLinePayloadsRemainStableAfterNextRead and
again at lines 173-269); extract a shared helper (eg. setupSSETest or
runSSETest) that accepts the payload sequence and an updater
(newAliasingSubscriptionUpdater) and returns the collected updates and any
error; move common pieces: httptest.NewServer handler wiring (serverDone,
flusher, headers, sending events), creating engineCtx/requestCtx and
graphql_datasource.NewGraphQLSubscriptionClient with WithReadTimeout, launching
client.Subscribe, and the completion/close waits into that helper, then call it
from both tests to reduce duplication.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@router/core/graphql_sse_regression_test.go`:
- Around line 91-115: The httptest handler is calling test assertions
(require.True / t.Fatal) inside its goroutine which can panic; instead capture
any handler-side errors and signals and send them over a channel to be asserted
in the main test goroutine. Modify the server handler created in
httptest.NewServer (the anonymous http.HandlerFunc) to replace require.True(t,
ok) and t.Fatal(...) with sending an error or boolean into a dedicated errCh (or
doneCh) and close/send on serverDone/updater-firstUpdate channels as needed,
then in the main test goroutine receive from errCh and perform the
require/assert (e.g., check the flusher ok, timeout errors) and fail the test
there. Ensure you reference the handler's flusher check and timeouts around
updater.firstUpdate and serverDone so all assertions happen in the main test
goroutine.

---

Nitpick comments:
In `@router/core/graphql_sse_regression_test.go`:
- Around line 84-171: The test repeats SSE server and client setup and wait
logic (in
TestGraphQLSubscriptionClient_SSESingleLinePayloadsRemainStableAfterNextRead and
again at lines 173-269); extract a shared helper (eg. setupSSETest or
runSSETest) that accepts the payload sequence and an updater
(newAliasingSubscriptionUpdater) and returns the collected updates and any
error; move common pieces: httptest.NewServer handler wiring (serverDone,
flusher, headers, sending events), creating engineCtx/requestCtx and
graphql_datasource.NewGraphQLSubscriptionClient with WithReadTimeout, launching
client.Subscribe, and the completion/close waits into that helper, then call it
from both tests to reduce duplication.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ff9c56e-ae10-42f4-ac82-9f36594f9dba

📥 Commits

Reviewing files that changed from the base of the PR and between db63fe9 and df1fa5a.

📒 Files selected for processing (1)
  • router/core/graphql_sse_regression_test.go

Comment on lines +91 to +115
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
flusher, ok := w.(http.Flusher)
require.True(t, ok)

w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")

_, _ = fmt.Fprintf(w, "event: next\ndata: %s\n\n", firstPayload)
flusher.Flush()

select {
case <-updater.firstUpdate:
case <-time.After(time.Second):
t.Fatal("timed out waiting for first SSE update to be observed")
}

_, _ = fmt.Fprintf(w, "event: next\ndata: %s\n\n", secondPayload)
flusher.Flush()

_, _ = fmt.Fprint(w, "event: complete\n\n")
flusher.Flush()

close(serverDone)
}))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only verification: find FailNow-style assertions in handler goroutines.
rg -nP 'httptest\.NewServer\(http\.HandlerFunc\(func\(' router/core/graphql_sse_regression_test.go
rg -nP '\brequire\.[A-Za-z_]+\(\s*t\s*,' router/core/graphql_sse_regression_test.go
rg -nP '\bt\.Fatalf?\(' router/core/graphql_sse_regression_test.go

Repository: wundergraph/cosmo

Length of output: 1280


🏁 Script executed:

cat -n router/core/graphql_sse_regression_test.go | sed -n '85,220p'

Repository: wundergraph/cosmo

Length of output: 4772


Avoid require/t.Fatal inside httptest handler goroutines.

require.True(t, ok) at lines 93 and 191, and t.Fatal(...) at lines 105 and 203 run in handler goroutines spawned by httptest.NewServer. Calling these assertion/failure methods outside the main test goroutine will cause a runtime panic. Route handler errors to a channel and assert in the main test goroutine instead.

💡 Suggested fix pattern
+	handlerErr := make(chan error, 1)
 	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		flusher, ok := w.(http.Flusher)
-		require.True(t, ok)
+		if !ok {
+			select { case handlerErr <- fmt.Errorf("response writer does not implement http.Flusher"): default: }
+			return
+		}
 ...
-		_, _ = fmt.Fprintf(w, "event: next\ndata: %s\n\n", firstPayload)
+		if _, err := fmt.Fprintf(w, "event: next\ndata: %s\n\n", firstPayload); err != nil {
+			select { case handlerErr <- fmt.Errorf("write first payload: %w", err): default: }
+			return
+		}
 		flusher.Flush()
 ...
-		case <-time.After(time.Second):
-			t.Fatal("timed out waiting for first SSE update to be observed")
+		case <-time.After(time.Second):
+			select { case handlerErr <- fmt.Errorf("timed out waiting for first SSE update"): default: }
+			return
 		}
 ...
 	}))
 ...
+	select {
+	case err := <-handlerErr:
+		require.NoError(t, err)
+	default:
+	}

Also applies to: lines 189–215

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router/core/graphql_sse_regression_test.go` around lines 91 - 115, The
httptest handler is calling test assertions (require.True / t.Fatal) inside its
goroutine which can panic; instead capture any handler-side errors and signals
and send them over a channel to be asserted in the main test goroutine. Modify
the server handler created in httptest.NewServer (the anonymous
http.HandlerFunc) to replace require.True(t, ok) and t.Fatal(...) with sending
an error or boolean into a dedicated errCh (or doneCh) and close/send on
serverDone/updater-firstUpdate channels as needed, then in the main test
goroutine receive from errCh and perform the require/assert (e.g., check the
flusher ok, timeout errors) and fail the test there. Ensure you reference the
handler's flusher check and timeouts around updater.firstUpdate and serverDone
so all assertions happen in the main test goroutine.

@endigma
Copy link
Copy Markdown
Member

endigma commented Apr 15, 2026

Hi @dbinnersley, would you mind testing against #2486?

I've been working on a pretty massive overhaul of most of the subscriptions system in the router for a while now, I believe this is likely fixed there

@dbinnersley
Copy link
Copy Markdown
Author

Hi @dbinnersley, would you mind testing against #2486?

I've been working on a pretty massive overhaul of most of the subscriptions system in the router for a while now, I believe this is likely fixed there

I ran the same tests with the updated api, and it does appear to be fixed in this branch. Is there any expected timeline on this one?

@endigma
Copy link
Copy Markdown
Member

endigma commented Apr 15, 2026

@dbinnersley we're very close, it's at review stage but it's a large change and I can't provide a precise answer at this point

if you'd like to run it yourself it does publish images for the PR in CI, just be warned about possible instability although it's unlikely this close to release

@github-actions
Copy link
Copy Markdown

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions Bot added the Stale label Apr 30, 2026
@endigma
Copy link
Copy Markdown
Member

endigma commented Apr 30, 2026

Hi @dbinnersley, the new version has officially landed as a stable router release

@github-actions github-actions Bot removed the Stale label May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants