@durable-streams/client-conformance-tests 0.2.0 → 0.2.1

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.
@@ -0,0 +1,663 @@
1
+ id: consumer-sse-base64
2
+ name: SSE Base64 Encoding
3
+ description: Tests for auto-detected base64 encoding of binary data in SSE mode (Protocol Section 5.7)
4
+ category: consumer
5
+ tags:
6
+ - sse
7
+ - base64
8
+ - binary
9
+ requires:
10
+ - sse
11
+
12
+ tests:
13
+ - id: sse-base64-basic
14
+ name: SSE decodes base64 binary data
15
+ description: Client should decode base64-encoded SSE data events for binary streams
16
+ setup:
17
+ - action: create
18
+ as: streamPath
19
+ contentType: application/octet-stream
20
+ - action: append
21
+ path: ${streamPath}
22
+ binaryData: "SGVsbG8gV29ybGQ=" # "Hello World" in base64
23
+ operations:
24
+ - action: read
25
+ path: ${streamPath}
26
+ live: sse
27
+ waitForUpToDate: true
28
+ expect:
29
+ minChunks: 1
30
+ upToDate: true
31
+
32
+ # Data integrity verification tests
33
+ - id: sse-base64-data-integrity-simple
34
+ name: SSE base64 data integrity - simple text
35
+ description: Verify decoded data matches original content exactly
36
+ setup:
37
+ - action: create
38
+ as: streamPath
39
+ contentType: application/octet-stream
40
+ - action: append
41
+ path: ${streamPath}
42
+ binaryData: "SGVsbG8gV29ybGQ=" # "Hello World" in base64
43
+ operations:
44
+ - action: read
45
+ path: ${streamPath}
46
+ live: sse
47
+ waitForUpToDate: true
48
+ expect:
49
+ minChunks: 1
50
+ upToDate: true
51
+ data: "Hello World"
52
+
53
+ - id: sse-base64-data-integrity-multiline
54
+ name: SSE base64 data integrity - multiline text
55
+ skip: "Swift implementation broken"
56
+ description: Verify multiline text content decodes correctly
57
+ setup:
58
+ - action: create
59
+ as: streamPath
60
+ contentType: application/octet-stream
61
+ - action: append
62
+ path: ${streamPath}
63
+ # "Line 1\nLine 2\nLine 3" in base64
64
+ binaryData: "TGluZSAxCkxpbmUgMgpMaW5lIDM="
65
+ operations:
66
+ - action: read
67
+ path: ${streamPath}
68
+ live: sse
69
+ waitForUpToDate: true
70
+ expect:
71
+ minChunks: 1
72
+ upToDate: true
73
+ dataContains: "Line 1"
74
+ dataContainsAll:
75
+ - "Line 1"
76
+ - "Line 2"
77
+ - "Line 3"
78
+
79
+ - id: sse-base64-data-integrity-multiple-appends
80
+ name: SSE base64 data integrity - multiple appends
81
+ description: Verify multiple appends are decoded and concatenated correctly
82
+ setup:
83
+ - action: create
84
+ as: streamPath
85
+ contentType: application/octet-stream
86
+ - action: append
87
+ path: ${streamPath}
88
+ binaryData: "Zmlyc3Q=" # "first" in base64
89
+ - action: append
90
+ path: ${streamPath}
91
+ binaryData: "c2Vjb25k" # "second" in base64
92
+ - action: append
93
+ path: ${streamPath}
94
+ binaryData: "dGhpcmQ=" # "third" in base64
95
+ operations:
96
+ - action: read
97
+ path: ${streamPath}
98
+ live: sse
99
+ waitForUpToDate: true
100
+ expect:
101
+ minChunks: 1
102
+ upToDate: true
103
+ dataContainsAll:
104
+ - "first"
105
+ - "second"
106
+ - "third"
107
+
108
+ - id: sse-base64-data-integrity-ascii
109
+ name: SSE base64 data integrity - ASCII printable characters
110
+ description: Verify all printable ASCII characters (32-126) decode correctly
111
+ setup:
112
+ - action: create
113
+ as: streamPath
114
+ contentType: application/octet-stream
115
+ - action: append
116
+ path: ${streamPath}
117
+ # All printable ASCII chars: space through tilde
118
+ # " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
119
+ binaryData: "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX4="
120
+ operations:
121
+ - action: read
122
+ path: ${streamPath}
123
+ live: sse
124
+ waitForUpToDate: true
125
+ expect:
126
+ minChunks: 1
127
+ upToDate: true
128
+ dataContainsAll:
129
+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
130
+ - "abcdefghijklmnopqrstuvwxyz"
131
+ - "0123456789"
132
+
133
+ - id: sse-base64-data-integrity-json-like
134
+ name: SSE base64 data integrity - JSON-like content
135
+ description: Verify JSON-like binary content is preserved through base64 encoding
136
+ setup:
137
+ - action: create
138
+ as: streamPath
139
+ contentType: application/octet-stream
140
+ - action: append
141
+ path: ${streamPath}
142
+ # {"key":"value","num":42} in base64
143
+ binaryData: "eyJrZXkiOiJ2YWx1ZSIsIm51bSI6NDJ9"
144
+ operations:
145
+ - action: read
146
+ path: ${streamPath}
147
+ live: sse
148
+ waitForUpToDate: true
149
+ expect:
150
+ minChunks: 1
151
+ upToDate: true
152
+ data: '{"key":"value","num":42}'
153
+
154
+ - id: sse-base64-empty-payload
155
+ name: SSE handles empty base64 payload
156
+ description: Empty binary data should be encoded as empty base64 and decoded correctly
157
+ setup:
158
+ - action: create
159
+ as: streamPath
160
+ contentType: application/octet-stream
161
+ - action: append
162
+ path: ${streamPath}
163
+ binaryData: "" # empty payload
164
+ operations:
165
+ - action: read
166
+ path: ${streamPath}
167
+ live: sse
168
+ waitForUpToDate: true
169
+ expect:
170
+ upToDate: true
171
+
172
+ - id: sse-base64-special-bytes
173
+ name: SSE handles special binary bytes
174
+ description: Binary data with null bytes, 0xFF, and other special values should round-trip correctly
175
+ setup:
176
+ - action: create
177
+ as: streamPath
178
+ contentType: application/octet-stream
179
+ - action: append
180
+ path: ${streamPath}
181
+ binaryData: "AP/+/fw=" # bytes: 0x00, 0xFF, 0xFE, 0xFD, 0xFC
182
+ operations:
183
+ - action: read
184
+ path: ${streamPath}
185
+ live: sse
186
+ waitForUpToDate: true
187
+ expect:
188
+ minChunks: 1
189
+ upToDate: true
190
+
191
+ - id: sse-base64-large-payload
192
+ name: SSE handles large base64 payload
193
+ description: Large binary payloads should be encoded and decoded correctly
194
+ setup:
195
+ - action: create
196
+ as: streamPath
197
+ contentType: application/octet-stream
198
+ - action: append
199
+ path: ${streamPath}
200
+ # 1KB of repeated pattern (base64 of 0x00-0xFF repeated ~4 times)
201
+ binaryData: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
202
+ operations:
203
+ - action: read
204
+ path: ${streamPath}
205
+ live: sse
206
+ waitForUpToDate: true
207
+ expect:
208
+ minChunks: 1
209
+ upToDate: true
210
+
211
+ - id: sse-base64-multiple-chunks
212
+ name: SSE handles multiple base64 chunks
213
+ description: Multiple binary appends should each be decoded correctly
214
+ setup:
215
+ - action: create
216
+ as: streamPath
217
+ contentType: application/octet-stream
218
+ - action: append
219
+ path: ${streamPath}
220
+ binaryData: "AQID" # bytes: 1, 2, 3
221
+ - action: append
222
+ path: ${streamPath}
223
+ binaryData: "BAUG" # bytes: 4, 5, 6
224
+ - action: append
225
+ path: ${streamPath}
226
+ binaryData: "BwgJ" # bytes: 7, 8, 9
227
+ operations:
228
+ - action: read
229
+ path: ${streamPath}
230
+ live: sse
231
+ waitForUpToDate: true
232
+ expect:
233
+ minChunks: 1
234
+ upToDate: true
235
+
236
+ - id: sse-base64-resume-from-offset
237
+ name: SSE base64 resumes from offset correctly
238
+ description: Reading from a specific offset should work with base64 encoding
239
+ setup:
240
+ - action: create
241
+ as: streamPath
242
+ contentType: application/octet-stream
243
+ - action: append
244
+ path: ${streamPath}
245
+ binaryData: "AAAA" # skip this
246
+ expect:
247
+ storeOffsetAs: skipOffset
248
+ - action: append
249
+ path: ${streamPath}
250
+ binaryData: "//8=" # include this (0xFF, 0xFF)
251
+ operations:
252
+ - action: read
253
+ path: ${streamPath}
254
+ offset: ${skipOffset}
255
+ live: sse
256
+ waitForUpToDate: true
257
+ expect:
258
+ minChunks: 1
259
+ upToDate: true
260
+
261
+ - id: sse-base64-up-to-date
262
+ name: SSE base64 signals up-to-date
263
+ description: SSE with base64 encoding should signal when caught up to head
264
+ setup:
265
+ - action: create
266
+ as: streamPath
267
+ contentType: application/octet-stream
268
+ - action: append
269
+ path: ${streamPath}
270
+ binaryData: "AQIDBA==" # bytes: 1, 2, 3, 4
271
+ operations:
272
+ - action: read
273
+ path: ${streamPath}
274
+ live: sse
275
+ waitForUpToDate: true
276
+ expect:
277
+ upToDate: true
278
+ minChunks: 1
279
+
280
+ - id: sse-base64-padding-variations
281
+ name: SSE handles base64 padding variations
282
+ description: Base64 with different padding (=, ==, no padding) should decode correctly
283
+ setup:
284
+ - action: create
285
+ as: streamPath
286
+ contentType: application/octet-stream
287
+ - action: append
288
+ path: ${streamPath}
289
+ binaryData: "YQ==" # 'a' - 2 padding chars
290
+ - action: append
291
+ path: ${streamPath}
292
+ binaryData: "YWI=" # 'ab' - 1 padding char
293
+ - action: append
294
+ path: ${streamPath}
295
+ binaryData: "YWJj" # 'abc' - no padding
296
+ operations:
297
+ - action: read
298
+ path: ${streamPath}
299
+ live: sse
300
+ waitForUpToDate: true
301
+ expect:
302
+ minChunks: 1
303
+ upToDate: true
304
+
305
+ - id: sse-base64-all-byte-values
306
+ name: SSE handles all byte values 0-255
307
+ description: All possible byte values should round-trip correctly through base64
308
+ setup:
309
+ - action: create
310
+ as: streamPath
311
+ contentType: application/octet-stream
312
+ - action: append
313
+ path: ${streamPath}
314
+ # All bytes 0x00-0xFF encoded as base64
315
+ binaryData: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
316
+ operations:
317
+ - action: read
318
+ path: ${streamPath}
319
+ live: sse
320
+ waitForUpToDate: true
321
+ expect:
322
+ minChunks: 1
323
+ upToDate: true
324
+
325
+ # SSE reconnection test - verifies decoder state resets properly
326
+ - id: sse-base64-reconnect-decoding
327
+ name: SSE base64 decoding works after reconnect
328
+ description: |
329
+ After SSE connection closes and client reconnects from last offset,
330
+ base64 decoding should work correctly without corruption from
331
+ any previous decoder state.
332
+ setup:
333
+ - action: create
334
+ as: streamPath
335
+ contentType: application/octet-stream
336
+ - action: append
337
+ path: ${streamPath}
338
+ binaryData: "Zmlyc3Q=" # "first"
339
+ expect:
340
+ storeOffsetAs: firstOffset
341
+ - action: append
342
+ path: ${streamPath}
343
+ binaryData: "c2Vjb25k" # "second"
344
+ operations:
345
+ # First read - gets "first", stops after 1 chunk
346
+ - action: read
347
+ path: ${streamPath}
348
+ live: sse
349
+ maxChunks: 1
350
+ expect:
351
+ minChunks: 1
352
+ dataContains: "first"
353
+ # Second read - reconnects from offset, should decode "second" correctly
354
+ - action: read
355
+ path: ${streamPath}
356
+ offset: ${firstOffset}
357
+ live: sse
358
+ waitForUpToDate: true
359
+ expect:
360
+ minChunks: 1
361
+ dataContains: "second"
362
+ upToDate: true
363
+
364
+ # Large payload test - verifies multi-line base64 handling
365
+ - id: sse-base64-large-payload-4kb
366
+ name: SSE handles 4KB base64 payload with potential line splits
367
+ skip: "Swift implementation broken"
368
+ description: |
369
+ Large binary payloads (4KB) may be split across multiple data: lines by servers.
370
+ Per Protocol Section 5.7, clients must concatenate lines and remove newlines
371
+ before decoding. This test uses ASCII-safe data to ensure proper verification
372
+ across all client implementations.
373
+ setup:
374
+ - action: create
375
+ as: streamPath
376
+ contentType: application/octet-stream
377
+ - action: append
378
+ path: ${streamPath}
379
+ # 4KB payload: 56 lines of "Line NNNN: ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz\n"
380
+ # All ASCII-safe characters that survive UTF-8 encoding
381
+ binaryData: "TGluZSAwMDAwOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMDE6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAwMjogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDAzOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMDQ6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAwNTogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDA2OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMDc6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAwODogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDA5OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMTA6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAxMTogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDEyOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMTM6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAxNDogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDE1OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMTY6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAxNzogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDE4OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMTk6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAyMDogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDIxOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMjI6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAyMzogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDI0OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMjU6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAyNjogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDI3OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMjg6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAyOTogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDMwOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMzE6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAzMjogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDMzOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMzQ6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAzNTogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDM2OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwMzc6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDAzODogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDM5OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwNDA6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDA0MTogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDQyOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwNDM6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDA0NDogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDQ1OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwNDY6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDA0NzogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDQ4OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwNDk6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDA1MDogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDUxOiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwNTI6IEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaMDEyMzQ1Njc4OWFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6CkxpbmUgMDA1MzogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVowMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoKTGluZSAwMDU0OiBBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWjAxMjM0NTY3ODlhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5egpMaW5lIDAwNTU6IEFCQ0RFRkdISUpLTE1OTw=="
382
+ operations:
383
+ - action: read
384
+ path: ${streamPath}
385
+ live: sse
386
+ waitForUpToDate: true
387
+ timeoutMs: 10000
388
+ expect:
389
+ minChunks: 1
390
+ upToDate: true
391
+ # Verify the decoded data contains expected patterns from the ASCII-safe payload
392
+ dataContainsAll:
393
+ - "Line 0000"
394
+ - "Line 0025"
395
+ - "Line 0050"
396
+ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
397
+ - "0123456789"
398
+ - "abcdefghijklmnopqrstuvwxyz"
399
+
400
+ # Null byte handling (Gap 6)
401
+ - id: sse-base64-single-null-byte
402
+ name: SSE base64 handles single null byte
403
+ description: |
404
+ A single 0x00 byte must survive base64 round-trip. This catches
405
+ C-string termination bugs and JS typed-array edge cases.
406
+ setup:
407
+ - action: create
408
+ as: streamPath
409
+ contentType: application/octet-stream
410
+ - action: append
411
+ path: ${streamPath}
412
+ binaryData: "AA==" # single 0x00 byte
413
+ operations:
414
+ - action: read
415
+ path: ${streamPath}
416
+ live: sse
417
+ waitForUpToDate: true
418
+ expect:
419
+ minChunks: 1
420
+ upToDate: true
421
+
422
+ # Live streaming with base64 (Gap 9)
423
+ - id: sse-base64-live-streaming
424
+ name: SSE base64 receives live data
425
+ description: |
426
+ SSE stream with base64 encoding should receive data appended after
427
+ the connection is established, not just historical data.
428
+ setup:
429
+ - action: create
430
+ as: streamPath
431
+ contentType: application/octet-stream
432
+ - action: append
433
+ path: ${streamPath}
434
+ binaryData: "aW5pdGlhbA==" # "initial"
435
+ expect:
436
+ storeOffsetAs: offset
437
+ operations:
438
+ - action: read
439
+ path: ${streamPath}
440
+ offset: ${offset}
441
+ live: sse
442
+ maxChunks: 1
443
+ timeoutMs: 10000
444
+ background: true
445
+ as: readOp
446
+ - action: wait
447
+ ms: 200
448
+ - action: server-append
449
+ path: ${streamPath}
450
+ data: "live-update"
451
+ - action: await
452
+ ref: readOp
453
+ expect:
454
+ dataContains: "live-update"
455
+ minChunks: 1
456
+
457
+ # RFC 4648 Section 10 test vectors (Gap 10)
458
+ - id: sse-base64-rfc4648-1byte
459
+ name: SSE base64 RFC 4648 vector - 1 byte (double padding)
460
+ description: RFC 4648 canonical vector for "f" (1 byte, double == padding)
461
+ setup:
462
+ - action: create
463
+ as: streamPath
464
+ contentType: application/octet-stream
465
+ - action: append
466
+ path: ${streamPath}
467
+ binaryData: "Zg==" # "f"
468
+ operations:
469
+ - action: read
470
+ path: ${streamPath}
471
+ live: sse
472
+ waitForUpToDate: true
473
+ expect:
474
+ minChunks: 1
475
+ upToDate: true
476
+ data: "f"
477
+
478
+ - id: sse-base64-rfc4648-2byte
479
+ name: SSE base64 RFC 4648 vector - 2 bytes (single padding)
480
+ description: RFC 4648 canonical vector for "fo" (2 bytes, single = padding)
481
+ setup:
482
+ - action: create
483
+ as: streamPath
484
+ contentType: application/octet-stream
485
+ - action: append
486
+ path: ${streamPath}
487
+ binaryData: "Zm8=" # "fo"
488
+ operations:
489
+ - action: read
490
+ path: ${streamPath}
491
+ live: sse
492
+ waitForUpToDate: true
493
+ expect:
494
+ minChunks: 1
495
+ upToDate: true
496
+ data: "fo"
497
+
498
+ - id: sse-base64-rfc4648-3byte
499
+ name: SSE base64 RFC 4648 vector - 3 bytes (no padding)
500
+ description: RFC 4648 canonical vector for "foo" (3 bytes, no padding)
501
+ setup:
502
+ - action: create
503
+ as: streamPath
504
+ contentType: application/octet-stream
505
+ - action: append
506
+ path: ${streamPath}
507
+ binaryData: "Zm9v" # "foo"
508
+ operations:
509
+ - action: read
510
+ path: ${streamPath}
511
+ live: sse
512
+ waitForUpToDate: true
513
+ expect:
514
+ minChunks: 1
515
+ upToDate: true
516
+ data: "foo"
517
+
518
+ - id: sse-base64-rfc4648-6byte
519
+ name: SSE base64 RFC 4648 vector - 6 bytes (multi-block)
520
+ description: RFC 4648 canonical vector for "foobar" (6 bytes, multi-block boundary, no padding)
521
+ setup:
522
+ - action: create
523
+ as: streamPath
524
+ contentType: application/octet-stream
525
+ - action: append
526
+ path: ${streamPath}
527
+ binaryData: "Zm9vYmFy" # "foobar"
528
+ operations:
529
+ - action: read
530
+ path: ${streamPath}
531
+ live: sse
532
+ waitForUpToDate: true
533
+ expect:
534
+ minChunks: 1
535
+ upToDate: true
536
+ data: "foobar"
537
+
538
+ # Content-type variations (Gap 11)
539
+ - id: sse-base64-content-type-protobuf
540
+ name: SSE base64 works with application/x-protobuf streams
541
+ description: |
542
+ Binary streams with content type application/x-protobuf should
543
+ work with base64 encoding, catching implementations that hardcode
544
+ application/octet-stream checks.
545
+ setup:
546
+ - action: create
547
+ as: streamPath
548
+ contentType: application/x-protobuf
549
+ - action: append
550
+ path: ${streamPath}
551
+ binaryData: "CAYSBG5hbWU=" # protobuf-like bytes
552
+ operations:
553
+ - action: read
554
+ path: ${streamPath}
555
+ live: sse
556
+ waitForUpToDate: true
557
+ expect:
558
+ minChunks: 1
559
+ upToDate: true
560
+
561
+ - id: sse-base64-content-type-image
562
+ name: SSE base64 works with image/png streams
563
+ description: |
564
+ Binary streams with content type image/png should work with
565
+ base64 encoding.
566
+ setup:
567
+ - action: create
568
+ as: streamPath
569
+ contentType: image/png
570
+ - action: append
571
+ path: ${streamPath}
572
+ binaryData: "iVBORw0KGgo=" # PNG magic bytes (truncated)
573
+ operations:
574
+ - action: read
575
+ path: ${streamPath}
576
+ live: sse
577
+ waitForUpToDate: true
578
+ expect:
579
+ minChunks: 1
580
+ upToDate: true
581
+
582
+ # Offset=now with base64 (Gap 17)
583
+ - id: sse-base64-offset-now
584
+ name: SSE base64 works with offset=now
585
+ description: |
586
+ Starting an SSE read with offset=now on a binary stream should
587
+ skip historical data and receive only new live data.
588
+ setup:
589
+ - action: create
590
+ as: streamPath
591
+ contentType: application/octet-stream
592
+ - action: append
593
+ path: ${streamPath}
594
+ binaryData: "aGlzdG9yaWNhbA==" # "historical"
595
+ operations:
596
+ - action: read
597
+ path: ${streamPath}
598
+ offset: "now"
599
+ live: sse
600
+ maxChunks: 1
601
+ timeoutMs: 10000
602
+ background: true
603
+ as: readOp
604
+ - action: wait
605
+ ms: 200
606
+ - action: server-append
607
+ path: ${streamPath}
608
+ data: "new-data"
609
+ - action: await
610
+ ref: readOp
611
+ expect:
612
+ dataContains: "new-data"
613
+ minChunks: 1
614
+
615
+ # Many small messages (Gap 12)
616
+ - id: sse-base64-many-small-messages
617
+ name: SSE base64 handles many small binary messages
618
+ description: |
619
+ Ten small 3-byte binary payloads should all be decoded correctly,
620
+ catching decoder performance issues and event boundary bugs.
621
+ setup:
622
+ - action: create
623
+ as: streamPath
624
+ contentType: application/octet-stream
625
+ - action: append
626
+ path: ${streamPath}
627
+ binaryData: "AQID" # bytes: 1, 2, 3
628
+ - action: append
629
+ path: ${streamPath}
630
+ binaryData: "BAUG" # bytes: 4, 5, 6
631
+ - action: append
632
+ path: ${streamPath}
633
+ binaryData: "BwgJ" # bytes: 7, 8, 9
634
+ - action: append
635
+ path: ${streamPath}
636
+ binaryData: "CgsM" # bytes: 10, 11, 12
637
+ - action: append
638
+ path: ${streamPath}
639
+ binaryData: "DQ4P" # bytes: 13, 14, 15
640
+ - action: append
641
+ path: ${streamPath}
642
+ binaryData: "EBEW" # bytes: 16, 17, 22
643
+ - action: append
644
+ path: ${streamPath}
645
+ binaryData: "ExQV" # bytes: 19, 20, 21
646
+ - action: append
647
+ path: ${streamPath}
648
+ binaryData: "FhcY" # bytes: 22, 23, 24
649
+ - action: append
650
+ path: ${streamPath}
651
+ binaryData: "GRob" # bytes: 25, 26, 27
652
+ - action: append
653
+ path: ${streamPath}
654
+ binaryData: "HB0e" # bytes: 28, 29, 30
655
+ operations:
656
+ - action: read
657
+ path: ${streamPath}
658
+ live: sse
659
+ waitForUpToDate: true
660
+ timeoutMs: 10000
661
+ expect:
662
+ minChunks: 1
663
+ upToDate: true