@durable-streams/client-conformance-tests 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +451 -0
  2. package/dist/adapters/typescript-adapter.d.ts +1 -0
  3. package/dist/adapters/typescript-adapter.js +586 -0
  4. package/dist/benchmark-runner-C_Yghc8f.js +1333 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +265 -0
  7. package/dist/index.d.ts +508 -0
  8. package/dist/index.js +4 -0
  9. package/dist/protocol-DyEvTHPF.d.ts +472 -0
  10. package/dist/protocol-qb83AeUH.js +120 -0
  11. package/dist/protocol.d.ts +2 -0
  12. package/dist/protocol.js +3 -0
  13. package/package.json +53 -0
  14. package/src/adapters/typescript-adapter.ts +848 -0
  15. package/src/benchmark-runner.ts +860 -0
  16. package/src/benchmark-scenarios.ts +311 -0
  17. package/src/cli.ts +294 -0
  18. package/src/index.ts +50 -0
  19. package/src/protocol.ts +656 -0
  20. package/src/runner.ts +1191 -0
  21. package/src/test-cases.ts +475 -0
  22. package/test-cases/consumer/cache-headers.yaml +150 -0
  23. package/test-cases/consumer/error-handling.yaml +108 -0
  24. package/test-cases/consumer/message-ordering.yaml +209 -0
  25. package/test-cases/consumer/offset-handling.yaml +209 -0
  26. package/test-cases/consumer/offset-resumption.yaml +197 -0
  27. package/test-cases/consumer/read-catchup.yaml +173 -0
  28. package/test-cases/consumer/read-longpoll.yaml +132 -0
  29. package/test-cases/consumer/read-sse.yaml +145 -0
  30. package/test-cases/consumer/retry-resilience.yaml +160 -0
  31. package/test-cases/consumer/streaming-equivalence.yaml +226 -0
  32. package/test-cases/lifecycle/dynamic-headers.yaml +147 -0
  33. package/test-cases/lifecycle/headers-params.yaml +117 -0
  34. package/test-cases/lifecycle/stream-lifecycle.yaml +148 -0
  35. package/test-cases/producer/append-data.yaml +142 -0
  36. package/test-cases/producer/batching.yaml +112 -0
  37. package/test-cases/producer/create-stream.yaml +87 -0
  38. package/test-cases/producer/error-handling.yaml +90 -0
  39. package/test-cases/producer/sequence-ordering.yaml +148 -0
@@ -0,0 +1,226 @@
1
+ id: consumer-streaming-equivalence
2
+ name: Streaming Mode Equivalence
3
+ description: Tests that SSE and Long-poll modes produce identical results
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - equivalence
8
+ - sse
9
+ - longpoll
10
+
11
+ tests:
12
+ - id: equivalence-existing-data
13
+ name: Both modes return same existing data
14
+ description: SSE and long-poll should return identical data for existing content
15
+ setup:
16
+ - action: create
17
+ as: streamPath
18
+ - action: append
19
+ path: ${streamPath}
20
+ data: "chunk1"
21
+ - action: append
22
+ path: ${streamPath}
23
+ data: "chunk2"
24
+ - action: append
25
+ path: ${streamPath}
26
+ data: "chunk3"
27
+ operations:
28
+ # Read with long-poll
29
+ - action: read
30
+ path: ${streamPath}
31
+ live: long-poll
32
+ waitForUpToDate: true
33
+ expect:
34
+ data: "chunk1chunk2chunk3"
35
+ upToDate: true
36
+ storeDataAs: longpollData
37
+ # Read same stream with SSE
38
+ - action: read
39
+ path: ${streamPath}
40
+ live: sse
41
+ waitForUpToDate: true
42
+ expect:
43
+ data: "chunk1chunk2chunk3"
44
+ upToDate: true
45
+ # Verify both got same data
46
+ - action: assert
47
+ equals:
48
+ left: ${longpollData}
49
+ right: "chunk1chunk2chunk3"
50
+ message: "Long-poll and SSE should return identical data"
51
+
52
+ - id: equivalence-from-offset
53
+ name: Both modes resume from same offset correctly
54
+ description: SSE and long-poll should resume from offset identically
55
+ setup:
56
+ - action: create
57
+ as: streamPath
58
+ - action: append
59
+ path: ${streamPath}
60
+ data: "before"
61
+ expect:
62
+ storeOffsetAs: midOffset
63
+ - action: append
64
+ path: ${streamPath}
65
+ data: "after"
66
+ operations:
67
+ # Read from offset with long-poll
68
+ - action: read
69
+ path: ${streamPath}
70
+ offset: ${midOffset}
71
+ live: long-poll
72
+ waitForUpToDate: true
73
+ expect:
74
+ data: "after"
75
+ upToDate: true
76
+ # Read from same offset with SSE
77
+ - action: read
78
+ path: ${streamPath}
79
+ offset: ${midOffset}
80
+ live: sse
81
+ waitForUpToDate: true
82
+ expect:
83
+ data: "after"
84
+ upToDate: true
85
+
86
+ - id: equivalence-empty-stream
87
+ name: Both modes handle empty stream identically
88
+ description: SSE and long-poll should both report up-to-date on empty stream
89
+ setup:
90
+ - action: create
91
+ as: streamPath
92
+ operations:
93
+ # Read empty stream with long-poll
94
+ - action: read
95
+ path: ${streamPath}
96
+ live: long-poll
97
+ waitForUpToDate: true
98
+ expect:
99
+ chunkCount: 0
100
+ upToDate: true
101
+ # Read empty stream with SSE
102
+ - action: read
103
+ path: ${streamPath}
104
+ live: sse
105
+ waitForUpToDate: true
106
+ expect:
107
+ chunkCount: 0
108
+ upToDate: true
109
+
110
+ - id: equivalence-live-new-data
111
+ name: Both modes receive live data
112
+ description: SSE and long-poll should both receive newly appended data
113
+ requires:
114
+ - sse
115
+ - long-poll
116
+ setup:
117
+ - action: create
118
+ as: streamPath
119
+ - action: append
120
+ path: ${streamPath}
121
+ data: "initial"
122
+ expect:
123
+ storeOffsetAs: initialOffset
124
+ operations:
125
+ # Test long-poll receives new data
126
+ - action: read
127
+ path: ${streamPath}
128
+ offset: ${initialOffset}
129
+ live: long-poll
130
+ timeoutMs: 5000
131
+ background: true
132
+ as: longpollRead
133
+ - action: wait
134
+ ms: 100
135
+ - action: server-append
136
+ path: ${streamPath}
137
+ data: "live-data-lp"
138
+ - action: await
139
+ ref: longpollRead
140
+ expect:
141
+ dataContains: "live-data-lp"
142
+
143
+ - id: equivalence-sse-live-new-data
144
+ name: SSE receives live data
145
+ description: SSE should receive newly appended data in live mode
146
+ requires:
147
+ - sse
148
+ setup:
149
+ - action: create
150
+ as: streamPath
151
+ - action: append
152
+ path: ${streamPath}
153
+ data: "initial"
154
+ expect:
155
+ storeOffsetAs: initialOffset
156
+ operations:
157
+ # Test SSE receives new data
158
+ - action: read
159
+ path: ${streamPath}
160
+ offset: ${initialOffset}
161
+ live: sse
162
+ timeoutMs: 5000
163
+ background: true
164
+ as: sseRead
165
+ - action: wait
166
+ ms: 100
167
+ - action: server-append
168
+ path: ${streamPath}
169
+ data: "live-data-sse"
170
+ - action: await
171
+ ref: sseRead
172
+ expect:
173
+ dataContains: "live-data-sse"
174
+
175
+ - id: equivalence-offset-semantics
176
+ name: Offset semantics identical between modes
177
+ description: Reading from specific offsets should work identically
178
+ setup:
179
+ - action: create
180
+ as: streamPath
181
+ - action: append
182
+ path: ${streamPath}
183
+ data: "a"
184
+ expect:
185
+ storeOffsetAs: offset1
186
+ - action: append
187
+ path: ${streamPath}
188
+ data: "b"
189
+ expect:
190
+ storeOffsetAs: offset2
191
+ - action: append
192
+ path: ${streamPath}
193
+ data: "c"
194
+ operations:
195
+ # Long-poll from offset1 should get "bc"
196
+ - action: read
197
+ path: ${streamPath}
198
+ offset: ${offset1}
199
+ live: long-poll
200
+ waitForUpToDate: true
201
+ expect:
202
+ data: "bc"
203
+ # SSE from offset1 should also get "bc"
204
+ - action: read
205
+ path: ${streamPath}
206
+ offset: ${offset1}
207
+ live: sse
208
+ waitForUpToDate: true
209
+ expect:
210
+ data: "bc"
211
+ # Long-poll from offset2 should get "c"
212
+ - action: read
213
+ path: ${streamPath}
214
+ offset: ${offset2}
215
+ live: long-poll
216
+ waitForUpToDate: true
217
+ expect:
218
+ data: "c"
219
+ # SSE from offset2 should also get "c"
220
+ - action: read
221
+ path: ${streamPath}
222
+ offset: ${offset2}
223
+ live: sse
224
+ waitForUpToDate: true
225
+ expect:
226
+ data: "c"
@@ -0,0 +1,147 @@
1
+ id: lifecycle-dynamic-headers
2
+ name: Dynamic Headers and Parameters
3
+ description: |
4
+ Tests for dynamic header and parameter handling.
5
+ Production clients need to support headers/params that are computed per-request
6
+ (e.g., OAuth token refresh, request timestamps, correlation IDs).
7
+ category: lifecycle
8
+ tags:
9
+ - headers
10
+ - params
11
+ - auth
12
+ - dynamic
13
+ requires:
14
+ - dynamicHeaders
15
+
16
+ tests:
17
+ - id: dynamic-header-per-request
18
+ name: Dynamic headers evaluated per request
19
+ description: |
20
+ Headers provided as functions should be evaluated on each request,
21
+ not cached. This is critical for token refresh scenarios.
22
+ setup:
23
+ - action: create
24
+ as: streamPath
25
+ operations:
26
+ # Configure adapter to use a counter-based dynamic header
27
+ - action: set-dynamic-header
28
+ name: X-Request-Counter
29
+ valueType: counter
30
+ # First append - should have counter=1
31
+ - action: append
32
+ path: ${streamPath}
33
+ data: "first"
34
+ expect:
35
+ status: 200
36
+ headersSent:
37
+ X-Request-Counter: "1"
38
+ # Second append - should have counter=2 (not cached)
39
+ - action: append
40
+ path: ${streamPath}
41
+ data: "second"
42
+ expect:
43
+ status: 200
44
+ headersSent:
45
+ X-Request-Counter: "2"
46
+ # Third append - should have counter=3
47
+ - action: append
48
+ path: ${streamPath}
49
+ data: "third"
50
+ expect:
51
+ status: 200
52
+ headersSent:
53
+ X-Request-Counter: "3"
54
+
55
+ - id: dynamic-header-on-read
56
+ name: Dynamic headers on repeated reads
57
+ description: Headers should be freshly evaluated on each read request
58
+ setup:
59
+ - action: create
60
+ as: streamPath
61
+ - action: append
62
+ path: ${streamPath}
63
+ data: "test-data"
64
+ operations:
65
+ - action: set-dynamic-header
66
+ name: X-Request-Counter
67
+ valueType: counter
68
+ # First read
69
+ - action: read
70
+ path: ${streamPath}
71
+ live: false
72
+ expect:
73
+ status: 200
74
+ headersSent:
75
+ X-Request-Counter: "1"
76
+ # Second read from same offset - counter should increment
77
+ - action: read
78
+ path: ${streamPath}
79
+ live: false
80
+ expect:
81
+ status: 200
82
+ headersSent:
83
+ X-Request-Counter: "2"
84
+
85
+ - id: dynamic-param-per-request
86
+ name: Dynamic params evaluated per request
87
+ description: |
88
+ URL parameters provided as functions should be evaluated on each request.
89
+ Useful for signed URLs, dynamic routing, etc.
90
+ setup:
91
+ - action: create
92
+ as: streamPath
93
+ - action: append
94
+ path: ${streamPath}
95
+ data: "data"
96
+ operations:
97
+ - action: set-dynamic-param
98
+ name: request_id
99
+ valueType: counter
100
+ - action: read
101
+ path: ${streamPath}
102
+ live: false
103
+ expect:
104
+ status: 200
105
+ paramsSent:
106
+ request_id: "1"
107
+ - action: read
108
+ path: ${streamPath}
109
+ live: false
110
+ expect:
111
+ status: 200
112
+ paramsSent:
113
+ request_id: "2"
114
+
115
+ - id: token-refresh-simulation
116
+ name: Token refresh simulation
117
+ description: |
118
+ Simulates OAuth token refresh by changing the Authorization header value
119
+ between requests. The client must use the new value, not cache the old one.
120
+ setup:
121
+ - action: create
122
+ as: streamPath
123
+ operations:
124
+ # Set initial token
125
+ - action: set-dynamic-header
126
+ name: Authorization
127
+ valueType: token
128
+ initialValue: "Bearer token-v1"
129
+ - action: append
130
+ path: ${streamPath}
131
+ data: "first"
132
+ expect:
133
+ status: 200
134
+ headersSent:
135
+ Authorization: "Bearer token-v1"
136
+ # Simulate token refresh
137
+ - action: set-dynamic-header
138
+ name: Authorization
139
+ valueType: token
140
+ initialValue: "Bearer token-v2"
141
+ - action: append
142
+ path: ${streamPath}
143
+ data: "second"
144
+ expect:
145
+ status: 200
146
+ headersSent:
147
+ Authorization: "Bearer token-v2"
@@ -0,0 +1,117 @@
1
+ id: lifecycle-headers
2
+ name: Headers and Parameters
3
+ description: Tests for header and parameter handling
4
+ category: lifecycle
5
+ tags:
6
+ - headers
7
+ - params
8
+ - auth
9
+
10
+ tests:
11
+ - id: custom-headers-create
12
+ name: Custom headers on create
13
+ description: Client should send custom headers on create
14
+ operations:
15
+ - action: create
16
+ as: streamPath
17
+ headers:
18
+ X-Custom-Header: "custom-value"
19
+ X-Another-Header: "another-value"
20
+ expect:
21
+ status: 201
22
+
23
+ - id: custom-headers-append
24
+ name: Custom headers on append
25
+ description: Client should send custom headers on append
26
+ setup:
27
+ - action: create
28
+ as: streamPath
29
+ operations:
30
+ - action: append
31
+ path: ${streamPath}
32
+ data: "with-headers"
33
+ headers:
34
+ X-Request-Id: "req-12345"
35
+ Authorization: "Bearer test-token"
36
+ expect:
37
+ status: 200
38
+
39
+ - id: custom-headers-read
40
+ name: Custom headers on read
41
+ description: Client should send custom headers on read
42
+ setup:
43
+ - action: create
44
+ as: streamPath
45
+ - action: append
46
+ path: ${streamPath}
47
+ data: "data"
48
+ operations:
49
+ - action: read
50
+ path: ${streamPath}
51
+ headers:
52
+ Authorization: "Bearer read-token"
53
+ live: false
54
+ expect:
55
+ status: 200
56
+
57
+ - id: custom-headers-head
58
+ name: Custom headers on head
59
+ description: Client should send custom headers on HEAD
60
+ setup:
61
+ - action: create
62
+ as: streamPath
63
+ operations:
64
+ - action: head
65
+ path: ${streamPath}
66
+ headers:
67
+ X-Trace-Id: "trace-abc"
68
+ expect:
69
+ status: 200
70
+
71
+ - id: custom-headers-delete
72
+ name: Custom headers on delete
73
+ description: Client should send custom headers on delete
74
+ setup:
75
+ - action: create
76
+ as: streamPath
77
+ operations:
78
+ - action: delete
79
+ path: ${streamPath}
80
+ headers:
81
+ Authorization: "Bearer delete-token"
82
+ expect:
83
+ status: 200
84
+
85
+ - id: content-type-preserved
86
+ name: Content type is preserved
87
+ description: Content type set on create should be returned on read
88
+ operations:
89
+ - action: create
90
+ as: streamPath
91
+ contentType: application/x-custom-type
92
+ expect:
93
+ status: 201
94
+ - action: head
95
+ path: ${streamPath}
96
+ expect:
97
+ contentType: application/x-custom-type
98
+ cleanup:
99
+ - action: delete
100
+ path: ${streamPath}
101
+
102
+ - id: content-type-with-charset
103
+ name: Content type with charset
104
+ description: Content type with charset parameter should be preserved
105
+ operations:
106
+ - action: create
107
+ as: streamPath
108
+ contentType: text/plain; charset=utf-8
109
+ expect:
110
+ status: 201
111
+ - action: head
112
+ path: ${streamPath}
113
+ expect:
114
+ contentType: text/plain; charset=utf-8
115
+ cleanup:
116
+ - action: delete
117
+ path: ${streamPath}
@@ -0,0 +1,148 @@
1
+ id: lifecycle-stream
2
+ name: Stream Lifecycle
3
+ description: Tests for complete stream lifecycle operations
4
+ category: lifecycle
5
+ tags:
6
+ - core
7
+ - lifecycle
8
+
9
+ tests:
10
+ - id: full-lifecycle
11
+ name: Full stream lifecycle
12
+ description: Create, append, read, and delete a stream
13
+ operations:
14
+ - action: create
15
+ as: streamPath
16
+ contentType: text/plain
17
+ expect:
18
+ status: 201
19
+ - action: append
20
+ path: ${streamPath}
21
+ data: "lifecycle-test-data"
22
+ expect:
23
+ status: 200
24
+ - action: read
25
+ path: ${streamPath}
26
+ live: false
27
+ expect:
28
+ data: "lifecycle-test-data"
29
+ upToDate: true
30
+ - action: delete
31
+ path: ${streamPath}
32
+ expect:
33
+ status: 200
34
+ - action: read
35
+ path: ${streamPath}
36
+ live: false
37
+ expect:
38
+ status: 404
39
+
40
+ - id: connect-existing
41
+ name: Connect to existing stream
42
+ description: Connect should work for existing streams
43
+ setup:
44
+ - action: create
45
+ as: streamPath
46
+ - action: append
47
+ path: ${streamPath}
48
+ data: "pre-existing"
49
+ operations:
50
+ - action: connect
51
+ path: ${streamPath}
52
+ expect:
53
+ status: 200
54
+ - action: read
55
+ path: ${streamPath}
56
+ live: false
57
+ expect:
58
+ data: "pre-existing"
59
+
60
+ - id: head-metadata
61
+ name: HEAD returns metadata
62
+ description: HEAD should return stream metadata without body
63
+ setup:
64
+ - action: create
65
+ as: streamPath
66
+ contentType: application/json
67
+ - action: append
68
+ path: ${streamPath}
69
+ data: '{"key": "value"}'
70
+ operations:
71
+ - action: head
72
+ path: ${streamPath}
73
+ expect:
74
+ status: 200
75
+ contentType: application/json
76
+ hasOffset: true
77
+
78
+ - id: head-empty-stream
79
+ name: HEAD on empty stream
80
+ description: HEAD should work on empty stream
81
+ setup:
82
+ - action: create
83
+ as: streamPath
84
+ operations:
85
+ - action: head
86
+ path: ${streamPath}
87
+ expect:
88
+ status: 200
89
+ hasOffset: true
90
+
91
+ - id: multiple-streams
92
+ name: Multiple independent streams
93
+ description: Operations on one stream should not affect others
94
+ operations:
95
+ - action: create
96
+ as: stream1
97
+ - action: create
98
+ as: stream2
99
+ - action: append
100
+ path: ${stream1}
101
+ data: "stream1-data"
102
+ - action: append
103
+ path: ${stream2}
104
+ data: "stream2-data"
105
+ - action: read
106
+ path: ${stream1}
107
+ live: false
108
+ expect:
109
+ data: "stream1-data"
110
+ - action: read
111
+ path: ${stream2}
112
+ live: false
113
+ expect:
114
+ data: "stream2-data"
115
+ - action: delete
116
+ path: ${stream1}
117
+ - action: read
118
+ path: ${stream2}
119
+ live: false
120
+ expect:
121
+ data: "stream2-data"
122
+ cleanup:
123
+ - action: delete
124
+ path: ${stream2}
125
+
126
+ - id: recreate-after-delete
127
+ name: Create after delete
128
+ description: Creating after delete should succeed as a new stream
129
+ operations:
130
+ - action: create
131
+ as: streamPath
132
+ expect:
133
+ status: 201
134
+ - action: append
135
+ path: ${streamPath}
136
+ data: "original"
137
+ - action: delete
138
+ path: ${streamPath}
139
+ expect:
140
+ status: 200
141
+ # Create at same path should succeed as a new stream
142
+ - action: create
143
+ path: ${streamPath}
144
+ expect:
145
+ status: 201
146
+ cleanup:
147
+ - action: delete
148
+ path: ${streamPath}