@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,108 @@
1
+ id: consumer-errors
2
+ name: Consumer Error Handling
3
+ description: Tests for error handling in consumer operations
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - errors
8
+ - read
9
+
10
+ tests:
11
+ - id: read-nonexistent-stream
12
+ name: Read non-existent stream
13
+ description: Client should handle 404 when reading non-existent stream
14
+ operations:
15
+ - action: read
16
+ path: /nonexistent-read-stream
17
+ live: false
18
+ expect:
19
+ status: 404
20
+ errorCode: NOT_FOUND
21
+
22
+ - id: read-deleted-stream
23
+ name: Read deleted stream
24
+ description: Client should handle 404 when stream was deleted
25
+ setup:
26
+ - action: create
27
+ as: streamPath
28
+ - action: append
29
+ path: ${streamPath}
30
+ data: "before-delete"
31
+ - action: delete
32
+ path: ${streamPath}
33
+ operations:
34
+ - action: read
35
+ path: ${streamPath}
36
+ live: false
37
+ expect:
38
+ status: 404
39
+ errorCode: NOT_FOUND
40
+
41
+ - id: read-invalid-offset
42
+ name: Read with invalid offset
43
+ description: Client should handle invalid offset format
44
+ setup:
45
+ - action: create
46
+ as: streamPath
47
+ - action: append
48
+ path: ${streamPath}
49
+ data: "data"
50
+ operations:
51
+ - action: read
52
+ path: ${streamPath}
53
+ offset: "not-a-valid-offset-format"
54
+ live: false
55
+ expect:
56
+ status: 400
57
+ errorCode: INVALID_OFFSET
58
+
59
+ - id: read-future-offset
60
+ name: Read with future offset
61
+ description: Client should handle offset beyond stream end
62
+ setup:
63
+ - action: create
64
+ as: streamPath
65
+ - action: append
66
+ path: ${streamPath}
67
+ data: "small"
68
+ operations:
69
+ - action: read
70
+ path: ${streamPath}
71
+ # Use an offset that's definitely beyond the stream
72
+ offset: "ffffffffffffffff"
73
+ live: false
74
+ expect:
75
+ # Server should either return empty or 400
76
+ chunkCount: 0
77
+
78
+ - id: longpoll-nonexistent
79
+ name: Long-poll non-existent stream
80
+ description: Long-poll should fail on non-existent stream
81
+ tags:
82
+ - longpoll
83
+ requires:
84
+ - long-poll
85
+ operations:
86
+ - action: read
87
+ path: /nonexistent-longpoll-stream
88
+ live: long-poll
89
+ timeoutMs: 1000
90
+ expect:
91
+ status: 404
92
+ errorCode: NOT_FOUND
93
+
94
+ - id: sse-nonexistent
95
+ name: SSE non-existent stream
96
+ description: SSE should fail on non-existent stream
97
+ tags:
98
+ - sse
99
+ requires:
100
+ - sse
101
+ operations:
102
+ - action: read
103
+ path: /nonexistent-sse-stream
104
+ live: sse
105
+ timeoutMs: 1000
106
+ expect:
107
+ status: 404
108
+ errorCode: NOT_FOUND
@@ -0,0 +1,209 @@
1
+ id: consumer-message-ordering
2
+ name: Message Ordering
3
+ description: Tests that messages are received in strict offset order
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - ordering
8
+ - live
9
+
10
+ tests:
11
+ - id: ordering-sequential-appends
12
+ name: Sequential appends maintain order
13
+ description: Messages should be read in the same order they were appended
14
+ setup:
15
+ - action: create
16
+ as: streamPath
17
+ operations:
18
+ - action: append
19
+ path: ${streamPath}
20
+ data: "first"
21
+ - action: append
22
+ path: ${streamPath}
23
+ data: "second"
24
+ - action: append
25
+ path: ${streamPath}
26
+ data: "third"
27
+ - action: append
28
+ path: ${streamPath}
29
+ data: "fourth"
30
+ - action: append
31
+ path: ${streamPath}
32
+ data: "fifth"
33
+ - action: read
34
+ path: ${streamPath}
35
+ expect:
36
+ data: "firstsecondthirdfourthfifth"
37
+
38
+ - id: ordering-longpoll-preserves-order
39
+ name: Long-poll preserves message order
40
+ description: Messages received via long-poll should be in offset order
41
+ setup:
42
+ - action: create
43
+ as: streamPath
44
+ - action: append
45
+ path: ${streamPath}
46
+ data: "a"
47
+ - action: append
48
+ path: ${streamPath}
49
+ data: "b"
50
+ - action: append
51
+ path: ${streamPath}
52
+ data: "c"
53
+ operations:
54
+ - action: read
55
+ path: ${streamPath}
56
+ live: long-poll
57
+ waitForUpToDate: true
58
+ expect:
59
+ data: "abc"
60
+
61
+ - id: ordering-sse-preserves-order
62
+ name: SSE preserves message order
63
+ description: Messages received via SSE should be in offset order
64
+ requires:
65
+ - sse
66
+ setup:
67
+ - action: create
68
+ as: streamPath
69
+ - action: append
70
+ path: ${streamPath}
71
+ data: "x"
72
+ - action: append
73
+ path: ${streamPath}
74
+ data: "y"
75
+ - action: append
76
+ path: ${streamPath}
77
+ data: "z"
78
+ operations:
79
+ - action: read
80
+ path: ${streamPath}
81
+ live: sse
82
+ waitForUpToDate: true
83
+ expect:
84
+ data: "xyz"
85
+
86
+ - id: ordering-offset-monotonic
87
+ name: Offsets are monotonically increasing
88
+ description: Each message's offset should be greater than the previous
89
+ setup:
90
+ - action: create
91
+ as: streamPath
92
+ - action: append
93
+ path: ${streamPath}
94
+ data: "1"
95
+ expect:
96
+ storeOffsetAs: offset1
97
+ - action: append
98
+ path: ${streamPath}
99
+ data: "2"
100
+ expect:
101
+ storeOffsetAs: offset2
102
+ - action: append
103
+ path: ${streamPath}
104
+ data: "3"
105
+ expect:
106
+ storeOffsetAs: offset3
107
+ operations:
108
+ # Read from offset1 should get "23"
109
+ - action: read
110
+ path: ${streamPath}
111
+ offset: ${offset1}
112
+ expect:
113
+ data: "23"
114
+ # Read from offset2 should get "3"
115
+ - action: read
116
+ path: ${streamPath}
117
+ offset: ${offset2}
118
+ expect:
119
+ data: "3"
120
+ # Read from offset3 should get nothing (at end)
121
+ - action: read
122
+ path: ${streamPath}
123
+ offset: ${offset3}
124
+ expect:
125
+ chunkCount: 0
126
+ upToDate: true
127
+
128
+ - id: ordering-live-new-data
129
+ name: Live mode receives new data in order
130
+ description: New data appended during live read should arrive in order
131
+ requires:
132
+ - long-poll
133
+ setup:
134
+ - action: create
135
+ as: streamPath
136
+ - action: append
137
+ path: ${streamPath}
138
+ data: "init"
139
+ expect:
140
+ storeOffsetAs: initialOffset
141
+ operations:
142
+ # Start long-poll in background
143
+ - action: read
144
+ path: ${streamPath}
145
+ offset: ${initialOffset}
146
+ live: long-poll
147
+ timeoutMs: 5000
148
+ maxChunks: 3
149
+ background: true
150
+ as: liveRead
151
+ - action: wait
152
+ ms: 100
153
+ # Append data in order
154
+ - action: server-append
155
+ path: ${streamPath}
156
+ data: "A"
157
+ - action: server-append
158
+ path: ${streamPath}
159
+ data: "B"
160
+ - action: server-append
161
+ path: ${streamPath}
162
+ data: "C"
163
+ # Wait for live read to get the data
164
+ - action: await
165
+ ref: liveRead
166
+ expect:
167
+ dataContains: "ABC"
168
+
169
+ - id: ordering-many-messages
170
+ name: Order preserved with many messages
171
+ description: Order should be maintained even with many sequential appends
172
+ setup:
173
+ - action: create
174
+ as: streamPath
175
+ operations:
176
+ - action: append
177
+ path: ${streamPath}
178
+ data: "01"
179
+ - action: append
180
+ path: ${streamPath}
181
+ data: "02"
182
+ - action: append
183
+ path: ${streamPath}
184
+ data: "03"
185
+ - action: append
186
+ path: ${streamPath}
187
+ data: "04"
188
+ - action: append
189
+ path: ${streamPath}
190
+ data: "05"
191
+ - action: append
192
+ path: ${streamPath}
193
+ data: "06"
194
+ - action: append
195
+ path: ${streamPath}
196
+ data: "07"
197
+ - action: append
198
+ path: ${streamPath}
199
+ data: "08"
200
+ - action: append
201
+ path: ${streamPath}
202
+ data: "09"
203
+ - action: append
204
+ path: ${streamPath}
205
+ data: "10"
206
+ - action: read
207
+ path: ${streamPath}
208
+ expect:
209
+ data: "01020304050607080910"
@@ -0,0 +1,209 @@
1
+ id: consumer-offset-handling
2
+ name: Offset Handling
3
+ description: Tests for offset edge cases and boundary conditions
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - offset
8
+ - edge-cases
9
+
10
+ tests:
11
+ - id: offset-minus-one-start
12
+ name: Offset -1 reads from start
13
+ description: Using offset -1 should read from the beginning of the stream
14
+ setup:
15
+ - action: create
16
+ as: streamPath
17
+ - action: append
18
+ path: ${streamPath}
19
+ data: "beginning"
20
+ - action: append
21
+ path: ${streamPath}
22
+ data: "middle"
23
+ - action: append
24
+ path: ${streamPath}
25
+ data: "end"
26
+ operations:
27
+ - action: read
28
+ path: ${streamPath}
29
+ offset: "-1"
30
+ expect:
31
+ data: "beginningmiddleend"
32
+
33
+ - id: offset-empty-stream
34
+ name: Read from empty stream
35
+ description: Reading from an empty stream should return no data and up-to-date
36
+ setup:
37
+ - action: create
38
+ as: streamPath
39
+ operations:
40
+ - action: read
41
+ path: ${streamPath}
42
+ expect:
43
+ chunkCount: 0
44
+ upToDate: true
45
+
46
+ - id: offset-at-end
47
+ name: Read from end of stream
48
+ description: Reading from the current end offset should return no new data
49
+ setup:
50
+ - action: create
51
+ as: streamPath
52
+ - action: append
53
+ path: ${streamPath}
54
+ data: "data"
55
+ expect:
56
+ storeOffsetAs: endOffset
57
+ operations:
58
+ - action: read
59
+ path: ${streamPath}
60
+ offset: ${endOffset}
61
+ expect:
62
+ chunkCount: 0
63
+ upToDate: true
64
+
65
+ - id: offset-resume-partial
66
+ name: Resume read from middle offset
67
+ description: Reading from a middle offset should only return subsequent data
68
+ setup:
69
+ - action: create
70
+ as: streamPath
71
+ - action: append
72
+ path: ${streamPath}
73
+ data: "part1"
74
+ expect:
75
+ storeOffsetAs: afterPart1
76
+ - action: append
77
+ path: ${streamPath}
78
+ data: "part2"
79
+ expect:
80
+ storeOffsetAs: afterPart2
81
+ - action: append
82
+ path: ${streamPath}
83
+ data: "part3"
84
+ operations:
85
+ # From afterPart1, should get part2 and part3
86
+ - action: read
87
+ path: ${streamPath}
88
+ offset: ${afterPart1}
89
+ expect:
90
+ data: "part2part3"
91
+ # From afterPart2, should only get part3
92
+ - action: read
93
+ path: ${streamPath}
94
+ offset: ${afterPart2}
95
+ expect:
96
+ data: "part3"
97
+
98
+ - id: offset-stored-correctly
99
+ name: Stored offset can be used to resume
100
+ description: Offsets stored from reads should work for resumption
101
+ setup:
102
+ - action: create
103
+ as: streamPath
104
+ - action: append
105
+ path: ${streamPath}
106
+ data: "chunk1"
107
+ - action: append
108
+ path: ${streamPath}
109
+ data: "chunk2"
110
+ operations:
111
+ # First read stores the offset
112
+ - action: read
113
+ path: ${streamPath}
114
+ expect:
115
+ data: "chunk1chunk2"
116
+ storeOffsetAs: resumeOffset
117
+ # Append more data
118
+ - action: append
119
+ path: ${streamPath}
120
+ data: "chunk3"
121
+ # Resume from stored offset should only get new data
122
+ - action: read
123
+ path: ${streamPath}
124
+ offset: ${resumeOffset}
125
+ expect:
126
+ data: "chunk3"
127
+
128
+ - id: offset-longpoll-resume
129
+ name: Long-poll respects offset for resumption
130
+ description: Long-poll should only return data after the specified offset
131
+ setup:
132
+ - action: create
133
+ as: streamPath
134
+ - action: append
135
+ path: ${streamPath}
136
+ data: "old"
137
+ expect:
138
+ storeOffsetAs: oldOffset
139
+ - action: append
140
+ path: ${streamPath}
141
+ data: "new"
142
+ operations:
143
+ - action: read
144
+ path: ${streamPath}
145
+ offset: ${oldOffset}
146
+ live: long-poll
147
+ timeoutMs: 1000
148
+ expect:
149
+ data: "new"
150
+ upToDate: true
151
+
152
+ - id: offset-sse-resume
153
+ name: SSE respects offset for resumption
154
+ description: SSE should only return data after the specified offset
155
+ requires:
156
+ - sse
157
+ setup:
158
+ - action: create
159
+ as: streamPath
160
+ - action: append
161
+ path: ${streamPath}
162
+ data: "old"
163
+ expect:
164
+ storeOffsetAs: oldOffset
165
+ - action: append
166
+ path: ${streamPath}
167
+ data: "new"
168
+ operations:
169
+ - action: read
170
+ path: ${streamPath}
171
+ offset: ${oldOffset}
172
+ live: sse
173
+ waitForUpToDate: true
174
+ expect:
175
+ data: "new"
176
+
177
+ - id: offset-multiple-reads-same-offset
178
+ name: Multiple reads from same offset
179
+ description: Reading from the same offset multiple times should return the same data
180
+ setup:
181
+ - action: create
182
+ as: streamPath
183
+ - action: append
184
+ path: ${streamPath}
185
+ data: "consistent"
186
+ expect:
187
+ storeOffsetAs: startOffset
188
+ - action: append
189
+ path: ${streamPath}
190
+ data: "data"
191
+ operations:
192
+ # First read
193
+ - action: read
194
+ path: ${streamPath}
195
+ offset: ${startOffset}
196
+ expect:
197
+ data: "data"
198
+ # Second read from same offset should get same result
199
+ - action: read
200
+ path: ${streamPath}
201
+ offset: ${startOffset}
202
+ expect:
203
+ data: "data"
204
+ # Third read to confirm
205
+ - action: read
206
+ path: ${streamPath}
207
+ offset: ${startOffset}
208
+ expect:
209
+ data: "data"
@@ -0,0 +1,197 @@
1
+ id: consumer-offset
2
+ name: Offset Resumption
3
+ description: Tests for offset handling and stream resumption
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - offset
8
+ - resumption
9
+
10
+ tests:
11
+ - id: offset-monotonic
12
+ name: Offsets are monotonically increasing
13
+ description: Each read should return an offset greater than the previous
14
+ setup:
15
+ - action: create
16
+ as: streamPath
17
+ - action: append
18
+ path: ${streamPath}
19
+ data: "AAA"
20
+ expect:
21
+ storeOffsetAs: afterA
22
+ - action: append
23
+ path: ${streamPath}
24
+ data: "BBB"
25
+ expect:
26
+ storeOffsetAs: afterB
27
+ - action: append
28
+ path: ${streamPath}
29
+ data: "CCC"
30
+ expect:
31
+ storeOffsetAs: afterC
32
+ operations:
33
+ # Read from afterA should get BBB and CCC
34
+ - action: read
35
+ path: ${streamPath}
36
+ offset: ${afterA}
37
+ live: false
38
+ expect:
39
+ data: "BBBCCC"
40
+ storeOffsetAs: readOffset1
41
+ # Read from afterB should get only CCC
42
+ - action: read
43
+ path: ${streamPath}
44
+ offset: ${afterB}
45
+ live: false
46
+ expect:
47
+ data: "CCC"
48
+ storeOffsetAs: readOffset2
49
+ # Read from afterC should get nothing (up-to-date)
50
+ - action: read
51
+ path: ${streamPath}
52
+ offset: ${afterC}
53
+ live: false
54
+ expect:
55
+ upToDate: true
56
+
57
+ - id: offset-exact-resumption
58
+ name: Exact resumption from offset
59
+ description: Reading from an offset should not skip or duplicate data
60
+ setup:
61
+ - action: create
62
+ as: streamPath
63
+ - action: append
64
+ path: ${streamPath}
65
+ data: "AAAA"
66
+ expect:
67
+ storeOffsetAs: afterA
68
+ - action: append
69
+ path: ${streamPath}
70
+ data: "BBBB"
71
+ expect:
72
+ storeOffsetAs: afterB
73
+ - action: append
74
+ path: ${streamPath}
75
+ data: "CCCC"
76
+ operations:
77
+ # Read from beginning, should get all
78
+ - action: read
79
+ path: ${streamPath}
80
+ live: false
81
+ expect:
82
+ data: "AAAABBBBCCCC"
83
+ # Read from after A, should get B and C
84
+ - action: read
85
+ path: ${streamPath}
86
+ offset: ${afterA}
87
+ live: false
88
+ expect:
89
+ data: "BBBBCCCC"
90
+ # Read from after B, should get only C
91
+ - action: read
92
+ path: ${streamPath}
93
+ offset: ${afterB}
94
+ live: false
95
+ expect:
96
+ data: "CCCC"
97
+
98
+ - id: offset-persists-across-sessions
99
+ name: Offset works across client sessions
100
+ description: An offset obtained in one session should work in another
101
+ setup:
102
+ - action: create
103
+ as: streamPath
104
+ - action: append
105
+ path: ${streamPath}
106
+ data: "session1-data"
107
+ expect:
108
+ storeOffsetAs: savedOffset
109
+ - action: append
110
+ path: ${streamPath}
111
+ data: "session2-data"
112
+ operations:
113
+ # Simulate new session by just using the saved offset
114
+ - action: read
115
+ path: ${streamPath}
116
+ offset: ${savedOffset}
117
+ live: false
118
+ expect:
119
+ data: "session2-data"
120
+ upToDate: true
121
+
122
+ - id: offset-from-head
123
+ name: Offset from HEAD request
124
+ description: Offset from HEAD should be usable for reading
125
+ setup:
126
+ - action: create
127
+ as: streamPath
128
+ - action: append
129
+ path: ${streamPath}
130
+ data: "before-head"
131
+ operations:
132
+ - action: head
133
+ path: ${streamPath}
134
+ expect:
135
+ storeAs: headResult
136
+ - action: append
137
+ path: ${streamPath}
138
+ data: "after-head"
139
+ - action: read
140
+ path: ${streamPath}
141
+ offset: ${headResult.offset}
142
+ live: false
143
+ expect:
144
+ data: "after-head"
145
+
146
+ - id: offset-zero-start
147
+ name: Read from offset zero
148
+ description: Reading from offset 0 or empty should start from beginning
149
+ setup:
150
+ - action: create
151
+ as: streamPath
152
+ - action: append
153
+ path: ${streamPath}
154
+ data: "from-the-start"
155
+ operations:
156
+ - action: read
157
+ path: ${streamPath}
158
+ offset: "0"
159
+ live: false
160
+ expect:
161
+ data: "from-the-start"
162
+ - action: read
163
+ path: ${streamPath}
164
+ live: false
165
+ expect:
166
+ data: "from-the-start"
167
+
168
+ - id: offset-byte-exactness
169
+ name: Byte-exact offset resumption
170
+ description: Resuming from offset should not lose or duplicate any bytes
171
+ tags:
172
+ - property
173
+ setup:
174
+ - action: create
175
+ as: streamPath
176
+ - action: append
177
+ path: ${streamPath}
178
+ data: "0123456789"
179
+ expect:
180
+ storeOffsetAs: mid
181
+ - action: append
182
+ path: ${streamPath}
183
+ data: "abcdefghij"
184
+ operations:
185
+ # Full read should have all data
186
+ - action: read
187
+ path: ${streamPath}
188
+ live: false
189
+ expect:
190
+ data: "0123456789abcdefghij"
191
+ # Partial read from mid offset should have only second half
192
+ - action: read
193
+ path: ${streamPath}
194
+ offset: ${mid}
195
+ live: false
196
+ expect:
197
+ data: "abcdefghij"