Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
489 commits
Select commit Hold shift + click to select a range
a79aeee
chore: CI stability run 48/50
jensneuse Mar 7, 2026
b6794af
chore: CI stability run 49/50
jensneuse Mar 7, 2026
addec93
chore: CI stability run 50/50
jensneuse Mar 7, 2026
b14ce85
chore: CI stability run 1/50
jensneuse Mar 7, 2026
ed4abe6
chore: CI stability run 2/50
jensneuse Mar 7, 2026
42f09d3
chore: CI stability run 3/50
jensneuse Mar 7, 2026
3e6a10c
chore: CI stability run 4/50
jensneuse Mar 7, 2026
33c3fd1
chore: CI stability run 5/50
jensneuse Mar 7, 2026
3026cf6
chore: CI stability run 6/50
jensneuse Mar 7, 2026
4231b1b
chore: CI stability run 7/50
jensneuse Mar 7, 2026
a90c50d
chore: CI stability run 8/50
jensneuse Mar 7, 2026
c08901f
chore: CI stability run 9/50
jensneuse Mar 7, 2026
80e1326
chore: CI stability run 10/50
jensneuse Mar 7, 2026
f5f176a
chore: CI stability run 1/50
jensneuse Mar 7, 2026
e6b65be
chore: CI stability run 11/50
jensneuse Mar 7, 2026
77568a1
chore: CI stability run 2/50
jensneuse Mar 7, 2026
39d46c8
chore: CI stability run 12/50
jensneuse Mar 7, 2026
aede796
chore: CI stability run 3/50
jensneuse Mar 7, 2026
7981684
chore: CI stability run 13/50
jensneuse Mar 7, 2026
ed8978c
chore: CI stability run 4/50
jensneuse Mar 7, 2026
f6920fa
chore: CI stability run 14/50
jensneuse Mar 7, 2026
844f093
chore: CI stability run 5/50
jensneuse Mar 7, 2026
1b858d2
chore: CI stability run 15/50
jensneuse Mar 7, 2026
6223f84
chore: CI stability run 6/50
jensneuse Mar 7, 2026
5b7e085
chore: CI stability run 16/50
jensneuse Mar 7, 2026
244e42c
chore: CI stability run 7/50
jensneuse Mar 7, 2026
5cf5482
chore: CI stability run 17/50
jensneuse Mar 7, 2026
5af22bb
chore: CI stability run 8/50
jensneuse Mar 7, 2026
33841a3
chore: CI stability run 18/50
jensneuse Mar 7, 2026
3ea6140
chore: CI stability run 9/50
jensneuse Mar 7, 2026
d5b542f
chore: CI stability run 19/50
jensneuse Mar 7, 2026
d73a102
chore: CI stability run 10/50
jensneuse Mar 7, 2026
96116ba
chore: CI stability run 20/50
jensneuse Mar 7, 2026
4c554b7
chore: CI stability run 11/50
jensneuse Mar 7, 2026
bf64f8d
chore: CI stability run 21/50
jensneuse Mar 7, 2026
1d08d30
chore: CI stability run 12/50
jensneuse Mar 7, 2026
694095c
chore: CI stability run 22/50
jensneuse Mar 7, 2026
868dc14
chore: CI stability run 13/50
jensneuse Mar 7, 2026
39bf2b4
chore: CI stability run 23/50
jensneuse Mar 7, 2026
e16223f
chore: CI stability run 14/50
jensneuse Mar 7, 2026
5a5da80
chore: CI stability run 24/50
jensneuse Mar 7, 2026
c5d910b
chore: CI stability run 15/50
jensneuse Mar 7, 2026
13fcced
chore: CI stability run 25/50
jensneuse Mar 7, 2026
729c3c5
chore: CI stability run 16/50
jensneuse Mar 7, 2026
0dd8dab
chore: CI stability run 26/50
jensneuse Mar 7, 2026
0712b4b
chore: CI stability run 17/50
jensneuse Mar 7, 2026
ea207fe
chore: CI stability run 27/50
jensneuse Mar 7, 2026
5622fd1
chore: CI stability run 18/50
jensneuse Mar 7, 2026
008f4d1
chore: CI stability run 28/50
jensneuse Mar 7, 2026
6fa813e
chore: CI stability run 19/50
jensneuse Mar 7, 2026
9b01f94
chore: CI stability run 29/50
jensneuse Mar 7, 2026
d81a0a3
chore: CI stability run 20/50
jensneuse Mar 7, 2026
57133c6
chore: CI stability run 30/50
jensneuse Mar 7, 2026
91f0fb4
chore: CI stability run 21/50
jensneuse Mar 7, 2026
4ed7533
chore: CI stability run 31/50
jensneuse Mar 7, 2026
c76e2f0
chore: CI stability run 22/50
jensneuse Mar 7, 2026
c013b25
chore: CI stability run 32/50
jensneuse Mar 7, 2026
5044bff
chore: CI stability run 23/50
jensneuse Mar 7, 2026
7083d1f
chore: CI stability run 33/50
jensneuse Mar 7, 2026
a52c258
chore: CI stability run 24/50
jensneuse Mar 7, 2026
d1b5a91
chore: CI stability run 34/50
jensneuse Mar 7, 2026
d0f1fa5
chore: CI stability run 25/50
jensneuse Mar 7, 2026
44e3136
chore: CI stability run 35/50
jensneuse Mar 7, 2026
2748789
chore: CI stability run 26/50
jensneuse Mar 7, 2026
6f26dda
chore: CI stability run 36/50
jensneuse Mar 7, 2026
27b65c0
chore: CI stability run 27/50
jensneuse Mar 7, 2026
701a565
chore: CI stability run 37/50
jensneuse Mar 7, 2026
8796b2d
chore: CI stability run 28/50
jensneuse Mar 7, 2026
e793702
chore: CI stability run 38/50
jensneuse Mar 7, 2026
9b9645d
chore: CI stability run 29/50
jensneuse Mar 7, 2026
b3109c4
chore: CI stability run 39/50
jensneuse Mar 7, 2026
efd0511
chore: CI stability run 30/50
jensneuse Mar 7, 2026
c8fc80b
chore: CI stability run 40/50
jensneuse Mar 7, 2026
0a32fd8
chore: CI stability run 31/50
jensneuse Mar 7, 2026
840bd5f
chore: CI stability run 41/50
jensneuse Mar 7, 2026
6d92a11
chore: CI stability run 32/50
jensneuse Mar 7, 2026
daddcaa
chore: CI stability run 42/50
jensneuse Mar 7, 2026
d7f8698
chore: CI stability run 33/50
jensneuse Mar 7, 2026
d522dab
chore: CI stability run 43/50
jensneuse Mar 7, 2026
d00b736
chore: CI stability run 34/50
jensneuse Mar 7, 2026
8e97839
chore: CI stability run 44/50
jensneuse Mar 7, 2026
212cc18
chore: CI stability run 35/50
jensneuse Mar 7, 2026
ab84d6c
chore: CI stability run 45/50
jensneuse Mar 7, 2026
b414246
chore: CI stability run 36/50
jensneuse Mar 7, 2026
f04d1b1
chore: CI stability run 46/50
jensneuse Mar 7, 2026
d28bef5
chore: CI stability run 37/50
jensneuse Mar 7, 2026
ab0c39f
chore: CI stability run 47/50
jensneuse Mar 7, 2026
b5d7776
chore: CI stability run 38/50
jensneuse Mar 7, 2026
b88679d
chore: CI stability run 48/50
jensneuse Mar 7, 2026
86b7239
chore: CI stability run 39/50
jensneuse Mar 7, 2026
d9418e6
chore: CI stability run 49/50
jensneuse Mar 7, 2026
cf8b766
chore: CI stability run 40/50
jensneuse Mar 7, 2026
5f5fcfa
chore: CI stability run 50/50
jensneuse Mar 7, 2026
cae15e5
chore: CI stability run 41/50
jensneuse Mar 7, 2026
e76dd9e
chore: CI stability run 1/50
jensneuse Mar 7, 2026
57b7d9a
chore: CI stability run 2/50
jensneuse Mar 7, 2026
36f2b17
chore: CI stability run 3/50
jensneuse Mar 7, 2026
f484110
chore: CI stability run 4/50
jensneuse Mar 7, 2026
820350d
chore: CI stability run 5/50
jensneuse Mar 7, 2026
f100a2b
chore: CI stability run 6/50
jensneuse Mar 7, 2026
87e4f14
chore: CI stability run 7/50
jensneuse Mar 7, 2026
f345aaf
chore: CI stability run 8/50
jensneuse Mar 7, 2026
13910c2
chore: CI stability run 9/50
jensneuse Mar 7, 2026
1e77eb9
chore: CI stability run 10/50
jensneuse Mar 7, 2026
ce4489d
chore: CI stability run 11/50
jensneuse Mar 8, 2026
bcb5159
chore: CI stability run 12/50
jensneuse Mar 8, 2026
982cc2e
chore: CI stability run 13/50
jensneuse Mar 8, 2026
95abbd2
chore: CI stability run 14/50
jensneuse Mar 8, 2026
684b801
chore: CI stability run 15/50
jensneuse Mar 8, 2026
80dfbc1
chore: CI stability run 16/50
jensneuse Mar 8, 2026
51873bc
chore: CI stability run 17/50
jensneuse Mar 8, 2026
e49de83
chore: CI stability run 18/50
jensneuse Mar 8, 2026
ab64a6e
chore: CI stability run 19/50
jensneuse Mar 8, 2026
92fff27
chore: CI stability run 20/50
jensneuse Mar 8, 2026
b07d931
chore: CI stability run 21/50
jensneuse Mar 8, 2026
7937624
chore: CI stability run 22/50
jensneuse Mar 8, 2026
56aded2
chore: CI stability run 23/50
jensneuse Mar 8, 2026
e8e4ccf
chore: CI stability run 24/50
jensneuse Mar 8, 2026
515160b
chore: CI stability run 25/50
jensneuse Mar 8, 2026
2cc9b21
chore: CI stability run 26/50
jensneuse Mar 8, 2026
08f9b40
chore: CI stability run 27/50
jensneuse Mar 8, 2026
124e8ff
chore: CI stability run 28/50
jensneuse Mar 8, 2026
a655848
chore: CI stability run 29/50
jensneuse Mar 8, 2026
e2ffbce
chore: CI stability run 30/50
jensneuse Mar 8, 2026
a51f2d0
chore: CI stability run 31/50
jensneuse Mar 8, 2026
d419d6b
chore: CI stability run 32/50
jensneuse Mar 8, 2026
13b0955
chore: CI stability run 33/50
jensneuse Mar 8, 2026
8e8fdb0
chore: CI stability run 34/50
jensneuse Mar 8, 2026
f5c6c30
chore: CI stability run 35/50
jensneuse Mar 8, 2026
1fc27f5
chore: CI stability run 36/50
jensneuse Mar 8, 2026
1181f6d
chore: CI stability run 37/50
jensneuse Mar 8, 2026
7a278c8
chore: CI stability run 38/50
jensneuse Mar 8, 2026
71afb71
chore: CI stability run 39/50
jensneuse Mar 8, 2026
f127f71
chore: CI stability run 40/50
jensneuse Mar 8, 2026
cdfdb20
chore: CI stability run 41/50
jensneuse Mar 8, 2026
408ff5a
chore: CI stability run 42/50
jensneuse Mar 8, 2026
9c3aa19
chore: CI stability run 43/50
jensneuse Mar 8, 2026
0ab6a9c
chore: CI stability run 44/50
jensneuse Mar 8, 2026
bcf1a4a
chore: CI stability run 45/50
jensneuse Mar 8, 2026
4b924fe
chore: CI stability run 46/50
jensneuse Mar 8, 2026
f320594
chore: CI stability run 47/50
jensneuse Mar 8, 2026
1a3e36c
chore: CI stability run 48/50
jensneuse Mar 8, 2026
de154b9
chore: CI stability run 49/50
jensneuse Mar 8, 2026
d04fdc3
chore: CI stability run 50/50
jensneuse Mar 8, 2026
4716298
chore: CI stability run 1/50
jensneuse Mar 8, 2026
09d744d
chore: CI stability run 2/50
jensneuse Mar 8, 2026
9a190fd
chore: CI stability run 3/50
jensneuse Mar 8, 2026
8d772d0
chore: CI stability run 4/50
jensneuse Mar 8, 2026
12bca88
chore: CI stability run 5/50
jensneuse Mar 8, 2026
c78acb1
chore: CI stability run 6/50
jensneuse Mar 8, 2026
8dd3cf2
chore: CI stability run 7/50
jensneuse Mar 8, 2026
f15af76
chore: CI stability run 8/50
jensneuse Mar 8, 2026
ea4da2b
chore: CI stability run 9/50
jensneuse Mar 8, 2026
53b070d
chore: CI stability run 10/50
jensneuse Mar 8, 2026
797a096
fix: poll for async hook call count in TestStartSubscriptionHook
jensneuse Mar 8, 2026
94e8381
chore: CI stability run 2/50
jensneuse Mar 8, 2026
8b6b231
chore: CI stability run 3/50
jensneuse Mar 8, 2026
a55aaf8
chore: CI stability run 4/50
jensneuse Mar 8, 2026
ea720b5
chore: CI stability run 5/50
jensneuse Mar 8, 2026
759a4e5
chore: CI stability run 6/50
jensneuse Mar 8, 2026
13ed13c
chore: CI stability run 7/50
jensneuse Mar 8, 2026
fc58f46
chore: CI stability run 8/50
jensneuse Mar 8, 2026
4b99b80
chore: CI stability run 9/50
jensneuse Mar 8, 2026
edbc619
chore: CI stability run 9/50 (retry - bun plugin build infra failure)
jensneuse Mar 8, 2026
f094a29
chore: CI stability run 10/50
jensneuse Mar 8, 2026
c3620b2
chore: CI stability run 11/50
jensneuse Mar 8, 2026
46dd41e
chore: CI stability run 12/50
jensneuse Mar 8, 2026
fe62d00
chore: CI stability run 13/50
jensneuse Mar 8, 2026
1026762
chore: CI stability run 14/50
jensneuse Mar 8, 2026
713168a
chore: CI stability run 15/50
jensneuse Mar 8, 2026
557eb1d
chore: CI stability run 16/50
jensneuse Mar 8, 2026
6b54eab
chore: CI stability run 17/50
jensneuse Mar 8, 2026
65568b7
chore: CI stability run 18/50
jensneuse Mar 8, 2026
93bc92f
chore: CI stability run 19/50
jensneuse Mar 8, 2026
a05f2b8
chore: CI stability run 20/50
jensneuse Mar 8, 2026
34259eb
chore: CI stability run 21/50
jensneuse Mar 8, 2026
2a902ad
chore: CI stability run 21/50 (retry - GH Actions infra failure)
jensneuse Mar 8, 2026
a5dcd51
chore: CI stability run 21/50 (retry2 - GH Actions queue stuck)
jensneuse Mar 8, 2026
bc0077d
chore: CI stability run 22/50
jensneuse Mar 8, 2026
6fadbe6
chore: CI stability run 23/50
jensneuse Mar 8, 2026
038bfec
chore: CI stability run 24/50
jensneuse Mar 8, 2026
c113864
chore: CI stability run 25/50
jensneuse Mar 8, 2026
62a1fa5
chore: CI stability run 26/50
jensneuse Mar 8, 2026
aac7171
chore: CI stability run 27/50
jensneuse Mar 8, 2026
84cd948
chore: CI stability run 28/50
jensneuse Mar 8, 2026
d9211e6
chore: CI stability run 29/50
jensneuse Mar 8, 2026
a4e35b0
chore: CI stability run 30/50
jensneuse Mar 8, 2026
8162a38
chore: CI stability run 31/50
jensneuse Mar 8, 2026
ce36754
chore: CI stability run 32/50
jensneuse Mar 8, 2026
bd15f5b
chore: CI stability run 33/50
jensneuse Mar 8, 2026
1fa2c0f
chore: CI stability run 34/50
jensneuse Mar 8, 2026
acc101b
chore: CI stability run 35/50
jensneuse Mar 8, 2026
8c8ad50
chore: CI stability run 36/50
jensneuse Mar 8, 2026
d7f100c
chore: CI stability run 37/50
jensneuse Mar 8, 2026
43e6541
chore: CI stability run 38/50
jensneuse Mar 8, 2026
e88a19b
chore: CI stability run 39/50
jensneuse Mar 8, 2026
b200fd9
chore: CI stability run 40/50
jensneuse Mar 8, 2026
e643b95
chore: CI stability run 41/50
jensneuse Mar 8, 2026
cb063fc
chore: CI stability run 42/50
jensneuse Mar 8, 2026
bb9d843
chore: CI stability run 43/50
jensneuse Mar 8, 2026
892343f
chore: CI stability run 44/50
jensneuse Mar 8, 2026
b21c146
chore: CI stability run 45/50
jensneuse Mar 8, 2026
823dda3
chore: CI stability run 46/50
jensneuse Mar 8, 2026
ccaddd7
chore: CI stability run 47/50
jensneuse Mar 8, 2026
ec31861
chore: CI stability run 48/50
jensneuse Mar 8, 2026
ad26491
chore: CI stability run 49/50
jensneuse Mar 8, 2026
9c58a10
chore: CI stability run 50/50
jensneuse Mar 8, 2026
f4a75a7
chore: CI stability run 1/50
jensneuse Mar 8, 2026
1a7782e
chore: CI stability run 2/50
jensneuse Mar 8, 2026
a69b63f
chore: CI stability run 3/50
jensneuse Mar 8, 2026
f39a5b3
chore: CI stability run 4/50
jensneuse Mar 8, 2026
1cb9686
chore: CI stability run 5/50
jensneuse Mar 8, 2026
c164c27
chore: CI stability run 6/50
jensneuse Mar 8, 2026
556d830
chore: CI stability run 7/50
jensneuse Mar 8, 2026
5cad44c
chore: CI stability run 8/50
jensneuse Mar 8, 2026
26c03d4
chore: CI stability run 9/50
jensneuse Mar 8, 2026
b4c28e7
chore: CI stability run 1/10
jensneuse Mar 9, 2026
f38551e
chore: CI stability run 2/10
jensneuse Mar 9, 2026
de00c8f
chore: CI stability run 3/10
jensneuse Mar 9, 2026
c40c4d4
chore: CI stability run 4/10
jensneuse Mar 9, 2026
1e4fc73
chore: CI stability run 5/10
jensneuse Mar 9, 2026
2b9270d
chore: CI stability run 6/10
jensneuse Mar 9, 2026
ff25ef1
chore: CI stability run 7/10
jensneuse Mar 9, 2026
8adbfce
chore: CI stability run 8/10
jensneuse Mar 9, 2026
4e677a2
chore: CI stability run 9/10
jensneuse Mar 9, 2026
7e2d1ca
chore: CI stability run 10/10
jensneuse Mar 9, 2026
25416d3
Merge remote-tracking branch 'origin/main' into jensneuse/fix-flaky-t…
jensneuse Mar 9, 2026
55158c4
refactor: colocate testdata with test subdirectories, add AGENTS.md
jensneuse Mar 9, 2026
3d34cf7
refactor: remove os.Chdir hack, use testenv.ResolvePath for all paths
jensneuse Mar 9, 2026
7f03608
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 9, 2026
d54f9a6
refactor: simplify paths, remove ResolvePath abstraction
jensneuse Mar 9, 2026
8dbf9d9
refactor: colocate cache_warmup testdata with operations tests, remov…
jensneuse Mar 9, 2026
3a7de10
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 9, 2026
b089c9a
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 9, 2026
9bb499f
fix: address PR review feedback from Noroth and StarpTech
jensneuse Mar 10, 2026
acd896b
fix: restore cacheHashNotStored constant and comment in persisted ops…
jensneuse Mar 10, 2026
a93db46
refactor: move shared test helpers to testutils package and fix impor…
jensneuse Mar 10, 2026
1a5c5b8
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 10, 2026
4b68521
chore: add .serena/ to .gitignore
jensneuse Mar 10, 2026
9c92bd4
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 10, 2026
6d52b19
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 11, 2026
21be65e
fix: replace undefined NatsWaitTimeout with EventWaitTimeout
jensneuse Mar 11, 2026
ac05837
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 11, 2026
acb2a02
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 11, 2026
8d43d87
test(router-tests): address review follow-ups
jensneuse Mar 11, 2026
dbfbaf9
test(router-tests): reduce protocol test diff noise
jensneuse Mar 11, 2026
5cbde3c
Merge remote-tracking branch 'origin/main' into jensneuse/fix-flaky-t…
jensneuse Mar 12, 2026
e33e467
Merge remote-tracking branch 'origin/main' into jensneuse/fix-flaky-t…
jensneuse Mar 12, 2026
83e5e17
refactor(router-tests): move wait sync into SyncReporter
jensneuse Mar 12, 2026
655cbf5
Merge branch 'main' into jensneuse/fix-flaky-tests
jensneuse Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 30 additions & 24 deletions .github/workflows/router-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,30 @@ jobs:
integration_test:
runs-on: ubuntu-latest-l
timeout-minutes: 30
env:
GOMAXPROCS: 4
strategy:
fail-fast: false
matrix:
test_target:
[
'./. ./fuzzquery ./lifecycle ./modules',
'./telemetry',
'./events',
'./connectrpc',
]
include:
- test_target: './observability'
label: observability
- test_target: './security'
label: security
- test_target: './operations'
label: operations
- test_target: './subscriptions'
label: subscriptions
- test_target: './protocol'
label: protocol
- test_target: './fuzzquery ./lifecycle ./modules'
label: fuzzquery_lifecycle_modules
- test_target: './telemetry'
label: telemetry
- test_target: './events'
label: events
- test_target: './connectrpc'
label: connectrpc
services:
nats:
image: ghcr.io/wundergraph/cosmo/nats:2.11.0-alpine
Expand Down Expand Up @@ -270,10 +284,10 @@ jobs:
kafka:
image: bitnamilegacy/kafka:3.7.0
options: >-
--health-cmd "kafka-broker-api-versions.sh --version"
--health-cmd "kafka-broker-api-versions.sh --bootstrap-server localhost:9092 | grep ApiVersion"
--health-interval 10s
--health-timeout 5s
--health-retries 5
--health-timeout 10s
--health-retries 10
env:
KAFKA_ENABLE_KRAFT: yes
KAFKA_CFG_PROCESS_ROLES: controller,broker
Expand Down Expand Up @@ -368,34 +382,26 @@ jobs:
docker exec "$cid" redis-cli -u "redis://cosmo:test@redis-0:6379" ping
docker exec "$cid" redis-cli -u "redis://cosmo:test@redis-0:6379" cluster nodes

- name: Run Integration tests ${{ matrix.test_target }}
- name: Run Integration tests ${{ matrix.label }}
working-directory: ./router-tests
run: make test-coverage test_retry_count=0 test_params="-run '^Test[^(Flaky)]' --timeout=5m -p 1 --parallel 10" test_target="${{ matrix.test_target }}"

- name: Compute artifact name for codecov upload
id: artifact_name
run: |
# Sanitize test target: remove './', replace spaces and special chars with underscores
target="${{ matrix.test_target }}"
sanitized=$(echo "$target" | sed 's|\.\/||g' | sed 's|[^a-zA-Z0-9]|_|g' | sed 's|_\+|_|g' | sed 's|^_||' | sed 's|_$||')
echo "sanitized=$sanitized" >> $GITHUB_OUTPUT
run: make test-coverage test_retry_count=0 test_params="-run '^Test' -skip '^TestFlaky' --timeout=8m -p 1 --parallel 2" test_target="${{ matrix.test_target }}"

- name: Upload integration results to Codecov
uses: ./.github/actions/codecov-upload-pr
with:
artifact-name: router-${{ steps.artifact_name.outputs.sanitized }}-nonFlaky
artifact-name: router-${{ matrix.label }}-nonFlaky
coverage-path: router-tests/coverage.out
retention-days: 14
codecov-token: ${{ secrets.CODECOV_TOKEN }}

- name: Run Flaky Integration tests ${{ matrix.test_target }}
- name: Run Flaky Integration tests ${{ matrix.label }}
working-directory: ./router-tests
run: make test-coverage test_retry_count=3 test_params="-run '^TestFlaky' --timeout=5m -p 1 --parallel 10" test_target="${{ matrix.test_target }}"
run: make test-coverage test_retry_count=3 test_params="-run '^TestFlaky' --timeout=8m -p 1 --parallel 2" test_target="${{ matrix.test_target }}"

- name: Upload flaky integration results to Codecov
uses: ./.github/actions/codecov-upload-pr
with:
artifact-name: router-${{ steps.artifact_name.outputs.sanitized }}-flaky
artifact-name: router-${{ matrix.label }}-flaky
coverage-path: router-tests/coverage.out
retention-days: 14
codecov-token: ${{ secrets.CODECOV_TOKEN }}
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ go.work.sum

# Mise local config
**/mise.local.toml

# Serena
.serena/
1 change: 1 addition & 0 deletions router-tests/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See [CLAUDE.md](./CLAUDE.md) for test development guidelines.
207 changes: 207 additions & 0 deletions router-tests/CLAUDE.md
Comment thread
jensneuse marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
# Router Integration Tests

## Directory Structure

Tests are organized into subdirectories by functional area. New test files added to any subdirectory are automatically included in CI — no matrix or config changes needed.

| Directory | Purpose |
|-----------|---------|
| `observability/` | Prometheus metrics, logging, metrics exporters |
| `security/` | Auth, TLS, mTLS, error handling, circuit breakers, rate limiting |
| `operations/` | Caching, persisted ops, normalization, query plans, introspection |
| `subscriptions/` | WebSocket, HTTP subscriptions |
| `protocol/` | GraphQL/HTTP protocol, headers, uploads, config, plugins |
| `events/` | NATS, Kafka, Redis event-driven subscriptions |
| `telemetry/` | OpenTelemetry tracing and metrics |
| `connectrpc/` | ConnectRPC/gRPC subgraph integration |
| `lifecycle/` | Router startup, shutdown, goroutine leaks |
| `modules/` | Custom router modules |
| `fuzzquery/` | Fuzz testing for query parsing |

Shared test helpers live in `testutils/` and `testenv/` (test environment setup). Subdirectories import shared helpers via `"github.com/wundergraph/cosmo/router-tests/testutils"`.

### Testdata Layout

Each subdirectory keeps its own `testdata/` for test fixtures specific to that area. Shared testdata (e.g., TLS certificates used by both `security/` and `events/`) lives in `testdata/` at the router-tests root.

Go tests run with CWD set to the package directory, so use simple relative paths: `testdata/...` for local fixtures, `../testdata/...` for shared fixtures, `../../router/...` for the router package.

| Location | Contents | Used By |
|----------|----------|---------|
| `operations/testdata/` | Query plan fixtures, introspection schemas, cache warmup fixtures | operations |
| `protocol/testdata/` | Router configs, query fixtures, MCP operations, tracing | protocol |
| `fuzzquery/testdata/` | Fuzz corpus | fuzzquery |
| `testdata/tls/` | TLS certificates (shared) | security, events |
| `testdata/connectrpc/` | Generated protobuf client stubs (Go package) | connectrpc |
| `testenv/testdata/` | Embedded router configs, CDN | all (via testenv) |

## Test Synchronization Architecture

Tests synchronize with the engine via `SyncReporter` (`testenv/sync_reporter.go`), a channel-based wrapper around `EngineStats` that is injected into the router at test setup. It emits typed `Event` values on a buffered channel whenever a Reporter method is called (subscription added, trigger fired, message sent, etc.).

**Two synchronization mechanisms:**
- **Predicate-based** (`Wait()` on inner `EngineStats`): Used by `WaitForSubscriptionCount`, `WaitForTriggerCount`, etc. Best for "wait until absolute count matches" conditions.
- **Channel-based** (`Events()` channel): Used by `NATSPublishUntilReceived` and `KafkaPublishUntilReceived`. Best for "wait until this specific event happens" patterns.

Tests access the `SyncReporter` through `e.syncReporter()` or via the existing `WaitFor*` helpers which delegate internally.

## Writing Reliable Tests

### 1. NATS/Kafka/Redis subscription tests: use warm-up before publishing

**Problem:** After `WaitForSubscriptionCount` and `WaitForTriggerCount`, the internal subscription pipeline may not be fully wired up. A direct `Publish()` can be silently lost, causing timeouts.

**Fix:** Use the `*PublishUntilReceived` methods for the first message. These helpers publish, wait for a `SubscriptionUpdateSent` event on the SyncReporter channel, and retry if the message was lost. All three event systems have a safe variant:
- `NATSPublishUntilReceived` / `NATSPublishUntilMinMessagesSent` (for fan-out)
- `KafkaPublishUntilReceived`
- `RedisPublishUntilReceived`

```go
// WRONG — message can be lost to race condition
xEnv.WaitForSubscriptionCount(1, timeout)
xEnv.WaitForTriggerCount(1, timeout)
err = conn.Publish(subject, data)
conn.Flush()
xEnv.WaitForMessagesSent(1, timeout) // may timeout forever

// RIGHT — retries until delivery is confirmed via SyncReporter event
xEnv.WaitForSubscriptionCount(1, timeout)
xEnv.WaitForTriggerCount(1, timeout)
xEnv.NATSPublishUntilReceived(conn, subject, data, 1, timeout)
// or: xEnv.KafkaPublishUntilReceived(topic, message, 1, timeout)
// or: xEnv.RedisPublishUntilReceived(topic, message, timeout)
```

### 2. Error-testing with intentionally bad messages: warm up first, then single-send

**Problem:** `PublishUntilReceived` retries on failure. If the message is intentionally invalid, retries produce duplicates that pollute the subscription channel.

**Fix:** First confirm the pipeline is active with a valid warm-up message via `PublishUntilReceived`. Then use a single non-retrying publish for the bad message.

```go
// Warm-up: confirm pipeline is active
xEnv.NATSPublishUntilReceived(conn, subject, validMsg, 1, timeout)
// read and validate the warm-up response...

// Now send the intentionally bad message (single publish, no retry)
conn.Publish(subject, invalidMsg)
conn.Flush()
// read and validate the error response...
```

### 3. WebSocket reads: use `WSReadJSON` instead of `conn.ReadJSON`

**Problem:** `conn.ReadJSON()` blocks forever if the expected message never arrives (e.g., due to a lost publish). The test hangs until the 8-minute Go test timeout kills it.

**Fix:** Use `testenv.WSReadJSON` (or `testenv.WSWriteJSON` for writes) which has built-in retry with 2-second deadlines per attempt and exponential backoff (up to 10 retries).

```go
// WRONG — hangs indefinitely if no message arrives
err = conn.ReadJSON(&msg)

// RIGHT — retries with deadline, fails fast after ~20 seconds
err = testenv.WSReadJSON(t, conn, &msg)
```

Only use manual `SetReadDeadline` + `conn.ReadJSON` when you expect an error (e.g., websocket close after config hot reload):

```go
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
err = conn.ReadJSON(&msg) // may return websocket.CloseError
conn.SetReadDeadline(time.Time{})
```

### 4. Non-deterministic order: sort before asserting

**Problem:** Metrics, spans, or other collections may appear in non-deterministic order. Asserting on index positions (`metrics[0]`) fails intermittently.

**Fix:** Sort the collection by a stable key before making positional assertions.

```go
sort.Slice(metrics, func(i, j int) bool {
return metrics[i].Labels["subgraph"] < metrics[j].Labels["subgraph"]
})
require.Equal(t, "employees", metrics[0].Labels["subgraph"])
```

### 5. Flaky test prefix convention

Tests known to be flaky use the `TestFlaky` prefix (e.g., `TestFlakyNatsEvents`). CI runs these with `test_retry_count=3` and a separate `-run '^TestFlaky'` pass. Once the root cause is fixed, move tests back to their non-flaky parent function.

### 6. Periodic exporters: wait for ALL expected items, not just one

**Problem:** When testing a periodic exporter (e.g., metrics log exporter with a 90ms interval), waiting for a single item to appear then asserting all items are present races — the exporter may not have exported everything in one cycle.

**Fix:** Use `require.Eventually` to wait for ALL expected items to appear, not just one sentinel value.

```go
// WRONG — only waits for one metric, then asserts all are present
require.Eventually(t, func() bool {
return findMetricLog(logs, "router.http.requests") != nil
}, 5*time.Second, 100*time.Millisecond)
for _, m := range scopeMetric.Metrics {
require.NotNil(t, findMetricLog(logs, m.Name)) // may fail for other metrics
}

// RIGHT — waits for every expected item
require.Eventually(t, func() bool {
logs := observer.FilterMessage("Metric").All()
for _, m := range scopeMetric.Metrics {
if findMetricLog(logs, m.Name) == nil {
return false
}
}
return true
}, 5*time.Second, 100*time.Millisecond)
```

### 7. Async hook invocation: poll before asserting counts

**Problem:** Module hooks (e.g., `OnStartSubscription`) may fire asynchronously after `WaitForSubscriptionCount` returns. Asserting `HookCallCount` immediately can see `0` because the hook hasn't executed yet.

**Fix:** Use `require.Eventually` to poll for the expected hook count before asserting.

```go
// WRONG — hook may not have fired yet
xEnv.WaitForSubscriptionCount(1, timeout)
assert.Equal(t, int32(1), customModule.HookCallCount.Load()) // may be 0

// RIGHT — poll until hook fires
xEnv.WaitForSubscriptionCount(1, timeout)
require.Eventually(t, func() bool {
return customModule.HookCallCount.Load() >= 1
}, time.Second*10, time.Millisecond*50)
```

### 8. Buffered channels in worker pools: prevent goroutine leaks on context cancellation

**Problem:** When a worker pool uses an unbuffered completion channel and the main goroutine exits early (e.g., via context cancellation), workers block forever on sends, leaking goroutines and potentially crashing the process.

**Fix:** Always buffer completion channels with capacity equal to the number of workers so sends never block when the receiver has stopped listening.

```go
// WRONG — workers block on send if main goroutine exits via <-done
itemCompleted := make(chan struct{})

// RIGHT — buffered, workers can always send and exit cleanly
itemCompleted := make(chan struct{}, workerCount)
```

## Key Test Helpers

| Helper | Location | Purpose |
|--------|----------|---------|
| `SyncReporter` | `testenv/sync_reporter.go` | Channel-based wrapper around EngineStats for test synchronization |
| `NATSPublishUntilReceived` | `testenv/testenv.go` | Publish NATS message with retry until `SubscriptionUpdateSent` event received |
| `KafkaPublishUntilReceived` | `testenv/testenv.go` | Same pattern for Kafka |
| `RedisPublishUntilReceived` | `testenv/testenv.go` | Same pattern for Redis |
| `WSReadJSON` / `WSWriteJSON` | `testenv/testenv.go` | WebSocket read/write with retry + read deadline (10 attempts, exponential backoff) |
| `WSReadMessage` / `WSWriteMessage` | `testenv/testenv.go` | WebSocket raw message read/write with retry + deadline |
| `WaitForSubscriptionCount` | `testenv/testenv.go` | Wait for subscription count to reach exact value (predicate-based) |
| `WaitForTriggerCount` | `testenv/testenv.go` | Wait for trigger count to reach at least N (predicate-based) |
| `WaitForMessagesSent` | `testenv/testenv.go` | Wait for MessagesSent to reach at least N (predicate-based) |
| `ConfigureAuth` | `testutils/utils.go` | Set up JWKS auth for tests |
| `ToPtr` | `testutils/utils.go` | Generic pointer helper |
| `EmployeesIDData` | `testutils/utils.go` | Standard expected response constant |

<!-- CI stability run: 10 of 10 -->
40 changes: 40 additions & 0 deletions router-tests/events/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package events_test

import (
"bufio"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/wundergraph/cosmo/router-tests/testenv"
)

const EventWaitTimeout = time.Second * 30

func assertLineEquals(t *testing.T, reader *bufio.Reader, expected string) {
t.Helper()
line := testenv.ReadSSELine(t, reader)
assert.Equal(t, expected, line)
}

func assertMultipartPrefix(t *testing.T, reader *bufio.Reader) {
t.Helper()
assertLineEquals(t, reader, "")
assertLineEquals(t, reader, "--graphql")
assertLineEquals(t, reader, "Content-Type: application/json")
assertLineEquals(t, reader, "")
}

func assertMultipartValueEventually(t *testing.T, reader *bufio.Reader, expected string) {
t.Helper()
assert.Eventually(t, func() bool {
assertMultipartPrefix(t, reader)
line, _, err := reader.ReadLine()
assert.NoError(t, err)
if string(line) == "{}" {
return false
}
assert.Equal(t, expected, string(line))
return true
}, EventWaitTimeout, time.Millisecond*100)
}
Loading
Loading