@durable-streams/client-conformance-tests 0.1.5 → 0.1.7
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 +75 -2
- package/dist/adapters/typescript-adapter.js +76 -3
- package/dist/{benchmark-runner-C_Yghc8f.js → benchmark-runner-CrE6JkbX.js} +106 -12
- package/dist/{benchmark-runner-CLAR9oLd.cjs → benchmark-runner-Db4he452.cjs} +107 -12
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +126 -11
- package/dist/index.d.ts +126 -11
- package/dist/index.js +1 -1
- package/dist/{protocol-3cf94Xyb.d.cts → protocol-D37G3c4e.d.cts} +80 -4
- package/dist/{protocol-DyEvTHPF.d.ts → protocol-Mcbiq3nQ.d.ts} +80 -4
- package/dist/protocol.d.cts +2 -2
- package/dist/protocol.d.ts +2 -2
- package/package.json +3 -3
- package/src/adapters/typescript-adapter.ts +127 -5
- package/src/protocol.ts +85 -1
- package/src/runner.ts +202 -17
- package/src/test-cases.ts +130 -8
- package/test-cases/consumer/error-handling.yaml +42 -0
- package/test-cases/consumer/fault-injection.yaml +202 -0
- package/test-cases/consumer/offset-handling.yaml +209 -0
- package/test-cases/producer/idempotent/autoclaim.yaml +214 -0
- package/test-cases/producer/idempotent/batching.yaml +98 -0
- package/test-cases/producer/idempotent/concurrent-requests.yaml +100 -0
- package/test-cases/producer/idempotent/epoch-management.yaml +333 -0
- package/test-cases/producer/idempotent/error-handling.yaml +194 -0
- package/test-cases/producer/idempotent/multi-producer.yaml +322 -0
- package/test-cases/producer/idempotent/sequence-validation.yaml +339 -0
- package/test-cases/producer/idempotent-json-batching.yaml +134 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
id: idempotent-error-handling
|
|
2
|
+
name: Idempotent Producer - Error Handling
|
|
3
|
+
description: |
|
|
4
|
+
Tests for producer header validation and error conditions.
|
|
5
|
+
Includes atomicity guarantees when appends fail.
|
|
6
|
+
category: producer
|
|
7
|
+
tags:
|
|
8
|
+
- idempotent
|
|
9
|
+
- validation
|
|
10
|
+
- errors
|
|
11
|
+
|
|
12
|
+
tests:
|
|
13
|
+
- id: partial-headers-rejected
|
|
14
|
+
name: Partial producer headers are rejected
|
|
15
|
+
description: All three producer headers must be provided together
|
|
16
|
+
setup:
|
|
17
|
+
- action: create
|
|
18
|
+
as: streamPath
|
|
19
|
+
contentType: text/plain
|
|
20
|
+
operations:
|
|
21
|
+
# Only Producer-Id
|
|
22
|
+
- action: server-append
|
|
23
|
+
path: ${streamPath}
|
|
24
|
+
data: "msg"
|
|
25
|
+
headers:
|
|
26
|
+
Producer-Id: test-producer
|
|
27
|
+
expect:
|
|
28
|
+
status: 400
|
|
29
|
+
# Missing Producer-Seq
|
|
30
|
+
- action: server-append
|
|
31
|
+
path: ${streamPath}
|
|
32
|
+
data: "msg"
|
|
33
|
+
headers:
|
|
34
|
+
Producer-Id: test-producer
|
|
35
|
+
Producer-Epoch: "0"
|
|
36
|
+
expect:
|
|
37
|
+
status: 400
|
|
38
|
+
|
|
39
|
+
- id: invalid-integer-formats-rejected
|
|
40
|
+
name: Invalid integer formats in producer headers are rejected
|
|
41
|
+
description: Server should reject non-integer values like "1abc" or "1e3"
|
|
42
|
+
setup:
|
|
43
|
+
- action: create
|
|
44
|
+
as: streamPath
|
|
45
|
+
contentType: text/plain
|
|
46
|
+
operations:
|
|
47
|
+
# Trailing junk in Producer-Seq
|
|
48
|
+
- action: server-append
|
|
49
|
+
path: ${streamPath}
|
|
50
|
+
data: "msg"
|
|
51
|
+
headers:
|
|
52
|
+
Producer-Id: test-producer
|
|
53
|
+
Producer-Epoch: "0"
|
|
54
|
+
Producer-Seq: "1abc"
|
|
55
|
+
expect:
|
|
56
|
+
status: 400
|
|
57
|
+
# Scientific notation
|
|
58
|
+
- action: server-append
|
|
59
|
+
path: ${streamPath}
|
|
60
|
+
data: "msg"
|
|
61
|
+
headers:
|
|
62
|
+
Producer-Id: test-producer
|
|
63
|
+
Producer-Epoch: "1e3"
|
|
64
|
+
Producer-Seq: "0"
|
|
65
|
+
expect:
|
|
66
|
+
status: 400
|
|
67
|
+
# Negative value
|
|
68
|
+
- action: server-append
|
|
69
|
+
path: ${streamPath}
|
|
70
|
+
data: "msg"
|
|
71
|
+
headers:
|
|
72
|
+
Producer-Id: test-producer
|
|
73
|
+
Producer-Epoch: "-1"
|
|
74
|
+
Producer-Seq: "0"
|
|
75
|
+
expect:
|
|
76
|
+
status: 400
|
|
77
|
+
|
|
78
|
+
- id: empty-producer-id-rejected
|
|
79
|
+
name: Empty producer ID is rejected
|
|
80
|
+
description: Producer-Id header with empty string should be rejected
|
|
81
|
+
setup:
|
|
82
|
+
- action: create
|
|
83
|
+
as: streamPath
|
|
84
|
+
contentType: text/plain
|
|
85
|
+
operations:
|
|
86
|
+
- action: server-append
|
|
87
|
+
path: ${streamPath}
|
|
88
|
+
data: "msg"
|
|
89
|
+
headers:
|
|
90
|
+
Producer-Id: ""
|
|
91
|
+
Producer-Epoch: "0"
|
|
92
|
+
Producer-Seq: "0"
|
|
93
|
+
expect:
|
|
94
|
+
status: 400
|
|
95
|
+
|
|
96
|
+
- id: failed-append-does-not-advance-producer-state
|
|
97
|
+
name: Failed append does not advance producer state (atomicity)
|
|
98
|
+
description: If append fails after producer validation (e.g., invalid JSON), producer seq should not advance
|
|
99
|
+
setup:
|
|
100
|
+
- action: create
|
|
101
|
+
as: streamPath
|
|
102
|
+
contentType: application/json
|
|
103
|
+
operations:
|
|
104
|
+
# First append succeeds
|
|
105
|
+
- action: server-append
|
|
106
|
+
path: ${streamPath}
|
|
107
|
+
data: '{"msg": 1}'
|
|
108
|
+
producerId: test-producer
|
|
109
|
+
producerEpoch: 0
|
|
110
|
+
producerSeq: 0
|
|
111
|
+
expect:
|
|
112
|
+
status: 200
|
|
113
|
+
# Second append with invalid JSON - should fail
|
|
114
|
+
- action: server-append
|
|
115
|
+
path: ${streamPath}
|
|
116
|
+
data: "not valid json"
|
|
117
|
+
producerId: test-producer
|
|
118
|
+
producerEpoch: 0
|
|
119
|
+
producerSeq: 1
|
|
120
|
+
expect:
|
|
121
|
+
status: 400
|
|
122
|
+
# Retry seq=1 with valid JSON - should succeed (not be rejected as duplicate or gap)
|
|
123
|
+
# If producer state was incorrectly advanced, this would fail
|
|
124
|
+
- action: server-append
|
|
125
|
+
path: ${streamPath}
|
|
126
|
+
data: '{"msg": 2}'
|
|
127
|
+
producerId: test-producer
|
|
128
|
+
producerEpoch: 0
|
|
129
|
+
producerSeq: 1
|
|
130
|
+
expect:
|
|
131
|
+
status: 200
|
|
132
|
+
# seq=2 should also work
|
|
133
|
+
- action: server-append
|
|
134
|
+
path: ${streamPath}
|
|
135
|
+
data: '{"msg": 3}'
|
|
136
|
+
producerId: test-producer
|
|
137
|
+
producerEpoch: 0
|
|
138
|
+
producerSeq: 2
|
|
139
|
+
expect:
|
|
140
|
+
status: 200
|
|
141
|
+
|
|
142
|
+
- id: gap-fill-resume
|
|
143
|
+
name: Gap can be filled and producer can resume
|
|
144
|
+
description: |
|
|
145
|
+
After a 409 gap error, the producer should be able to fill the gap
|
|
146
|
+
and then continue. Some implementations accidentally "lock out" a
|
|
147
|
+
sequence once a gap was observed, or mutate state on the 409 path.
|
|
148
|
+
setup:
|
|
149
|
+
- action: create
|
|
150
|
+
as: streamPath
|
|
151
|
+
contentType: text/plain
|
|
152
|
+
operations:
|
|
153
|
+
# seq=0 succeeds
|
|
154
|
+
- action: server-append
|
|
155
|
+
path: ${streamPath}
|
|
156
|
+
data: "msg0"
|
|
157
|
+
producerId: test-producer
|
|
158
|
+
producerEpoch: 0
|
|
159
|
+
producerSeq: 0
|
|
160
|
+
expect:
|
|
161
|
+
status: 200
|
|
162
|
+
# seq=2 creates a gap -> 409
|
|
163
|
+
- action: server-append
|
|
164
|
+
path: ${streamPath}
|
|
165
|
+
data: "msg2"
|
|
166
|
+
producerId: test-producer
|
|
167
|
+
producerEpoch: 0
|
|
168
|
+
producerSeq: 2
|
|
169
|
+
expect:
|
|
170
|
+
status: 409
|
|
171
|
+
# Fill the gap with seq=1
|
|
172
|
+
- action: server-append
|
|
173
|
+
path: ${streamPath}
|
|
174
|
+
data: "msg1"
|
|
175
|
+
producerId: test-producer
|
|
176
|
+
producerEpoch: 0
|
|
177
|
+
producerSeq: 1
|
|
178
|
+
expect:
|
|
179
|
+
status: 200
|
|
180
|
+
# Now seq=2 should succeed
|
|
181
|
+
- action: server-append
|
|
182
|
+
path: ${streamPath}
|
|
183
|
+
data: "msg2"
|
|
184
|
+
producerId: test-producer
|
|
185
|
+
producerEpoch: 0
|
|
186
|
+
producerSeq: 2
|
|
187
|
+
expect:
|
|
188
|
+
status: 200
|
|
189
|
+
# Verify correct order in stream (text/plain concatenates all data)
|
|
190
|
+
- action: read
|
|
191
|
+
path: ${streamPath}
|
|
192
|
+
expect:
|
|
193
|
+
data: "msg0msg1msg2"
|
|
194
|
+
upToDate: true
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
id: idempotent-multi-producer
|
|
2
|
+
name: Idempotent Producer - Multi-Producer Scenarios
|
|
3
|
+
description: |
|
|
4
|
+
Tests for multiple producers on the same stream and producer isolation.
|
|
5
|
+
Each (stream, producerId) has independent epoch/sequence state.
|
|
6
|
+
category: producer
|
|
7
|
+
tags:
|
|
8
|
+
- idempotent
|
|
9
|
+
- multi-producer
|
|
10
|
+
- isolation
|
|
11
|
+
|
|
12
|
+
tests:
|
|
13
|
+
- id: multiple-producers-independent
|
|
14
|
+
name: Multiple producers have independent state
|
|
15
|
+
description: Different producer IDs should have separate epoch/seq tracking
|
|
16
|
+
setup:
|
|
17
|
+
- action: create
|
|
18
|
+
as: streamPath
|
|
19
|
+
contentType: text/plain
|
|
20
|
+
operations:
|
|
21
|
+
# Producer A: seq=0
|
|
22
|
+
- action: server-append
|
|
23
|
+
path: ${streamPath}
|
|
24
|
+
data: "A0"
|
|
25
|
+
producerId: producer-A
|
|
26
|
+
producerEpoch: 0
|
|
27
|
+
producerSeq: 0
|
|
28
|
+
expect:
|
|
29
|
+
status: 200
|
|
30
|
+
# Producer B: seq=0 (independent)
|
|
31
|
+
- action: server-append
|
|
32
|
+
path: ${streamPath}
|
|
33
|
+
data: "B0"
|
|
34
|
+
producerId: producer-B
|
|
35
|
+
producerEpoch: 0
|
|
36
|
+
producerSeq: 0
|
|
37
|
+
expect:
|
|
38
|
+
status: 200
|
|
39
|
+
# Producer A: seq=1
|
|
40
|
+
- action: server-append
|
|
41
|
+
path: ${streamPath}
|
|
42
|
+
data: "A1"
|
|
43
|
+
producerId: producer-A
|
|
44
|
+
producerEpoch: 0
|
|
45
|
+
producerSeq: 1
|
|
46
|
+
expect:
|
|
47
|
+
status: 200
|
|
48
|
+
# Producer B: seq=1
|
|
49
|
+
- action: server-append
|
|
50
|
+
path: ${streamPath}
|
|
51
|
+
data: "B1"
|
|
52
|
+
producerId: producer-B
|
|
53
|
+
producerEpoch: 0
|
|
54
|
+
producerSeq: 1
|
|
55
|
+
expect:
|
|
56
|
+
status: 200
|
|
57
|
+
|
|
58
|
+
- id: producer-scope-per-stream
|
|
59
|
+
name: Same producer ID works independently on different streams
|
|
60
|
+
description: |
|
|
61
|
+
Producer state must be scoped per (stream, producerId), not globally
|
|
62
|
+
by producerId alone. A common implementation bug is to key producer
|
|
63
|
+
state only by producer ID, causing cross-stream interference.
|
|
64
|
+
setup:
|
|
65
|
+
- action: create
|
|
66
|
+
as: streamA
|
|
67
|
+
contentType: text/plain
|
|
68
|
+
- action: create
|
|
69
|
+
as: streamB
|
|
70
|
+
contentType: text/plain
|
|
71
|
+
operations:
|
|
72
|
+
# Same producer, epoch=0, seq=0 on stream A
|
|
73
|
+
- action: server-append
|
|
74
|
+
path: ${streamA}
|
|
75
|
+
data: "streamA-msg"
|
|
76
|
+
producerId: shared-producer
|
|
77
|
+
producerEpoch: 0
|
|
78
|
+
producerSeq: 0
|
|
79
|
+
expect:
|
|
80
|
+
status: 200
|
|
81
|
+
# Same producer, epoch=0, seq=0 on stream B - must be 200, not 204/403
|
|
82
|
+
- action: server-append
|
|
83
|
+
path: ${streamB}
|
|
84
|
+
data: "streamB-msg"
|
|
85
|
+
producerId: shared-producer
|
|
86
|
+
producerEpoch: 0
|
|
87
|
+
producerSeq: 0
|
|
88
|
+
expect:
|
|
89
|
+
status: 200
|
|
90
|
+
# Verify each stream has its own message
|
|
91
|
+
- action: read
|
|
92
|
+
path: ${streamA}
|
|
93
|
+
expect:
|
|
94
|
+
data: "streamA-msg"
|
|
95
|
+
upToDate: true
|
|
96
|
+
- action: read
|
|
97
|
+
path: ${streamB}
|
|
98
|
+
expect:
|
|
99
|
+
data: "streamB-msg"
|
|
100
|
+
upToDate: true
|
|
101
|
+
|
|
102
|
+
- id: producer-epoch-per-stream
|
|
103
|
+
name: Producer epochs are independent per stream
|
|
104
|
+
description: |
|
|
105
|
+
Epoch fencing on one stream must not affect the same producer on
|
|
106
|
+
another stream. Each (stream, producerId) has independent epoch state.
|
|
107
|
+
setup:
|
|
108
|
+
- action: create
|
|
109
|
+
as: streamA
|
|
110
|
+
contentType: text/plain
|
|
111
|
+
- action: create
|
|
112
|
+
as: streamB
|
|
113
|
+
contentType: text/plain
|
|
114
|
+
operations:
|
|
115
|
+
# Establish epoch=5 on stream A
|
|
116
|
+
- action: server-append
|
|
117
|
+
path: ${streamA}
|
|
118
|
+
data: "A-epoch5"
|
|
119
|
+
producerId: shared-producer
|
|
120
|
+
producerEpoch: 5
|
|
121
|
+
producerSeq: 0
|
|
122
|
+
expect:
|
|
123
|
+
status: 200
|
|
124
|
+
# epoch=0 on stream B should still work (different stream)
|
|
125
|
+
- action: server-append
|
|
126
|
+
path: ${streamB}
|
|
127
|
+
data: "B-epoch0"
|
|
128
|
+
producerId: shared-producer
|
|
129
|
+
producerEpoch: 0
|
|
130
|
+
producerSeq: 0
|
|
131
|
+
expect:
|
|
132
|
+
status: 200
|
|
133
|
+
# epoch=0 on stream A should be fenced
|
|
134
|
+
- action: server-append
|
|
135
|
+
path: ${streamA}
|
|
136
|
+
data: "A-epoch0-zombie"
|
|
137
|
+
producerId: shared-producer
|
|
138
|
+
producerEpoch: 0
|
|
139
|
+
producerSeq: 1
|
|
140
|
+
expect:
|
|
141
|
+
status: 403
|
|
142
|
+
producerEpoch: 5
|
|
143
|
+
|
|
144
|
+
- id: interleaved-producers-ordering
|
|
145
|
+
name: Interleaved producers maintain per-producer ordering
|
|
146
|
+
description: |
|
|
147
|
+
When multiple producers write interleaved, each producer's messages
|
|
148
|
+
should appear in their sequence order relative to each other.
|
|
149
|
+
setup:
|
|
150
|
+
- action: create
|
|
151
|
+
as: streamPath
|
|
152
|
+
contentType: text/plain
|
|
153
|
+
operations:
|
|
154
|
+
# Interleave writes from two producers
|
|
155
|
+
- action: server-append
|
|
156
|
+
path: ${streamPath}
|
|
157
|
+
data: "A0"
|
|
158
|
+
producerId: producer-A
|
|
159
|
+
producerEpoch: 0
|
|
160
|
+
producerSeq: 0
|
|
161
|
+
expect:
|
|
162
|
+
status: 200
|
|
163
|
+
- action: server-append
|
|
164
|
+
path: ${streamPath}
|
|
165
|
+
data: "B0"
|
|
166
|
+
producerId: producer-B
|
|
167
|
+
producerEpoch: 0
|
|
168
|
+
producerSeq: 0
|
|
169
|
+
expect:
|
|
170
|
+
status: 200
|
|
171
|
+
- action: server-append
|
|
172
|
+
path: ${streamPath}
|
|
173
|
+
data: "A1"
|
|
174
|
+
producerId: producer-A
|
|
175
|
+
producerEpoch: 0
|
|
176
|
+
producerSeq: 1
|
|
177
|
+
expect:
|
|
178
|
+
status: 200
|
|
179
|
+
- action: server-append
|
|
180
|
+
path: ${streamPath}
|
|
181
|
+
data: "B1"
|
|
182
|
+
producerId: producer-B
|
|
183
|
+
producerEpoch: 0
|
|
184
|
+
producerSeq: 1
|
|
185
|
+
expect:
|
|
186
|
+
status: 200
|
|
187
|
+
- action: server-append
|
|
188
|
+
path: ${streamPath}
|
|
189
|
+
data: "A2"
|
|
190
|
+
producerId: producer-A
|
|
191
|
+
producerEpoch: 0
|
|
192
|
+
producerSeq: 2
|
|
193
|
+
expect:
|
|
194
|
+
status: 200
|
|
195
|
+
# Read and verify A0 comes before A1 comes before A2
|
|
196
|
+
# and B0 comes before B1
|
|
197
|
+
- action: read
|
|
198
|
+
path: ${streamPath}
|
|
199
|
+
expect:
|
|
200
|
+
dataContainsAll:
|
|
201
|
+
- "A0"
|
|
202
|
+
- "A1"
|
|
203
|
+
- "A2"
|
|
204
|
+
- "B0"
|
|
205
|
+
- "B1"
|
|
206
|
+
upToDate: true
|
|
207
|
+
|
|
208
|
+
- id: delete-recreate-resets-producer-state
|
|
209
|
+
name: Deleting and recreating stream resets producer state
|
|
210
|
+
description: |
|
|
211
|
+
When a stream is deleted and recreated, producer state must be reset.
|
|
212
|
+
The new stream is a fresh log, so (epoch=0, seq=0) must be accepted
|
|
213
|
+
even if the same producer previously wrote to the old stream.
|
|
214
|
+
setup:
|
|
215
|
+
- action: create
|
|
216
|
+
as: streamPath
|
|
217
|
+
contentType: text/plain
|
|
218
|
+
operations:
|
|
219
|
+
# Write to original stream
|
|
220
|
+
- action: server-append
|
|
221
|
+
path: ${streamPath}
|
|
222
|
+
data: "original-msg"
|
|
223
|
+
producerId: test-producer
|
|
224
|
+
producerEpoch: 0
|
|
225
|
+
producerSeq: 0
|
|
226
|
+
expect:
|
|
227
|
+
status: 200
|
|
228
|
+
# Advance sequence
|
|
229
|
+
- action: server-append
|
|
230
|
+
path: ${streamPath}
|
|
231
|
+
data: "original-msg2"
|
|
232
|
+
producerId: test-producer
|
|
233
|
+
producerEpoch: 0
|
|
234
|
+
producerSeq: 1
|
|
235
|
+
expect:
|
|
236
|
+
status: 200
|
|
237
|
+
# Delete the stream
|
|
238
|
+
- action: delete
|
|
239
|
+
path: ${streamPath}
|
|
240
|
+
expect:
|
|
241
|
+
status: 200
|
|
242
|
+
# Recreate at same path
|
|
243
|
+
- action: create
|
|
244
|
+
path: ${streamPath}
|
|
245
|
+
contentType: text/plain
|
|
246
|
+
# Same producer, epoch=0, seq=0 must work (new stream)
|
|
247
|
+
- action: server-append
|
|
248
|
+
path: ${streamPath}
|
|
249
|
+
data: "recreated-msg"
|
|
250
|
+
producerId: test-producer
|
|
251
|
+
producerEpoch: 0
|
|
252
|
+
producerSeq: 0
|
|
253
|
+
expect:
|
|
254
|
+
status: 200
|
|
255
|
+
# Verify only the new message exists
|
|
256
|
+
- action: read
|
|
257
|
+
path: ${streamPath}
|
|
258
|
+
expect:
|
|
259
|
+
data: "recreated-msg"
|
|
260
|
+
upToDate: true
|
|
261
|
+
|
|
262
|
+
- id: producer-id-long
|
|
263
|
+
name: Long producer ID works
|
|
264
|
+
description: Very long producer IDs should be accepted
|
|
265
|
+
setup:
|
|
266
|
+
- action: create
|
|
267
|
+
as: streamPath
|
|
268
|
+
contentType: text/plain
|
|
269
|
+
operations:
|
|
270
|
+
- action: server-append
|
|
271
|
+
path: ${streamPath}
|
|
272
|
+
data: "long-id"
|
|
273
|
+
producerId: "very-long-producer-id-that-goes-on-for-quite-a-while-to-test-string-handling-in-the-server-implementation-and-make-sure-it-doesnt-truncate"
|
|
274
|
+
producerEpoch: 0
|
|
275
|
+
producerSeq: 0
|
|
276
|
+
expect:
|
|
277
|
+
status: 200
|
|
278
|
+
# Duplicate detection works with long ID
|
|
279
|
+
- action: server-append
|
|
280
|
+
path: ${streamPath}
|
|
281
|
+
data: "long-id"
|
|
282
|
+
producerId: "very-long-producer-id-that-goes-on-for-quite-a-while-to-test-string-handling-in-the-server-implementation-and-make-sure-it-doesnt-truncate"
|
|
283
|
+
producerEpoch: 0
|
|
284
|
+
producerSeq: 0
|
|
285
|
+
expect:
|
|
286
|
+
status: 204
|
|
287
|
+
|
|
288
|
+
- id: producer-id-with-special-chars
|
|
289
|
+
name: Producer ID with special characters works
|
|
290
|
+
description: Producer IDs can contain colons, slashes, and other special chars
|
|
291
|
+
setup:
|
|
292
|
+
- action: create
|
|
293
|
+
as: streamPath
|
|
294
|
+
contentType: text/plain
|
|
295
|
+
operations:
|
|
296
|
+
# Colon-separated ID (common pattern)
|
|
297
|
+
- action: server-append
|
|
298
|
+
path: ${streamPath}
|
|
299
|
+
data: "msg1"
|
|
300
|
+
producerId: "service:instance:123"
|
|
301
|
+
producerEpoch: 0
|
|
302
|
+
producerSeq: 0
|
|
303
|
+
expect:
|
|
304
|
+
status: 200
|
|
305
|
+
# Slash-separated ID (path-like)
|
|
306
|
+
- action: server-append
|
|
307
|
+
path: ${streamPath}
|
|
308
|
+
data: "msg2"
|
|
309
|
+
producerId: "region/zone/host"
|
|
310
|
+
producerEpoch: 0
|
|
311
|
+
producerSeq: 0
|
|
312
|
+
expect:
|
|
313
|
+
status: 200
|
|
314
|
+
# UUID format
|
|
315
|
+
- action: server-append
|
|
316
|
+
path: ${streamPath}
|
|
317
|
+
data: "msg3"
|
|
318
|
+
producerId: "550e8400-e29b-41d4-a716-446655440000"
|
|
319
|
+
producerEpoch: 0
|
|
320
|
+
producerSeq: 0
|
|
321
|
+
expect:
|
|
322
|
+
status: 200
|