@durable-streams/client-conformance-tests 0.1.8 → 0.1.9
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.
- package/dist/adapters/typescript-adapter.cjs +72 -22
- package/dist/adapters/typescript-adapter.js +72 -22
- package/dist/{benchmark-runner-CrE6JkbX.js → benchmark-runner-81waaCzs.js} +89 -9
- package/dist/{benchmark-runner-Db4he452.cjs → benchmark-runner-DliEfq9k.cjs} +93 -8
- package/dist/cli.cjs +41 -5
- package/dist/cli.js +41 -5
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +50 -3
- package/dist/index.d.ts +50 -3
- package/dist/index.js +2 -2
- package/dist/{protocol-qb83AeUH.js → protocol-1p0soayz.js} +2 -1
- package/dist/{protocol-D37G3c4e.d.cts → protocol-BxZTqJmO.d.cts} +67 -5
- package/dist/{protocol-XeAOKBD-.cjs → protocol-IioVPNaP.cjs} +2 -1
- package/dist/{protocol-Mcbiq3nQ.d.ts → protocol-JuFzdV5x.d.ts} +67 -5
- package/dist/protocol.cjs +1 -1
- package/dist/protocol.d.cts +2 -2
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.js +1 -1
- package/package.json +8 -3
- package/src/adapters/typescript-adapter.ts +110 -32
- package/src/benchmark-runner.ts +75 -1
- package/src/benchmark-scenarios.ts +4 -4
- package/src/cli.ts +46 -5
- package/src/protocol.ts +75 -2
- package/src/runner.ts +72 -1
- package/src/test-cases.ts +55 -0
- package/test-cases/consumer/error-context.yaml +67 -0
- package/test-cases/consumer/json-parsing-errors.yaml +115 -0
- package/test-cases/consumer/read-auto.yaml +155 -0
- package/test-cases/consumer/read-sse.yaml +24 -0
- package/test-cases/consumer/retry-resilience.yaml +28 -0
- package/test-cases/consumer/sse-parsing-errors.yaml +121 -0
- package/test-cases/producer/error-context.yaml +72 -0
- package/test-cases/producer/idempotent-json-batching.yaml +40 -0
- package/test-cases/validation/input-validation.yaml +192 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
id: consumer-auto
|
|
2
|
+
name: Auto Live Mode
|
|
3
|
+
description: Tests for auto live mode that catches up first then transitions to live tailing
|
|
4
|
+
category: consumer
|
|
5
|
+
tags:
|
|
6
|
+
- core
|
|
7
|
+
- read
|
|
8
|
+
- auto
|
|
9
|
+
- live
|
|
10
|
+
requires:
|
|
11
|
+
- auto
|
|
12
|
+
|
|
13
|
+
tests:
|
|
14
|
+
- id: auto-catches-up-with-existing-data
|
|
15
|
+
name: Auto mode catches up with existing data first
|
|
16
|
+
description: Auto mode should read all existing data before entering live mode
|
|
17
|
+
setup:
|
|
18
|
+
- action: create
|
|
19
|
+
as: streamPath
|
|
20
|
+
- action: append
|
|
21
|
+
path: ${streamPath}
|
|
22
|
+
data: "data1"
|
|
23
|
+
- action: append
|
|
24
|
+
path: ${streamPath}
|
|
25
|
+
data: "data2"
|
|
26
|
+
- action: append
|
|
27
|
+
path: ${streamPath}
|
|
28
|
+
data: "data3"
|
|
29
|
+
operations:
|
|
30
|
+
- action: read
|
|
31
|
+
path: ${streamPath}
|
|
32
|
+
live: true
|
|
33
|
+
timeoutMs: 2000
|
|
34
|
+
waitForUpToDate: true
|
|
35
|
+
expect:
|
|
36
|
+
data: "data1data2data3"
|
|
37
|
+
upToDate: true
|
|
38
|
+
|
|
39
|
+
- id: auto-waits-for-new-data
|
|
40
|
+
name: Auto mode waits for new data after catching up
|
|
41
|
+
description: After catching up, auto mode should wait for new data
|
|
42
|
+
setup:
|
|
43
|
+
- action: create
|
|
44
|
+
as: streamPath
|
|
45
|
+
- action: append
|
|
46
|
+
path: ${streamPath}
|
|
47
|
+
data: "initial"
|
|
48
|
+
expect:
|
|
49
|
+
storeOffsetAs: initialOffset
|
|
50
|
+
operations:
|
|
51
|
+
# Start auto read in background (will catch up then wait)
|
|
52
|
+
- action: read
|
|
53
|
+
path: ${streamPath}
|
|
54
|
+
offset: ${initialOffset}
|
|
55
|
+
live: true
|
|
56
|
+
timeoutMs: 10000
|
|
57
|
+
background: true
|
|
58
|
+
as: readOp
|
|
59
|
+
# Wait for the read to start and catch up
|
|
60
|
+
- action: wait
|
|
61
|
+
ms: 300
|
|
62
|
+
# Append new data
|
|
63
|
+
- action: server-append
|
|
64
|
+
path: ${streamPath}
|
|
65
|
+
data: "new-data"
|
|
66
|
+
# Wait for the background read to receive the data
|
|
67
|
+
- action: await
|
|
68
|
+
ref: readOp
|
|
69
|
+
expect:
|
|
70
|
+
dataContains: "new-data"
|
|
71
|
+
|
|
72
|
+
- id: auto-returns-immediately-on-existing-data
|
|
73
|
+
name: Auto mode returns immediately with existing data
|
|
74
|
+
description: Auto mode should not delay when there's data to return
|
|
75
|
+
setup:
|
|
76
|
+
- action: create
|
|
77
|
+
as: streamPath
|
|
78
|
+
- action: append
|
|
79
|
+
path: ${streamPath}
|
|
80
|
+
data: "existing"
|
|
81
|
+
operations:
|
|
82
|
+
- action: read
|
|
83
|
+
path: ${streamPath}
|
|
84
|
+
live: true
|
|
85
|
+
timeoutMs: 1000
|
|
86
|
+
waitForUpToDate: true
|
|
87
|
+
expect:
|
|
88
|
+
data: "existing"
|
|
89
|
+
upToDate: true
|
|
90
|
+
|
|
91
|
+
- id: auto-resumes-from-offset
|
|
92
|
+
name: Auto mode resumes from provided offset
|
|
93
|
+
description: Auto mode should start from the given offset, not beginning
|
|
94
|
+
setup:
|
|
95
|
+
- action: create
|
|
96
|
+
as: streamPath
|
|
97
|
+
- action: append
|
|
98
|
+
path: ${streamPath}
|
|
99
|
+
data: "old-data"
|
|
100
|
+
expect:
|
|
101
|
+
storeOffsetAs: offset
|
|
102
|
+
- action: append
|
|
103
|
+
path: ${streamPath}
|
|
104
|
+
data: "new-data"
|
|
105
|
+
operations:
|
|
106
|
+
- action: read
|
|
107
|
+
path: ${streamPath}
|
|
108
|
+
offset: ${offset}
|
|
109
|
+
live: true
|
|
110
|
+
timeoutMs: 1000
|
|
111
|
+
waitForUpToDate: true
|
|
112
|
+
expect:
|
|
113
|
+
data: "new-data"
|
|
114
|
+
upToDate: true
|
|
115
|
+
|
|
116
|
+
- id: auto-empty-stream
|
|
117
|
+
name: Auto mode handles empty stream
|
|
118
|
+
description: Auto mode should handle empty streams gracefully
|
|
119
|
+
setup:
|
|
120
|
+
- action: create
|
|
121
|
+
as: streamPath
|
|
122
|
+
operations:
|
|
123
|
+
- action: read
|
|
124
|
+
path: ${streamPath}
|
|
125
|
+
live: true
|
|
126
|
+
timeoutMs: 500
|
|
127
|
+
waitForUpToDate: true
|
|
128
|
+
expect:
|
|
129
|
+
chunkCount: 0
|
|
130
|
+
upToDate: true
|
|
131
|
+
|
|
132
|
+
- id: auto-multiple-chunks
|
|
133
|
+
name: Auto mode collects multiple chunks
|
|
134
|
+
description: Auto mode should receive multiple data chunks
|
|
135
|
+
setup:
|
|
136
|
+
- action: create
|
|
137
|
+
as: streamPath
|
|
138
|
+
operations:
|
|
139
|
+
- action: append
|
|
140
|
+
path: ${streamPath}
|
|
141
|
+
data: "chunk1"
|
|
142
|
+
- action: append
|
|
143
|
+
path: ${streamPath}
|
|
144
|
+
data: "chunk2"
|
|
145
|
+
- action: append
|
|
146
|
+
path: ${streamPath}
|
|
147
|
+
data: "chunk3"
|
|
148
|
+
- action: read
|
|
149
|
+
path: ${streamPath}
|
|
150
|
+
live: true
|
|
151
|
+
timeoutMs: 2000
|
|
152
|
+
waitForUpToDate: true
|
|
153
|
+
expect:
|
|
154
|
+
data: "chunk1chunk2chunk3"
|
|
155
|
+
upToDate: true
|
|
@@ -230,3 +230,27 @@ tests:
|
|
|
230
230
|
expect:
|
|
231
231
|
data: "a\u0085b\u2028c\u2029d"
|
|
232
232
|
minChunks: 1
|
|
233
|
+
|
|
234
|
+
- id: sse-strips-single-leading-space
|
|
235
|
+
name: SSE strips only single leading space
|
|
236
|
+
description: |
|
|
237
|
+
Per EventSource spec section 9.2.4: "If value starts with a
|
|
238
|
+
U+0020 SPACE character, remove it from value."
|
|
239
|
+
Only ONE space should be removed, preserving any additional spaces.
|
|
240
|
+
This prevents parsers from using trim() or similar which would
|
|
241
|
+
incorrectly strip all leading/trailing whitespace.
|
|
242
|
+
setup:
|
|
243
|
+
- action: create
|
|
244
|
+
as: streamPath
|
|
245
|
+
contentType: text/plain
|
|
246
|
+
- action: append
|
|
247
|
+
path: ${streamPath}
|
|
248
|
+
data: " two leading spaces"
|
|
249
|
+
operations:
|
|
250
|
+
- action: read
|
|
251
|
+
path: ${streamPath}
|
|
252
|
+
live: sse
|
|
253
|
+
waitForUpToDate: true
|
|
254
|
+
expect:
|
|
255
|
+
data: " two leading spaces"
|
|
256
|
+
minChunks: 1
|
|
@@ -158,3 +158,31 @@ tests:
|
|
|
158
158
|
data: "retry-data"
|
|
159
159
|
cleanup:
|
|
160
160
|
- action: clear-errors
|
|
161
|
+
|
|
162
|
+
- id: retry-exhaustion-fails
|
|
163
|
+
name: Client fails after retry limit exhaustion
|
|
164
|
+
description: |
|
|
165
|
+
Client should eventually fail when transient errors persist beyond
|
|
166
|
+
the retry limit. This prevents infinite retry loops and ensures
|
|
167
|
+
clients surface errors to callers rather than hanging forever.
|
|
168
|
+
setup:
|
|
169
|
+
- action: create
|
|
170
|
+
as: streamPath
|
|
171
|
+
- action: append
|
|
172
|
+
path: ${streamPath}
|
|
173
|
+
data: "test-data"
|
|
174
|
+
operations:
|
|
175
|
+
# Inject many 500 errors - more than any reasonable retry limit
|
|
176
|
+
- action: inject-error
|
|
177
|
+
path: ${streamPath}
|
|
178
|
+
status: 500
|
|
179
|
+
count: 20
|
|
180
|
+
# Client should eventually give up and return an error
|
|
181
|
+
- action: read
|
|
182
|
+
path: ${streamPath}
|
|
183
|
+
expect:
|
|
184
|
+
error: true
|
|
185
|
+
# The error could be 500 (last failed attempt) or a client-specific
|
|
186
|
+
# retry exhaustion error - either is acceptable
|
|
187
|
+
cleanup:
|
|
188
|
+
- action: clear-errors
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
id: consumer-sse-parsing-errors
|
|
2
|
+
name: SSE Parsing Error Handling
|
|
3
|
+
description: Tests that clients properly handle malformed SSE events instead of silently failing
|
|
4
|
+
category: consumer
|
|
5
|
+
requires:
|
|
6
|
+
- sse
|
|
7
|
+
tags:
|
|
8
|
+
- sse
|
|
9
|
+
- error-handling
|
|
10
|
+
- resilience
|
|
11
|
+
- fault-injection
|
|
12
|
+
|
|
13
|
+
tests:
|
|
14
|
+
- id: unknown-sse-event-type
|
|
15
|
+
name: Client gracefully handles unknown SSE event type
|
|
16
|
+
description: Client should ignore unknown event types and continue processing
|
|
17
|
+
setup:
|
|
18
|
+
- action: create
|
|
19
|
+
as: streamPath
|
|
20
|
+
- action: append
|
|
21
|
+
path: ${streamPath}
|
|
22
|
+
data: "test-data"
|
|
23
|
+
operations:
|
|
24
|
+
# Inject an unknown SSE event type before the stream starts
|
|
25
|
+
- action: inject-error
|
|
26
|
+
path: ${streamPath}
|
|
27
|
+
injectSseEvent:
|
|
28
|
+
eventType: "unknown-future-type"
|
|
29
|
+
data: '{"some":"future-data"}'
|
|
30
|
+
count: 1
|
|
31
|
+
# Client should skip the unknown event and still read the actual data
|
|
32
|
+
- action: read
|
|
33
|
+
path: ${streamPath}
|
|
34
|
+
live: sse
|
|
35
|
+
maxChunks: 1
|
|
36
|
+
expect:
|
|
37
|
+
data: "test-data"
|
|
38
|
+
cleanup:
|
|
39
|
+
- action: clear-errors
|
|
40
|
+
|
|
41
|
+
- id: malformed-control-event-json
|
|
42
|
+
name: Client throws on malformed control event JSON
|
|
43
|
+
description: Control events contain critical offset data - malformed JSON should throw
|
|
44
|
+
setup:
|
|
45
|
+
- action: create
|
|
46
|
+
as: streamPath
|
|
47
|
+
- action: append
|
|
48
|
+
path: ${streamPath}
|
|
49
|
+
data: "data-before-error"
|
|
50
|
+
operations:
|
|
51
|
+
# Inject a control event with invalid JSON
|
|
52
|
+
- action: inject-error
|
|
53
|
+
path: ${streamPath}
|
|
54
|
+
injectSseEvent:
|
|
55
|
+
eventType: "control"
|
|
56
|
+
data: "{invalid json here"
|
|
57
|
+
count: 1
|
|
58
|
+
# Client should throw a parse error
|
|
59
|
+
- action: read
|
|
60
|
+
path: ${streamPath}
|
|
61
|
+
live: sse
|
|
62
|
+
maxChunks: 1
|
|
63
|
+
expect:
|
|
64
|
+
errorCode: PARSE_ERROR
|
|
65
|
+
cleanup:
|
|
66
|
+
- action: clear-errors
|
|
67
|
+
|
|
68
|
+
- id: empty-control-event-data
|
|
69
|
+
name: Client handles empty control event data
|
|
70
|
+
description: Empty control event should be handled gracefully
|
|
71
|
+
setup:
|
|
72
|
+
- action: create
|
|
73
|
+
as: streamPath
|
|
74
|
+
- action: append
|
|
75
|
+
path: ${streamPath}
|
|
76
|
+
data: "actual-data"
|
|
77
|
+
operations:
|
|
78
|
+
# Inject a control event with empty data
|
|
79
|
+
- action: inject-error
|
|
80
|
+
path: ${streamPath}
|
|
81
|
+
injectSseEvent:
|
|
82
|
+
eventType: "control"
|
|
83
|
+
data: ""
|
|
84
|
+
count: 1
|
|
85
|
+
# Client should throw because empty is not valid JSON
|
|
86
|
+
- action: read
|
|
87
|
+
path: ${streamPath}
|
|
88
|
+
live: sse
|
|
89
|
+
maxChunks: 1
|
|
90
|
+
expect:
|
|
91
|
+
errorCode: PARSE_ERROR
|
|
92
|
+
cleanup:
|
|
93
|
+
- action: clear-errors
|
|
94
|
+
|
|
95
|
+
- id: multiple-unknown-events
|
|
96
|
+
name: Client handles multiple unknown event types
|
|
97
|
+
description: Client should skip all unknown events and process valid ones
|
|
98
|
+
setup:
|
|
99
|
+
- action: create
|
|
100
|
+
as: streamPath
|
|
101
|
+
- action: append
|
|
102
|
+
path: ${streamPath}
|
|
103
|
+
data: "expected-data"
|
|
104
|
+
operations:
|
|
105
|
+
# Inject first unknown event
|
|
106
|
+
- action: inject-error
|
|
107
|
+
path: ${streamPath}
|
|
108
|
+
injectSseEvent:
|
|
109
|
+
eventType: "future-event-v2"
|
|
110
|
+
data: '{"version":2}'
|
|
111
|
+
count: 1
|
|
112
|
+
# Note: Only one injection per fault, so this tests single unknown event
|
|
113
|
+
# Client should still read the actual data
|
|
114
|
+
- action: read
|
|
115
|
+
path: ${streamPath}
|
|
116
|
+
live: sse
|
|
117
|
+
maxChunks: 1
|
|
118
|
+
expect:
|
|
119
|
+
data: "expected-data"
|
|
120
|
+
cleanup:
|
|
121
|
+
- action: clear-errors
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
id: producer-error-context
|
|
2
|
+
name: Producer Error Context
|
|
3
|
+
description: |
|
|
4
|
+
Tests that producer error messages include helpful context.
|
|
5
|
+
Ensures errors provide enough information for debugging.
|
|
6
|
+
Note: These tests validate server-level responses (status codes).
|
|
7
|
+
Client-level error message validation requires idempotent-append tests.
|
|
8
|
+
category: producer
|
|
9
|
+
tags:
|
|
10
|
+
- errors
|
|
11
|
+
- context
|
|
12
|
+
- messages
|
|
13
|
+
|
|
14
|
+
tests:
|
|
15
|
+
- id: sequence-conflict-returns-expected-seq
|
|
16
|
+
name: Sequence conflict returns expected sequence info
|
|
17
|
+
description: 409 sequence conflict should return expected/received seq headers
|
|
18
|
+
setup:
|
|
19
|
+
- action: create
|
|
20
|
+
as: streamPath
|
|
21
|
+
contentType: text/plain
|
|
22
|
+
operations:
|
|
23
|
+
# First append succeeds
|
|
24
|
+
- action: server-append
|
|
25
|
+
path: ${streamPath}
|
|
26
|
+
data: "msg0"
|
|
27
|
+
producerId: error-context-producer
|
|
28
|
+
producerEpoch: 0
|
|
29
|
+
producerSeq: 0
|
|
30
|
+
expect:
|
|
31
|
+
status: 200
|
|
32
|
+
# Skip seq=1, try seq=2 -> 409 gap
|
|
33
|
+
- action: server-append
|
|
34
|
+
path: ${streamPath}
|
|
35
|
+
data: "msg2"
|
|
36
|
+
producerId: error-context-producer
|
|
37
|
+
producerEpoch: 0
|
|
38
|
+
producerSeq: 2
|
|
39
|
+
expect:
|
|
40
|
+
status: 409
|
|
41
|
+
# Server returns expected/received seq in headers
|
|
42
|
+
producerExpectedSeq: 1
|
|
43
|
+
producerReceivedSeq: 2
|
|
44
|
+
|
|
45
|
+
- id: stale-epoch-returns-current-epoch
|
|
46
|
+
name: Stale epoch returns current epoch info
|
|
47
|
+
description: 403 stale epoch should return current epoch in header
|
|
48
|
+
setup:
|
|
49
|
+
- action: create
|
|
50
|
+
as: streamPath
|
|
51
|
+
contentType: text/plain
|
|
52
|
+
operations:
|
|
53
|
+
# Establish epoch 1
|
|
54
|
+
- action: server-append
|
|
55
|
+
path: ${streamPath}
|
|
56
|
+
data: "epoch1"
|
|
57
|
+
producerId: epoch-context-producer
|
|
58
|
+
producerEpoch: 1
|
|
59
|
+
producerSeq: 0
|
|
60
|
+
expect:
|
|
61
|
+
status: 200
|
|
62
|
+
# Try with stale epoch 0
|
|
63
|
+
- action: server-append
|
|
64
|
+
path: ${streamPath}
|
|
65
|
+
data: "stale"
|
|
66
|
+
producerId: epoch-context-producer
|
|
67
|
+
producerEpoch: 0
|
|
68
|
+
producerSeq: 0
|
|
69
|
+
expect:
|
|
70
|
+
status: 403
|
|
71
|
+
# Server returns current epoch in header
|
|
72
|
+
producerEpoch: 1
|
|
@@ -132,3 +132,43 @@ tests:
|
|
|
132
132
|
expect:
|
|
133
133
|
data: "Hello World"
|
|
134
134
|
upToDate: true
|
|
135
|
+
|
|
136
|
+
- id: client-deduplication-works
|
|
137
|
+
name: Client IdempotentProducer correctly deduplicates
|
|
138
|
+
description: |
|
|
139
|
+
This test verifies that the CLIENT's IdempotentProducer implementation
|
|
140
|
+
correctly sends Producer-Id/Epoch/Seq headers so server-side deduplication
|
|
141
|
+
actually works. Without correct headers, retries would create duplicates.
|
|
142
|
+
setup:
|
|
143
|
+
- action: create
|
|
144
|
+
as: streamPath
|
|
145
|
+
contentType: application/json
|
|
146
|
+
operations:
|
|
147
|
+
# Send a batch of items - this creates epoch=0, seq=0
|
|
148
|
+
- action: idempotent-append-batch
|
|
149
|
+
path: ${streamPath}
|
|
150
|
+
producerId: dedup-test-producer
|
|
151
|
+
epoch: 0
|
|
152
|
+
items:
|
|
153
|
+
- data: '{"msg": "first"}'
|
|
154
|
+
- data: '{"msg": "second"}'
|
|
155
|
+
expect:
|
|
156
|
+
allSucceed: true
|
|
157
|
+
# Send the SAME batch again with a NEW producer instance at epoch=0
|
|
158
|
+
# If client sends correct headers, server should deduplicate
|
|
159
|
+
# If client sends wrong headers, this would create duplicate data
|
|
160
|
+
- action: idempotent-append-batch
|
|
161
|
+
path: ${streamPath}
|
|
162
|
+
producerId: dedup-test-producer
|
|
163
|
+
epoch: 0
|
|
164
|
+
items:
|
|
165
|
+
- data: '{"msg": "first"}'
|
|
166
|
+
- data: '{"msg": "second"}'
|
|
167
|
+
expect:
|
|
168
|
+
allSucceed: true
|
|
169
|
+
# Verify NO duplicates - should only have 2 messages, not 4
|
|
170
|
+
- action: read
|
|
171
|
+
path: ${streamPath}
|
|
172
|
+
expect:
|
|
173
|
+
data: '[{"msg":"first"},{"msg":"second"}]'
|
|
174
|
+
upToDate: true
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
id: input-validation
|
|
2
|
+
name: Client Input Validation
|
|
3
|
+
description: Tests for client-side input parameter validation
|
|
4
|
+
category: lifecycle
|
|
5
|
+
tags:
|
|
6
|
+
- validation
|
|
7
|
+
- core
|
|
8
|
+
|
|
9
|
+
tests:
|
|
10
|
+
# RetryOptions validation tests (PHP-specific - TS uses BackoffOptions)
|
|
11
|
+
- id: retry-options-valid
|
|
12
|
+
name: Valid RetryOptions passes validation
|
|
13
|
+
description: Default retry options should be accepted
|
|
14
|
+
requires:
|
|
15
|
+
- retryOptions
|
|
16
|
+
operations:
|
|
17
|
+
- action: validate
|
|
18
|
+
target:
|
|
19
|
+
target: retry-options
|
|
20
|
+
maxRetries: 3
|
|
21
|
+
initialDelayMs: 100
|
|
22
|
+
maxDelayMs: 5000
|
|
23
|
+
multiplier: 2.0
|
|
24
|
+
expect:
|
|
25
|
+
valid: true
|
|
26
|
+
|
|
27
|
+
- id: retry-options-negative-max-retries
|
|
28
|
+
name: Negative maxRetries rejected
|
|
29
|
+
description: Client should reject negative maxRetries
|
|
30
|
+
requires:
|
|
31
|
+
- retryOptions
|
|
32
|
+
operations:
|
|
33
|
+
- action: validate
|
|
34
|
+
target:
|
|
35
|
+
target: retry-options
|
|
36
|
+
maxRetries: -1
|
|
37
|
+
expect:
|
|
38
|
+
valid: false
|
|
39
|
+
errorCode: INVALID_ARGUMENT
|
|
40
|
+
errorContains: maxRetries
|
|
41
|
+
|
|
42
|
+
- id: retry-options-zero-initial-delay
|
|
43
|
+
name: Zero initialDelayMs rejected
|
|
44
|
+
description: Client should reject zero or negative initialDelayMs
|
|
45
|
+
requires:
|
|
46
|
+
- retryOptions
|
|
47
|
+
operations:
|
|
48
|
+
- action: validate
|
|
49
|
+
target:
|
|
50
|
+
target: retry-options
|
|
51
|
+
initialDelayMs: 0
|
|
52
|
+
expect:
|
|
53
|
+
valid: false
|
|
54
|
+
errorCode: INVALID_ARGUMENT
|
|
55
|
+
errorContains: initialDelayMs
|
|
56
|
+
|
|
57
|
+
- id: retry-options-negative-initial-delay
|
|
58
|
+
name: Negative initialDelayMs rejected
|
|
59
|
+
description: Client should reject negative initialDelayMs
|
|
60
|
+
requires:
|
|
61
|
+
- retryOptions
|
|
62
|
+
operations:
|
|
63
|
+
- action: validate
|
|
64
|
+
target:
|
|
65
|
+
target: retry-options
|
|
66
|
+
initialDelayMs: -100
|
|
67
|
+
expect:
|
|
68
|
+
valid: false
|
|
69
|
+
errorCode: INVALID_ARGUMENT
|
|
70
|
+
errorContains: initialDelayMs
|
|
71
|
+
|
|
72
|
+
- id: retry-options-max-less-than-initial
|
|
73
|
+
name: maxDelayMs less than initialDelayMs rejected
|
|
74
|
+
description: Client should reject maxDelayMs < initialDelayMs
|
|
75
|
+
requires:
|
|
76
|
+
- retryOptions
|
|
77
|
+
operations:
|
|
78
|
+
- action: validate
|
|
79
|
+
target:
|
|
80
|
+
target: retry-options
|
|
81
|
+
initialDelayMs: 1000
|
|
82
|
+
maxDelayMs: 500
|
|
83
|
+
expect:
|
|
84
|
+
valid: false
|
|
85
|
+
errorCode: INVALID_ARGUMENT
|
|
86
|
+
errorContains: maxDelayMs
|
|
87
|
+
|
|
88
|
+
- id: retry-options-multiplier-less-than-one
|
|
89
|
+
name: Multiplier less than 1.0 rejected
|
|
90
|
+
description: Client should reject multiplier < 1.0
|
|
91
|
+
requires:
|
|
92
|
+
- retryOptions
|
|
93
|
+
operations:
|
|
94
|
+
- action: validate
|
|
95
|
+
target:
|
|
96
|
+
target: retry-options
|
|
97
|
+
multiplier: 0.5
|
|
98
|
+
expect:
|
|
99
|
+
valid: false
|
|
100
|
+
errorCode: INVALID_ARGUMENT
|
|
101
|
+
errorContains: multiplier
|
|
102
|
+
|
|
103
|
+
# IdempotentProducer validation tests (shared across clients)
|
|
104
|
+
- id: producer-valid
|
|
105
|
+
name: Valid IdempotentProducer passes validation
|
|
106
|
+
description: Default producer options should be accepted
|
|
107
|
+
operations:
|
|
108
|
+
- action: validate
|
|
109
|
+
target:
|
|
110
|
+
target: idempotent-producer
|
|
111
|
+
producerId: test-producer
|
|
112
|
+
epoch: 0
|
|
113
|
+
maxBatchBytes: 1048576
|
|
114
|
+
expect:
|
|
115
|
+
valid: true
|
|
116
|
+
|
|
117
|
+
- id: producer-negative-epoch
|
|
118
|
+
name: Negative epoch rejected
|
|
119
|
+
description: Client should reject negative epoch
|
|
120
|
+
operations:
|
|
121
|
+
- action: validate
|
|
122
|
+
target:
|
|
123
|
+
target: idempotent-producer
|
|
124
|
+
producerId: test-producer
|
|
125
|
+
epoch: -1
|
|
126
|
+
expect:
|
|
127
|
+
valid: false
|
|
128
|
+
errorCode: INVALID_ARGUMENT
|
|
129
|
+
errorContains: epoch
|
|
130
|
+
|
|
131
|
+
- id: producer-zero-max-batch-bytes
|
|
132
|
+
name: Zero maxBatchBytes rejected
|
|
133
|
+
description: Client should reject zero maxBatchBytes (Go treats 0 as default)
|
|
134
|
+
requires:
|
|
135
|
+
- strictZeroValidation
|
|
136
|
+
operations:
|
|
137
|
+
- action: validate
|
|
138
|
+
target:
|
|
139
|
+
target: idempotent-producer
|
|
140
|
+
producerId: test-producer
|
|
141
|
+
maxBatchBytes: 0
|
|
142
|
+
expect:
|
|
143
|
+
valid: false
|
|
144
|
+
errorCode: INVALID_ARGUMENT
|
|
145
|
+
errorContains: maxBatchBytes
|
|
146
|
+
|
|
147
|
+
- id: producer-negative-max-batch-bytes
|
|
148
|
+
name: Negative maxBatchBytes rejected
|
|
149
|
+
description: Client should reject negative maxBatchBytes
|
|
150
|
+
operations:
|
|
151
|
+
- action: validate
|
|
152
|
+
target:
|
|
153
|
+
target: idempotent-producer
|
|
154
|
+
producerId: test-producer
|
|
155
|
+
maxBatchBytes: -1
|
|
156
|
+
expect:
|
|
157
|
+
valid: false
|
|
158
|
+
errorCode: INVALID_ARGUMENT
|
|
159
|
+
errorContains: maxBatchBytes
|
|
160
|
+
|
|
161
|
+
# maxBatchItems validation (PHP-specific - TS doesn't have this option)
|
|
162
|
+
- id: producer-zero-max-batch-items
|
|
163
|
+
name: Zero maxBatchItems rejected
|
|
164
|
+
description: Client should reject zero maxBatchItems
|
|
165
|
+
requires:
|
|
166
|
+
- batchItems
|
|
167
|
+
operations:
|
|
168
|
+
- action: validate
|
|
169
|
+
target:
|
|
170
|
+
target: idempotent-producer
|
|
171
|
+
producerId: test-producer
|
|
172
|
+
maxBatchItems: 0
|
|
173
|
+
expect:
|
|
174
|
+
valid: false
|
|
175
|
+
errorCode: INVALID_ARGUMENT
|
|
176
|
+
errorContains: maxBatchItems
|
|
177
|
+
|
|
178
|
+
- id: producer-negative-max-batch-items
|
|
179
|
+
name: Negative maxBatchItems rejected
|
|
180
|
+
description: Client should reject negative maxBatchItems
|
|
181
|
+
requires:
|
|
182
|
+
- batchItems
|
|
183
|
+
operations:
|
|
184
|
+
- action: validate
|
|
185
|
+
target:
|
|
186
|
+
target: idempotent-producer
|
|
187
|
+
producerId: test-producer
|
|
188
|
+
maxBatchItems: -1
|
|
189
|
+
expect:
|
|
190
|
+
valid: false
|
|
191
|
+
errorCode: INVALID_ARGUMENT
|
|
192
|
+
errorContains: maxBatchItems
|