@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,173 @@
1
+ id: consumer-catchup
2
+ name: Catch-up Reads
3
+ description: Tests for reading existing data from streams (catch-up mode)
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - read
8
+ - catchup
9
+
10
+ tests:
11
+ - id: read-empty-stream
12
+ name: Read empty stream
13
+ description: Client should handle reading from an empty stream
14
+ setup:
15
+ - action: create
16
+ as: streamPath
17
+ operations:
18
+ - action: read
19
+ path: ${streamPath}
20
+ live: false
21
+ expect:
22
+ status: 200
23
+ chunkCount: 0
24
+ upToDate: true
25
+
26
+ - id: read-single-chunk
27
+ name: Read single chunk
28
+ description: Client should read a single chunk of data
29
+ setup:
30
+ - action: create
31
+ as: streamPath
32
+ - action: append
33
+ path: ${streamPath}
34
+ data: "Hello, World!"
35
+ operations:
36
+ - action: read
37
+ path: ${streamPath}
38
+ live: false
39
+ expect:
40
+ status: 200
41
+ data: "Hello, World!"
42
+ upToDate: true
43
+
44
+ - id: read-multiple-chunks
45
+ name: Read multiple chunks
46
+ description: Client should read all chunks concatenated
47
+ setup:
48
+ - action: create
49
+ as: streamPath
50
+ - action: append
51
+ path: ${streamPath}
52
+ data: "Chunk1 "
53
+ - action: append
54
+ path: ${streamPath}
55
+ data: "Chunk2 "
56
+ - action: append
57
+ path: ${streamPath}
58
+ data: "Chunk3"
59
+ operations:
60
+ - action: read
61
+ path: ${streamPath}
62
+ live: false
63
+ expect:
64
+ data: "Chunk1 Chunk2 Chunk3"
65
+ upToDate: true
66
+
67
+ - id: read-from-offset
68
+ name: Read from specific offset
69
+ description: Client should resume reading from a given offset
70
+ setup:
71
+ - action: create
72
+ as: streamPath
73
+ - action: append
74
+ path: ${streamPath}
75
+ data: "First"
76
+ expect:
77
+ storeOffsetAs: firstOffset
78
+ - action: append
79
+ path: ${streamPath}
80
+ data: "Second"
81
+ operations:
82
+ - action: read
83
+ path: ${streamPath}
84
+ offset: ${firstOffset}
85
+ live: false
86
+ expect:
87
+ data: "Second"
88
+ upToDate: true
89
+
90
+ - id: read-from-beginning
91
+ name: Read from beginning after offset
92
+ description: Reading without offset should start from beginning
93
+ setup:
94
+ - action: create
95
+ as: streamPath
96
+ - action: append
97
+ path: ${streamPath}
98
+ data: "Beginning"
99
+ - action: append
100
+ path: ${streamPath}
101
+ data: "End"
102
+ operations:
103
+ - action: read
104
+ path: ${streamPath}
105
+ live: false
106
+ expect:
107
+ data: "BeginningEnd"
108
+ upToDate: true
109
+
110
+ - id: read-preserves-offset
111
+ name: Read returns usable offset
112
+ description: Offset from read result can be used to resume
113
+ setup:
114
+ - action: create
115
+ as: streamPath
116
+ - action: append
117
+ path: ${streamPath}
118
+ data: "Part1"
119
+ - action: append
120
+ path: ${streamPath}
121
+ data: "Part2"
122
+ operations:
123
+ - action: read
124
+ path: ${streamPath}
125
+ maxChunks: 1
126
+ live: false
127
+ expect:
128
+ storeOffsetAs: resumeOffset
129
+ - action: append
130
+ path: ${streamPath}
131
+ data: "Part3"
132
+ - action: read
133
+ path: ${streamPath}
134
+ offset: ${resumeOffset}
135
+ live: false
136
+ expect:
137
+ dataContains: "Part3"
138
+
139
+ - id: read-binary-data
140
+ name: Read binary data
141
+ description: Client should correctly read binary data
142
+ setup:
143
+ - action: create
144
+ as: streamPath
145
+ contentType: application/octet-stream
146
+ - action: append
147
+ path: ${streamPath}
148
+ binaryData: "AQID" # [1, 2, 3] in base64
149
+ operations:
150
+ - action: read
151
+ path: ${streamPath}
152
+ live: false
153
+ expect:
154
+ status: 200
155
+ upToDate: true
156
+
157
+ - id: read-unicode-data
158
+ name: Read unicode data
159
+ description: Client should correctly handle unicode in reads
160
+ setup:
161
+ - action: create
162
+ as: streamPath
163
+ contentType: text/plain; charset=utf-8
164
+ - action: append
165
+ path: ${streamPath}
166
+ data: "Unicode: 日本語 🎉 Ñoño"
167
+ operations:
168
+ - action: read
169
+ path: ${streamPath}
170
+ live: false
171
+ expect:
172
+ data: "Unicode: 日本語 🎉 Ñoño"
173
+ upToDate: true
@@ -0,0 +1,132 @@
1
+ id: consumer-longpoll
2
+ name: Long-Poll Reads
3
+ description: Tests for long-poll live reading from streams
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - read
8
+ - longpoll
9
+ - live
10
+ requires:
11
+ - long-poll
12
+
13
+ tests:
14
+ - id: longpoll-waits-for-data
15
+ name: Long-poll waits for new data
16
+ description: Client should wait for new data in long-poll mode
17
+ setup:
18
+ - action: create
19
+ as: streamPath
20
+ - action: append
21
+ path: ${streamPath}
22
+ data: "initial"
23
+ expect:
24
+ storeOffsetAs: initialOffset
25
+ operations:
26
+ # Start long-poll read in background (will be waiting for new data)
27
+ - action: read
28
+ path: ${streamPath}
29
+ offset: ${initialOffset}
30
+ live: long-poll
31
+ timeoutMs: 10000
32
+ background: true
33
+ as: readOp
34
+ # Wait for the read to start
35
+ - action: wait
36
+ ms: 200
37
+ # Append data via direct server HTTP (adapter is blocked on read)
38
+ - action: server-append
39
+ path: ${streamPath}
40
+ data: "new-data"
41
+ # Wait for the background read to complete
42
+ - action: await
43
+ ref: readOp
44
+ expect:
45
+ dataContains: "new-data"
46
+
47
+ - id: longpoll-returns-immediately-with-data
48
+ name: Long-poll returns immediately if data exists
49
+ description: Long-poll should not wait if there's already data to return
50
+ setup:
51
+ - action: create
52
+ as: streamPath
53
+ - action: append
54
+ path: ${streamPath}
55
+ data: "existing-data"
56
+ operations:
57
+ - action: read
58
+ path: ${streamPath}
59
+ live: long-poll
60
+ timeoutMs: 1000
61
+ expect:
62
+ data: "existing-data"
63
+ upToDate: true
64
+
65
+ - id: longpoll-timeout
66
+ name: Long-poll times out gracefully
67
+ description: Long-poll should timeout and return up-to-date when no new data arrives
68
+ setup:
69
+ - action: create
70
+ as: streamPath
71
+ - action: append
72
+ path: ${streamPath}
73
+ data: "initial"
74
+ expect:
75
+ storeOffsetAs: offset
76
+ operations:
77
+ - action: read
78
+ path: ${streamPath}
79
+ offset: ${offset}
80
+ live: long-poll
81
+ timeoutMs: 500
82
+ expect:
83
+ chunkCount: 0
84
+ upToDate: true
85
+
86
+ - id: longpoll-multiple-chunks
87
+ name: Long-poll receives multiple chunks
88
+ description: Long-poll should receive all available chunks
89
+ setup:
90
+ - action: create
91
+ as: streamPath
92
+ operations:
93
+ - action: append
94
+ path: ${streamPath}
95
+ data: "chunk1"
96
+ - action: append
97
+ path: ${streamPath}
98
+ data: "chunk2"
99
+ - action: append
100
+ path: ${streamPath}
101
+ data: "chunk3"
102
+ - action: read
103
+ path: ${streamPath}
104
+ live: long-poll
105
+ timeoutMs: 1000
106
+ expect:
107
+ data: "chunk1chunk2chunk3"
108
+ upToDate: true
109
+
110
+ - id: longpoll-resume-from-offset
111
+ name: Long-poll resumes from offset
112
+ description: Long-poll should resume from the provided offset
113
+ setup:
114
+ - action: create
115
+ as: streamPath
116
+ - action: append
117
+ path: ${streamPath}
118
+ data: "old-data"
119
+ expect:
120
+ storeOffsetAs: offset
121
+ - action: append
122
+ path: ${streamPath}
123
+ data: "new-data"
124
+ operations:
125
+ - action: read
126
+ path: ${streamPath}
127
+ offset: ${offset}
128
+ live: long-poll
129
+ timeoutMs: 1000
130
+ expect:
131
+ data: "new-data"
132
+ upToDate: true
@@ -0,0 +1,145 @@
1
+ id: consumer-sse
2
+ name: SSE Reads
3
+ description: Tests for Server-Sent Events live reading from streams
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - read
8
+ - sse
9
+ - live
10
+ requires:
11
+ - sse
12
+
13
+ tests:
14
+ - id: sse-receives-existing-data
15
+ name: SSE receives existing data
16
+ description: SSE stream should first deliver existing data
17
+ setup:
18
+ - action: create
19
+ as: streamPath
20
+ - action: append
21
+ path: ${streamPath}
22
+ data: "existing1"
23
+ - action: append
24
+ path: ${streamPath}
25
+ data: "existing2"
26
+ operations:
27
+ - action: read
28
+ path: ${streamPath}
29
+ live: sse
30
+ maxChunks: 2
31
+ waitForUpToDate: true
32
+ expect:
33
+ dataContainsAll:
34
+ - "existing1"
35
+ - "existing2"
36
+ minChunks: 1
37
+
38
+ - id: sse-receives-new-data
39
+ name: SSE receives new data
40
+ description: SSE stream should receive data appended after connection
41
+ setup:
42
+ - action: create
43
+ as: streamPath
44
+ - action: append
45
+ path: ${streamPath}
46
+ data: "initial"
47
+ expect:
48
+ storeOffsetAs: offset
49
+ operations:
50
+ # Start SSE read in background (will be waiting for new data)
51
+ - action: read
52
+ path: ${streamPath}
53
+ offset: ${offset}
54
+ live: sse
55
+ maxChunks: 1
56
+ timeoutMs: 10000
57
+ background: true
58
+ as: readOp
59
+ # Wait for the SSE connection to establish
60
+ - action: wait
61
+ ms: 200
62
+ # Append data via direct server HTTP (adapter is blocked on read)
63
+ - action: server-append
64
+ path: ${streamPath}
65
+ data: "new-data"
66
+ # Wait for the background read to complete
67
+ - action: await
68
+ ref: readOp
69
+ expect:
70
+ dataContains: "new-data"
71
+ minChunks: 1
72
+
73
+ - id: sse-resumes-from-offset
74
+ name: SSE resumes from offset
75
+ description: SSE should start from specified offset
76
+ setup:
77
+ - action: create
78
+ as: streamPath
79
+ - action: append
80
+ path: ${streamPath}
81
+ data: "skip-this"
82
+ expect:
83
+ storeOffsetAs: skipOffset
84
+ - action: append
85
+ path: ${streamPath}
86
+ data: "include-this"
87
+ operations:
88
+ - action: read
89
+ path: ${streamPath}
90
+ offset: ${skipOffset}
91
+ live: sse
92
+ waitForUpToDate: true
93
+ expect:
94
+ data: "include-this"
95
+
96
+ - id: sse-up-to-date-signal
97
+ name: SSE signals up-to-date
98
+ description: SSE should indicate when caught up to head
99
+ setup:
100
+ - action: create
101
+ as: streamPath
102
+ - action: append
103
+ path: ${streamPath}
104
+ data: "data"
105
+ operations:
106
+ - action: read
107
+ path: ${streamPath}
108
+ live: sse
109
+ waitForUpToDate: true
110
+ expect:
111
+ upToDate: true
112
+
113
+ - id: sse-empty-stream
114
+ name: SSE on empty stream
115
+ description: SSE should work on empty stream and signal up-to-date
116
+ setup:
117
+ - action: create
118
+ as: streamPath
119
+ operations:
120
+ - action: read
121
+ path: ${streamPath}
122
+ live: sse
123
+ waitForUpToDate: true
124
+ expect:
125
+ upToDate: true
126
+ chunkCount: 0
127
+
128
+ - id: sse-binary-data
129
+ name: SSE with binary data
130
+ description: SSE should correctly transmit binary data
131
+ setup:
132
+ - action: create
133
+ as: streamPath
134
+ contentType: application/octet-stream
135
+ - action: append
136
+ path: ${streamPath}
137
+ binaryData: "/f7/AA==" # some binary bytes
138
+ operations:
139
+ - action: read
140
+ path: ${streamPath}
141
+ live: sse
142
+ waitForUpToDate: true
143
+ expect:
144
+ minChunks: 1
145
+ upToDate: true
@@ -0,0 +1,160 @@
1
+ id: consumer-retry-resilience
2
+ name: Retry and Resilience
3
+ description: Tests for client retry behavior on transient errors
4
+ category: consumer
5
+ tags:
6
+ - core
7
+ - retry
8
+ - resilience
9
+ - error-handling
10
+
11
+ tests:
12
+ - id: retry-on-500
13
+ name: Client retries on 500 Internal Server Error
14
+ description: Client should automatically retry when server returns 500
15
+ setup:
16
+ - action: create
17
+ as: streamPath
18
+ - action: append
19
+ path: ${streamPath}
20
+ data: "test-data"
21
+ operations:
22
+ # Inject a 500 error for the first request
23
+ - action: inject-error
24
+ path: ${streamPath}
25
+ status: 500
26
+ count: 1
27
+ # Client should retry and eventually succeed
28
+ - action: read
29
+ path: ${streamPath}
30
+ expect:
31
+ data: "test-data"
32
+ cleanup:
33
+ - action: clear-errors
34
+
35
+ - id: retry-on-503
36
+ name: Client retries on 503 Service Unavailable
37
+ description: Client should automatically retry when server returns 503
38
+ setup:
39
+ - action: create
40
+ as: streamPath
41
+ - action: append
42
+ path: ${streamPath}
43
+ data: "test-data"
44
+ operations:
45
+ # Inject a 503 error for the first request
46
+ - action: inject-error
47
+ path: ${streamPath}
48
+ status: 503
49
+ count: 1
50
+ # Client should retry and eventually succeed
51
+ - action: read
52
+ path: ${streamPath}
53
+ expect:
54
+ data: "test-data"
55
+ cleanup:
56
+ - action: clear-errors
57
+
58
+ - id: retry-on-429
59
+ name: Client retries on 429 Too Many Requests
60
+ description: Client should retry after rate limiting with backoff
61
+ setup:
62
+ - action: create
63
+ as: streamPath
64
+ - action: append
65
+ path: ${streamPath}
66
+ data: "test-data"
67
+ operations:
68
+ # Inject a 429 error with Retry-After header
69
+ - action: inject-error
70
+ path: ${streamPath}
71
+ status: 429
72
+ count: 1
73
+ retryAfter: 1
74
+ # Client should wait and retry, eventually succeeding
75
+ - action: read
76
+ path: ${streamPath}
77
+ expect:
78
+ data: "test-data"
79
+ cleanup:
80
+ - action: clear-errors
81
+
82
+ - id: retry-multiple-failures
83
+ name: Client retries through multiple transient failures
84
+ description: Client should retry through several consecutive 500 errors
85
+ setup:
86
+ - action: create
87
+ as: streamPath
88
+ - action: append
89
+ path: ${streamPath}
90
+ data: "persisted-data"
91
+ operations:
92
+ # Inject 2 consecutive 500 errors
93
+ - action: inject-error
94
+ path: ${streamPath}
95
+ status: 500
96
+ count: 2
97
+ # Client should retry multiple times and eventually succeed
98
+ - action: read
99
+ path: ${streamPath}
100
+ expect:
101
+ data: "persisted-data"
102
+ cleanup:
103
+ - action: clear-errors
104
+
105
+ - id: no-retry-on-404
106
+ name: Client does not retry on 404 Not Found
107
+ description: 404 is a permanent error and should not be retried
108
+ operations:
109
+ # Try to read from non-existent stream (will get 404)
110
+ - action: read
111
+ path: /non-existent-stream-${randomUUID}
112
+ expect:
113
+ status: 404
114
+
115
+ - id: no-retry-on-400
116
+ name: Client does not retry on 400 Bad Request
117
+ description: 400 is a client error and should not be retried
118
+ setup:
119
+ - action: create
120
+ as: streamPath
121
+ - action: append
122
+ path: ${streamPath}
123
+ data: "data"
124
+ operations:
125
+ # Inject a 400 error
126
+ - action: inject-error
127
+ path: ${streamPath}
128
+ status: 400
129
+ count: 1
130
+ # Client should not retry, should fail immediately
131
+ - action: read
132
+ path: ${streamPath}
133
+ expect:
134
+ status: 400
135
+ cleanup:
136
+ - action: clear-errors
137
+
138
+ - id: append-retry-on-500
139
+ name: Append retries on 500 Internal Server Error
140
+ description: Append should automatically retry when server returns 500
141
+ setup:
142
+ - action: create
143
+ as: streamPath
144
+ operations:
145
+ # Inject a 500 error for the first append
146
+ - action: inject-error
147
+ path: ${streamPath}
148
+ status: 500
149
+ count: 1
150
+ # Client should retry and eventually succeed
151
+ - action: append
152
+ path: ${streamPath}
153
+ data: "retry-data"
154
+ # Verify data was appended
155
+ - action: read
156
+ path: ${streamPath}
157
+ expect:
158
+ data: "retry-data"
159
+ cleanup:
160
+ - action: clear-errors