@durable-streams/client-conformance-tests 0.1.8 → 0.2.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.
- 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
|
@@ -110,8 +110,8 @@ interface ReadCommand {
|
|
|
110
110
|
path: string;
|
|
111
111
|
/** Starting offset (opaque string from previous reads) */
|
|
112
112
|
offset?: string;
|
|
113
|
-
/** Live mode: false for catch-up only, "long-poll" or "sse" for
|
|
114
|
-
live?: false | `long-poll` | `sse`;
|
|
113
|
+
/** Live mode: false for catch-up only, true for auto-select, "long-poll" or "sse" for explicit */
|
|
114
|
+
live?: false | true | `long-poll` | `sse`;
|
|
115
115
|
/** Timeout for long-poll in milliseconds */
|
|
116
116
|
timeoutMs?: number;
|
|
117
117
|
/** Maximum number of chunks to read (for testing) */
|
|
@@ -176,6 +176,51 @@ interface ClearDynamicCommand {
|
|
|
176
176
|
type: `clear-dynamic`;
|
|
177
177
|
}
|
|
178
178
|
/**
|
|
179
|
+
* Test client-side input validation.
|
|
180
|
+
*
|
|
181
|
+
* This command tests that the client properly validates input parameters
|
|
182
|
+
* before making any network requests. The adapter should attempt to create
|
|
183
|
+
* the specified object with the given parameters and report whether
|
|
184
|
+
* validation passed or failed.
|
|
185
|
+
*/
|
|
186
|
+
interface ValidateCommand {
|
|
187
|
+
type: `validate`;
|
|
188
|
+
/** What to validate */
|
|
189
|
+
target: ValidateTarget;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Validation targets - what client-side validation to test.
|
|
193
|
+
*/
|
|
194
|
+
type ValidateTarget = ValidateRetryOptions | ValidateIdempotentProducer;
|
|
195
|
+
/**
|
|
196
|
+
* Validate RetryOptions construction.
|
|
197
|
+
*/
|
|
198
|
+
interface ValidateRetryOptions {
|
|
199
|
+
target: `retry-options`;
|
|
200
|
+
/** Max retries (should reject < 0) */
|
|
201
|
+
maxRetries?: number;
|
|
202
|
+
/** Initial delay in ms (should reject <= 0) */
|
|
203
|
+
initialDelayMs?: number;
|
|
204
|
+
/** Max delay in ms (should reject < initialDelayMs) */
|
|
205
|
+
maxDelayMs?: number;
|
|
206
|
+
/** Backoff multiplier (should reject < 1.0) */
|
|
207
|
+
multiplier?: number;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Validate IdempotentProducer construction.
|
|
211
|
+
*/
|
|
212
|
+
interface ValidateIdempotentProducer {
|
|
213
|
+
target: `idempotent-producer`;
|
|
214
|
+
/** Producer ID (required, non-empty) */
|
|
215
|
+
producerId?: string;
|
|
216
|
+
/** Starting epoch (should reject < 0) */
|
|
217
|
+
epoch?: number;
|
|
218
|
+
/** Max batch bytes (should reject <= 0) */
|
|
219
|
+
maxBatchBytes?: number;
|
|
220
|
+
/** Max batch items (should reject <= 0) */
|
|
221
|
+
maxBatchItems?: number;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
179
224
|
* Execute a timed benchmark operation.
|
|
180
225
|
* The adapter times the operation internally using high-resolution timing.
|
|
181
226
|
*/
|
|
@@ -235,7 +280,7 @@ interface BenchmarkThroughputReadOp {
|
|
|
235
280
|
/**
|
|
236
281
|
* All possible commands from test runner to client.
|
|
237
282
|
*/
|
|
238
|
-
type TestCommand = InitCommand | CreateCommand | ConnectCommand | AppendCommand | IdempotentAppendCommand | IdempotentAppendBatchCommand | ReadCommand | HeadCommand | DeleteCommand | ShutdownCommand | SetDynamicHeaderCommand | SetDynamicParamCommand | ClearDynamicCommand | BenchmarkCommand;
|
|
283
|
+
type TestCommand = InitCommand | CreateCommand | ConnectCommand | AppendCommand | IdempotentAppendCommand | IdempotentAppendBatchCommand | ReadCommand | HeadCommand | DeleteCommand | ShutdownCommand | SetDynamicHeaderCommand | SetDynamicParamCommand | ClearDynamicCommand | BenchmarkCommand | ValidateCommand;
|
|
239
284
|
/**
|
|
240
285
|
* Successful initialization result.
|
|
241
286
|
*/
|
|
@@ -254,10 +299,18 @@ interface InitResult {
|
|
|
254
299
|
sse?: boolean;
|
|
255
300
|
/** Supports long-poll mode */
|
|
256
301
|
longPoll?: boolean;
|
|
302
|
+
/** Supports auto mode (catch-up then auto-select SSE or long-poll) */
|
|
303
|
+
auto?: boolean;
|
|
257
304
|
/** Supports streaming reads */
|
|
258
305
|
streaming?: boolean;
|
|
259
306
|
/** Supports dynamic headers/params (functions evaluated per-request) */
|
|
260
307
|
dynamicHeaders?: boolean;
|
|
308
|
+
/** Supports RetryOptions validation (PHP-specific) */
|
|
309
|
+
retryOptions?: boolean;
|
|
310
|
+
/** Supports maxBatchItems option (PHP-specific) */
|
|
311
|
+
batchItems?: boolean;
|
|
312
|
+
/** Rejects zero values as invalid (vs treating 0 as "use default" like Go) */
|
|
313
|
+
strictZeroValidation?: boolean;
|
|
261
314
|
};
|
|
262
315
|
}
|
|
263
316
|
/**
|
|
@@ -421,6 +474,13 @@ interface ClearDynamicResult {
|
|
|
421
474
|
success: true;
|
|
422
475
|
}
|
|
423
476
|
/**
|
|
477
|
+
* Successful validate result (validation passed).
|
|
478
|
+
*/
|
|
479
|
+
interface ValidateResult {
|
|
480
|
+
type: `validate`;
|
|
481
|
+
success: true;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
424
484
|
* Successful benchmark result with timing.
|
|
425
485
|
*/
|
|
426
486
|
interface BenchmarkResult {
|
|
@@ -461,7 +521,7 @@ interface ErrorResult {
|
|
|
461
521
|
/**
|
|
462
522
|
* All possible results from client to test runner.
|
|
463
523
|
*/
|
|
464
|
-
type TestResult = InitResult | CreateResult | ConnectResult | AppendResult | IdempotentAppendResult | IdempotentAppendBatchResult | ReadResult | HeadResult | DeleteResult | ShutdownResult | SetDynamicHeaderResult | SetDynamicParamResult | ClearDynamicResult | BenchmarkResult | ErrorResult;
|
|
524
|
+
type TestResult = InitResult | CreateResult | ConnectResult | AppendResult | IdempotentAppendResult | IdempotentAppendBatchResult | ReadResult | HeadResult | DeleteResult | ShutdownResult | SetDynamicHeaderResult | SetDynamicParamResult | ClearDynamicResult | ValidateResult | BenchmarkResult | ErrorResult;
|
|
465
525
|
/**
|
|
466
526
|
* Parse a JSON line into a TestCommand.
|
|
467
527
|
*/
|
|
@@ -510,6 +570,8 @@ declare const ErrorCodes: {
|
|
|
510
570
|
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
511
571
|
/** Operation not supported by this client */
|
|
512
572
|
readonly NOT_SUPPORTED: "NOT_SUPPORTED";
|
|
573
|
+
/** Invalid argument passed to client API */
|
|
574
|
+
readonly INVALID_ARGUMENT: "INVALID_ARGUMENT";
|
|
513
575
|
};
|
|
514
576
|
type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
|
515
577
|
/**
|
|
@@ -545,4 +607,4 @@ declare function calculateStats(durationsNs: Array<bigint>): BenchmarkStats;
|
|
|
545
607
|
* Format a BenchmarkStats object for display.
|
|
546
608
|
*/
|
|
547
609
|
declare function formatStats(stats: BenchmarkStats, unit?: string): Record<string, string>; //#endregion
|
|
548
|
-
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult };
|
|
610
|
+
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult };
|
|
@@ -50,7 +50,8 @@ const ErrorCodes = {
|
|
|
50
50
|
UNEXPECTED_STATUS: `UNEXPECTED_STATUS`,
|
|
51
51
|
PARSE_ERROR: `PARSE_ERROR`,
|
|
52
52
|
INTERNAL_ERROR: `INTERNAL_ERROR`,
|
|
53
|
-
NOT_SUPPORTED: `NOT_SUPPORTED
|
|
53
|
+
NOT_SUPPORTED: `NOT_SUPPORTED`,
|
|
54
|
+
INVALID_ARGUMENT: `INVALID_ARGUMENT`
|
|
54
55
|
};
|
|
55
56
|
/**
|
|
56
57
|
* Calculate statistics from an array of durations in nanoseconds.
|
|
@@ -110,8 +110,8 @@ interface ReadCommand {
|
|
|
110
110
|
path: string;
|
|
111
111
|
/** Starting offset (opaque string from previous reads) */
|
|
112
112
|
offset?: string;
|
|
113
|
-
/** Live mode: false for catch-up only, "long-poll" or "sse" for
|
|
114
|
-
live?: false | `long-poll` | `sse`;
|
|
113
|
+
/** Live mode: false for catch-up only, true for auto-select, "long-poll" or "sse" for explicit */
|
|
114
|
+
live?: false | true | `long-poll` | `sse`;
|
|
115
115
|
/** Timeout for long-poll in milliseconds */
|
|
116
116
|
timeoutMs?: number;
|
|
117
117
|
/** Maximum number of chunks to read (for testing) */
|
|
@@ -176,6 +176,51 @@ interface ClearDynamicCommand {
|
|
|
176
176
|
type: `clear-dynamic`;
|
|
177
177
|
}
|
|
178
178
|
/**
|
|
179
|
+
* Test client-side input validation.
|
|
180
|
+
*
|
|
181
|
+
* This command tests that the client properly validates input parameters
|
|
182
|
+
* before making any network requests. The adapter should attempt to create
|
|
183
|
+
* the specified object with the given parameters and report whether
|
|
184
|
+
* validation passed or failed.
|
|
185
|
+
*/
|
|
186
|
+
interface ValidateCommand {
|
|
187
|
+
type: `validate`;
|
|
188
|
+
/** What to validate */
|
|
189
|
+
target: ValidateTarget;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Validation targets - what client-side validation to test.
|
|
193
|
+
*/
|
|
194
|
+
type ValidateTarget = ValidateRetryOptions | ValidateIdempotentProducer;
|
|
195
|
+
/**
|
|
196
|
+
* Validate RetryOptions construction.
|
|
197
|
+
*/
|
|
198
|
+
interface ValidateRetryOptions {
|
|
199
|
+
target: `retry-options`;
|
|
200
|
+
/** Max retries (should reject < 0) */
|
|
201
|
+
maxRetries?: number;
|
|
202
|
+
/** Initial delay in ms (should reject <= 0) */
|
|
203
|
+
initialDelayMs?: number;
|
|
204
|
+
/** Max delay in ms (should reject < initialDelayMs) */
|
|
205
|
+
maxDelayMs?: number;
|
|
206
|
+
/** Backoff multiplier (should reject < 1.0) */
|
|
207
|
+
multiplier?: number;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Validate IdempotentProducer construction.
|
|
211
|
+
*/
|
|
212
|
+
interface ValidateIdempotentProducer {
|
|
213
|
+
target: `idempotent-producer`;
|
|
214
|
+
/** Producer ID (required, non-empty) */
|
|
215
|
+
producerId?: string;
|
|
216
|
+
/** Starting epoch (should reject < 0) */
|
|
217
|
+
epoch?: number;
|
|
218
|
+
/** Max batch bytes (should reject <= 0) */
|
|
219
|
+
maxBatchBytes?: number;
|
|
220
|
+
/** Max batch items (should reject <= 0) */
|
|
221
|
+
maxBatchItems?: number;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
179
224
|
* Execute a timed benchmark operation.
|
|
180
225
|
* The adapter times the operation internally using high-resolution timing.
|
|
181
226
|
*/
|
|
@@ -235,7 +280,7 @@ interface BenchmarkThroughputReadOp {
|
|
|
235
280
|
/**
|
|
236
281
|
* All possible commands from test runner to client.
|
|
237
282
|
*/
|
|
238
|
-
type TestCommand = InitCommand | CreateCommand | ConnectCommand | AppendCommand | IdempotentAppendCommand | IdempotentAppendBatchCommand | ReadCommand | HeadCommand | DeleteCommand | ShutdownCommand | SetDynamicHeaderCommand | SetDynamicParamCommand | ClearDynamicCommand | BenchmarkCommand;
|
|
283
|
+
type TestCommand = InitCommand | CreateCommand | ConnectCommand | AppendCommand | IdempotentAppendCommand | IdempotentAppendBatchCommand | ReadCommand | HeadCommand | DeleteCommand | ShutdownCommand | SetDynamicHeaderCommand | SetDynamicParamCommand | ClearDynamicCommand | BenchmarkCommand | ValidateCommand;
|
|
239
284
|
/**
|
|
240
285
|
* Successful initialization result.
|
|
241
286
|
*/
|
|
@@ -254,10 +299,18 @@ interface InitResult {
|
|
|
254
299
|
sse?: boolean;
|
|
255
300
|
/** Supports long-poll mode */
|
|
256
301
|
longPoll?: boolean;
|
|
302
|
+
/** Supports auto mode (catch-up then auto-select SSE or long-poll) */
|
|
303
|
+
auto?: boolean;
|
|
257
304
|
/** Supports streaming reads */
|
|
258
305
|
streaming?: boolean;
|
|
259
306
|
/** Supports dynamic headers/params (functions evaluated per-request) */
|
|
260
307
|
dynamicHeaders?: boolean;
|
|
308
|
+
/** Supports RetryOptions validation (PHP-specific) */
|
|
309
|
+
retryOptions?: boolean;
|
|
310
|
+
/** Supports maxBatchItems option (PHP-specific) */
|
|
311
|
+
batchItems?: boolean;
|
|
312
|
+
/** Rejects zero values as invalid (vs treating 0 as "use default" like Go) */
|
|
313
|
+
strictZeroValidation?: boolean;
|
|
261
314
|
};
|
|
262
315
|
}
|
|
263
316
|
/**
|
|
@@ -421,6 +474,13 @@ interface ClearDynamicResult {
|
|
|
421
474
|
success: true;
|
|
422
475
|
}
|
|
423
476
|
/**
|
|
477
|
+
* Successful validate result (validation passed).
|
|
478
|
+
*/
|
|
479
|
+
interface ValidateResult {
|
|
480
|
+
type: `validate`;
|
|
481
|
+
success: true;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
424
484
|
* Successful benchmark result with timing.
|
|
425
485
|
*/
|
|
426
486
|
interface BenchmarkResult {
|
|
@@ -461,7 +521,7 @@ interface ErrorResult {
|
|
|
461
521
|
/**
|
|
462
522
|
* All possible results from client to test runner.
|
|
463
523
|
*/
|
|
464
|
-
type TestResult = InitResult | CreateResult | ConnectResult | AppendResult | IdempotentAppendResult | IdempotentAppendBatchResult | ReadResult | HeadResult | DeleteResult | ShutdownResult | SetDynamicHeaderResult | SetDynamicParamResult | ClearDynamicResult | BenchmarkResult | ErrorResult;
|
|
524
|
+
type TestResult = InitResult | CreateResult | ConnectResult | AppendResult | IdempotentAppendResult | IdempotentAppendBatchResult | ReadResult | HeadResult | DeleteResult | ShutdownResult | SetDynamicHeaderResult | SetDynamicParamResult | ClearDynamicResult | ValidateResult | BenchmarkResult | ErrorResult;
|
|
465
525
|
/**
|
|
466
526
|
* Parse a JSON line into a TestCommand.
|
|
467
527
|
*/
|
|
@@ -510,6 +570,8 @@ declare const ErrorCodes: {
|
|
|
510
570
|
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
511
571
|
/** Operation not supported by this client */
|
|
512
572
|
readonly NOT_SUPPORTED: "NOT_SUPPORTED";
|
|
573
|
+
/** Invalid argument passed to client API */
|
|
574
|
+
readonly INVALID_ARGUMENT: "INVALID_ARGUMENT";
|
|
513
575
|
};
|
|
514
576
|
type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
|
515
577
|
/**
|
|
@@ -545,4 +607,4 @@ declare function calculateStats(durationsNs: Array<bigint>): BenchmarkStats;
|
|
|
545
607
|
* Format a BenchmarkStats object for display.
|
|
546
608
|
*/
|
|
547
609
|
declare function formatStats(stats: BenchmarkStats, unit?: string): Record<string, string>; //#endregion
|
|
548
|
-
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes as ErrorCodes$1, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats as calculateStats$1, decodeBase64 as decodeBase64$1, encodeBase64 as encodeBase64$1, formatStats as formatStats$1, parseCommand as parseCommand$1, parseResult as parseResult$1, serializeCommand as serializeCommand$1, serializeResult as serializeResult$1 };
|
|
610
|
+
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes as ErrorCodes$1, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats as calculateStats$1, decodeBase64 as decodeBase64$1, encodeBase64 as encodeBase64$1, formatStats as formatStats$1, parseCommand as parseCommand$1, parseResult as parseResult$1, serializeCommand as serializeCommand$1, serializeResult as serializeResult$1 };
|
package/dist/protocol.cjs
CHANGED
package/dist/protocol.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-
|
|
2
|
-
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult };
|
|
1
|
+
import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-BxZTqJmO.cjs";
|
|
2
|
+
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult };
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes$1 as ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats$1 as calculateStats, decodeBase64$1 as decodeBase64, encodeBase64$1 as encodeBase64, formatStats$1 as formatStats, parseCommand$1 as parseCommand, parseResult$1 as parseResult, serializeCommand$1 as serializeCommand, serializeResult$1 as serializeResult } from "./protocol-
|
|
2
|
-
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult };
|
|
1
|
+
import { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes$1 as ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats$1 as calculateStats, decodeBase64$1 as decodeBase64, encodeBase64$1 as encodeBase64, formatStats$1 as formatStats, parseCommand$1 as parseCommand, parseResult$1 as parseResult, serializeCommand$1 as serializeCommand, serializeResult$1 as serializeResult } from "./protocol-JuFzdV5x.js";
|
|
2
|
+
export { AppendCommand, AppendResult, BenchmarkAppendOp, BenchmarkCommand, BenchmarkCreateOp, BenchmarkOperation, BenchmarkReadOp, BenchmarkResult, BenchmarkRoundtripOp, BenchmarkStats, BenchmarkThroughputAppendOp, BenchmarkThroughputReadOp, ClearDynamicCommand, ClearDynamicResult, ConnectCommand, ConnectResult, CreateCommand, CreateResult, DeleteCommand, DeleteResult, ErrorCode, ErrorCodes, ErrorResult, HeadCommand, HeadResult, IdempotentAppendBatchCommand, IdempotentAppendBatchResult, IdempotentAppendCommand, IdempotentAppendResult, InitCommand, InitResult, ReadChunk, ReadCommand, ReadResult, SetDynamicHeaderCommand, SetDynamicHeaderResult, SetDynamicParamCommand, SetDynamicParamResult, ShutdownCommand, ShutdownResult, TestCommand, TestResult, ValidateCommand, ValidateIdempotentProducer, ValidateResult, ValidateRetryOptions, ValidateTarget, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult };
|
package/dist/protocol.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { ErrorCodes, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-
|
|
1
|
+
import { ErrorCodes, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult } from "./protocol-1p0soayz.js";
|
|
2
2
|
|
|
3
3
|
export { ErrorCodes, calculateStats, decodeBase64, encodeBase64, formatStats, parseCommand, parseResult, serializeCommand, serializeResult };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@durable-streams/client-conformance-tests",
|
|
3
3
|
"description": "Conformance test suite for Durable Streams client implementations (producer and consumer)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"author": "Durable Stream contributors",
|
|
6
6
|
"bin": {
|
|
7
7
|
"client-conformance-tests": "./dist/cli.js",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"fast-check": "^4.4.0",
|
|
15
15
|
"tsx": "^4.19.2",
|
|
16
16
|
"yaml": "^2.7.1",
|
|
17
|
-
"@durable-streams/client": "0.
|
|
18
|
-
"@durable-streams/server": "0.
|
|
17
|
+
"@durable-streams/client": "0.2.0",
|
|
18
|
+
"@durable-streams/server": "0.2.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"tsdown": "^0.9.0",
|
|
@@ -73,11 +73,16 @@
|
|
|
73
73
|
"build": "tsdown",
|
|
74
74
|
"dev": "tsdown --watch",
|
|
75
75
|
"test:run": "tsx src/cli.ts --run ts",
|
|
76
|
+
"test:run:elixir": "tsx src/cli.ts --run ../client-elixir/run-conformance-adapter.sh",
|
|
77
|
+
"test:run:elixir:verbose": "tsx src/cli.ts --run ../client-elixir/run-conformance-adapter.sh --verbose",
|
|
76
78
|
"test:run:python": "tsx src/cli.ts --run ../client-py/run-conformance-adapter.sh",
|
|
77
79
|
"test:run:python-async": "tsx src/cli.ts --run ../client-py/run-conformance-adapter-async.sh",
|
|
78
80
|
"test:run:python-async:verbose": "tsx src/cli.ts --run ../client-py/run-conformance-adapter-async.sh --verbose",
|
|
79
81
|
"test:run:python:verbose": "tsx src/cli.ts --run ../client-py/run-conformance-adapter.sh --verbose",
|
|
80
82
|
"test:run:verbose": "tsx src/cli.ts --run ts --verbose",
|
|
83
|
+
"bench:ts": "tsx src/cli.ts --bench ts",
|
|
84
|
+
"bench:elixir": "tsx src/cli.ts --bench ../client-elixir/run-conformance-adapter.sh",
|
|
85
|
+
"bench:python": "tsx src/cli.ts --bench ../client-py/run-conformance-adapter.sh",
|
|
81
86
|
"typecheck": "tsc --noEmit"
|
|
82
87
|
}
|
|
83
88
|
}
|
|
@@ -132,8 +132,10 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
132
132
|
batching: true,
|
|
133
133
|
sse: true,
|
|
134
134
|
longPoll: true,
|
|
135
|
+
auto: true,
|
|
135
136
|
streaming: true,
|
|
136
137
|
dynamicHeaders: true,
|
|
138
|
+
strictZeroValidation: true,
|
|
137
139
|
},
|
|
138
140
|
}
|
|
139
141
|
}
|
|
@@ -268,11 +270,13 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
268
270
|
}
|
|
269
271
|
|
|
270
272
|
// Determine live mode
|
|
271
|
-
let live: `long-poll` | `sse` | false
|
|
273
|
+
let live: true | `long-poll` | `sse` | false
|
|
272
274
|
if (command.live === `long-poll`) {
|
|
273
275
|
live = `long-poll`
|
|
274
276
|
} else if (command.live === `sse`) {
|
|
275
277
|
live = `sse`
|
|
278
|
+
} else if (command.live === true) {
|
|
279
|
+
live = true
|
|
276
280
|
} else {
|
|
277
281
|
live = false
|
|
278
282
|
}
|
|
@@ -326,9 +330,25 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
326
330
|
// Collect chunks using body() for non-live mode or bodyStream() for live
|
|
327
331
|
const maxChunks = command.maxChunks ?? 100
|
|
328
332
|
|
|
333
|
+
// Determine if we should use JSON parsing based on content type
|
|
334
|
+
const contentType = streamContentTypes.get(command.path)
|
|
335
|
+
const isJson = contentType?.includes(`application/json`) ?? false
|
|
336
|
+
|
|
329
337
|
if (!live) {
|
|
330
|
-
// For non-live mode, use body()
|
|
331
|
-
|
|
338
|
+
// For non-live mode, use json() or body() based on content type
|
|
339
|
+
if (isJson) {
|
|
340
|
+
// Use JSON parsing to trigger PARSE_ERROR on malformed JSON
|
|
341
|
+
const items = await response.json()
|
|
342
|
+
if (items.length > 0) {
|
|
343
|
+
// Serialize the items array back to string for the test framework
|
|
344
|
+
// Keep as array format to match what the server returns
|
|
345
|
+
chunks.push({
|
|
346
|
+
data: JSON.stringify(items),
|
|
347
|
+
offset: response.offset,
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
// Use byte reading for non-JSON content
|
|
332
352
|
const data = await response.body()
|
|
333
353
|
if (data.length > 0) {
|
|
334
354
|
chunks.push({
|
|
@@ -336,11 +356,9 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
336
356
|
offset: response.offset,
|
|
337
357
|
})
|
|
338
358
|
}
|
|
339
|
-
finalOffset = response.offset
|
|
340
|
-
upToDate = response.upToDate
|
|
341
|
-
} catch {
|
|
342
|
-
// If body fails, stream might be empty
|
|
343
359
|
}
|
|
360
|
+
finalOffset = response.offset
|
|
361
|
+
upToDate = response.upToDate
|
|
344
362
|
} else {
|
|
345
363
|
// For live mode, use subscribeBytes which provides per-chunk metadata
|
|
346
364
|
const decoder = new TextDecoder()
|
|
@@ -349,7 +367,7 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
349
367
|
let done = false
|
|
350
368
|
|
|
351
369
|
// Create a promise that resolves when we're done collecting chunks
|
|
352
|
-
await new Promise<void>((resolve) => {
|
|
370
|
+
await new Promise<void>((resolve, reject) => {
|
|
353
371
|
// Set up subscription timeout
|
|
354
372
|
const subscriptionTimeoutId = setTimeout(() => {
|
|
355
373
|
done = true
|
|
@@ -420,11 +438,12 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
420
438
|
resolve()
|
|
421
439
|
}
|
|
422
440
|
})
|
|
423
|
-
.catch(() => {
|
|
441
|
+
.catch((err) => {
|
|
424
442
|
if (!done) {
|
|
425
443
|
done = true
|
|
426
444
|
clearTimeout(subscriptionTimeoutId)
|
|
427
|
-
|
|
445
|
+
// Propagate errors (like SSE parse errors) to the outer handler
|
|
446
|
+
reject(err)
|
|
428
447
|
}
|
|
429
448
|
})
|
|
430
449
|
|
|
@@ -564,18 +583,10 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
564
583
|
lingerMs: 0, // Send immediately for testing
|
|
565
584
|
})
|
|
566
585
|
|
|
567
|
-
// For JSON streams, parse the string data into a native object
|
|
568
|
-
// (IdempotentProducer expects native objects for JSON streams)
|
|
569
|
-
const normalizedContentType = contentType
|
|
570
|
-
.split(`;`)[0]
|
|
571
|
-
?.trim()
|
|
572
|
-
.toLowerCase()
|
|
573
|
-
const isJson = normalizedContentType === `application/json`
|
|
574
|
-
const data = isJson ? JSON.parse(command.data) : command.data
|
|
575
|
-
|
|
576
586
|
try {
|
|
577
587
|
// append() is fire-and-forget (synchronous), then flush() sends the batch
|
|
578
|
-
|
|
588
|
+
// Data is already pre-serialized, pass directly to append()
|
|
589
|
+
producer.append(command.data)
|
|
579
590
|
await producer.flush()
|
|
580
591
|
await producer.close()
|
|
581
592
|
|
|
@@ -620,20 +631,10 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
620
631
|
maxBatchBytes: testingConcurrency ? 1 : 1024 * 1024,
|
|
621
632
|
})
|
|
622
633
|
|
|
623
|
-
// For JSON streams, parse string items into native objects
|
|
624
|
-
// (IdempotentProducer expects native objects for JSON streams)
|
|
625
|
-
const normalizedContentType = contentType
|
|
626
|
-
.split(`;`)[0]
|
|
627
|
-
?.trim()
|
|
628
|
-
.toLowerCase()
|
|
629
|
-
const isJson = normalizedContentType === `application/json`
|
|
630
|
-
const items = isJson
|
|
631
|
-
? command.items.map((item: string) => JSON.parse(item))
|
|
632
|
-
: command.items
|
|
633
|
-
|
|
634
634
|
try {
|
|
635
635
|
// append() is fire-and-forget (synchronous), adds to pending batch
|
|
636
|
-
|
|
636
|
+
// Data is already pre-serialized, pass directly to append()
|
|
637
|
+
for (const item of command.items) {
|
|
637
638
|
producer.append(item)
|
|
638
639
|
}
|
|
639
640
|
|
|
@@ -655,6 +656,63 @@ async function handleCommand(command: TestCommand): Promise<TestResult> {
|
|
|
655
656
|
}
|
|
656
657
|
}
|
|
657
658
|
|
|
659
|
+
case `validate`: {
|
|
660
|
+
// Test client-side input validation
|
|
661
|
+
const { target } = command
|
|
662
|
+
|
|
663
|
+
try {
|
|
664
|
+
switch (target.target) {
|
|
665
|
+
case `retry-options`: {
|
|
666
|
+
// TypeScript client doesn't have a separate RetryOptions class
|
|
667
|
+
// The retry options are validated when passed to stream() or IdempotentProducer
|
|
668
|
+
// For now, just return success since TS uses the fetch defaults
|
|
669
|
+
return {
|
|
670
|
+
type: `validate`,
|
|
671
|
+
success: true,
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
case `idempotent-producer`: {
|
|
676
|
+
// Try to create an IdempotentProducer with the given options
|
|
677
|
+
const ds = new DurableStream({
|
|
678
|
+
url: `${serverUrl}/test-validate`,
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
// IdempotentProducer doesn't currently validate constructor params in TS
|
|
682
|
+
|
|
683
|
+
// Creating the producer tests validation - we don't need the instance
|
|
684
|
+
new IdempotentProducer(ds, target.producerId ?? `test-producer`, {
|
|
685
|
+
epoch: target.epoch,
|
|
686
|
+
maxBatchBytes: target.maxBatchBytes,
|
|
687
|
+
})
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
type: `validate`,
|
|
691
|
+
success: true,
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
default:
|
|
696
|
+
return {
|
|
697
|
+
type: `error`,
|
|
698
|
+
success: false,
|
|
699
|
+
commandType: `validate`,
|
|
700
|
+
errorCode: ErrorCodes.NOT_SUPPORTED,
|
|
701
|
+
message: `Unknown validation target: ${(target as { target: string }).target}`,
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
} catch (err) {
|
|
705
|
+
// Validation failed - return error with details
|
|
706
|
+
return {
|
|
707
|
+
type: `error`,
|
|
708
|
+
success: false,
|
|
709
|
+
commandType: `validate`,
|
|
710
|
+
errorCode: ErrorCodes.INVALID_ARGUMENT,
|
|
711
|
+
message: err instanceof Error ? err.message : String(err),
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
658
716
|
default:
|
|
659
717
|
return {
|
|
660
718
|
type: `error`,
|
|
@@ -687,6 +745,8 @@ function errorResult(
|
|
|
687
745
|
} else if (err.code === `BAD_REQUEST`) {
|
|
688
746
|
errorCode = ErrorCodes.INVALID_OFFSET
|
|
689
747
|
status = 400
|
|
748
|
+
} else if (err.code === `PARSE_ERROR`) {
|
|
749
|
+
errorCode = ErrorCodes.PARSE_ERROR
|
|
690
750
|
}
|
|
691
751
|
|
|
692
752
|
return {
|
|
@@ -744,6 +804,24 @@ function errorResult(
|
|
|
744
804
|
}
|
|
745
805
|
}
|
|
746
806
|
|
|
807
|
+
// JSON parsing errors (SyntaxError) or SSE parsing errors
|
|
808
|
+
if (
|
|
809
|
+
err instanceof SyntaxError ||
|
|
810
|
+
err.name === `SyntaxError` ||
|
|
811
|
+
err.message.includes(`JSON`) ||
|
|
812
|
+
err.message.includes(`parse`) ||
|
|
813
|
+
err.message.includes(`SSE`) ||
|
|
814
|
+
err.message.includes(`control event`)
|
|
815
|
+
) {
|
|
816
|
+
return {
|
|
817
|
+
type: `error`,
|
|
818
|
+
success: false,
|
|
819
|
+
commandType,
|
|
820
|
+
errorCode: ErrorCodes.PARSE_ERROR,
|
|
821
|
+
message: err.message,
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
747
825
|
return {
|
|
748
826
|
type: `error`,
|
|
749
827
|
success: false,
|
package/src/benchmark-runner.ts
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
import { spawn } from "node:child_process"
|
|
12
12
|
import { createInterface } from "node:readline"
|
|
13
13
|
import { randomUUID } from "node:crypto"
|
|
14
|
+
import { readFile, readdir } from "node:fs/promises"
|
|
15
|
+
import { join } from "node:path"
|
|
14
16
|
import { DurableStreamTestServer } from "@durable-streams/server"
|
|
15
17
|
import { DurableStream } from "@durable-streams/client"
|
|
16
18
|
import {
|
|
@@ -316,7 +318,7 @@ async function runScenario(
|
|
|
316
318
|
messages.push({ n: i, data: `message-${i}-padding-for-size` })
|
|
317
319
|
}
|
|
318
320
|
// Batch append for speed
|
|
319
|
-
await Promise.all(messages.map((msg) => ds.append(msg)))
|
|
321
|
+
await Promise.all(messages.map((msg) => ds.append(JSON.stringify(msg))))
|
|
320
322
|
}
|
|
321
323
|
}
|
|
322
324
|
|
|
@@ -858,3 +860,75 @@ export async function runBenchmarks(
|
|
|
858
860
|
}
|
|
859
861
|
|
|
860
862
|
export { allScenarios, getScenarioById }
|
|
863
|
+
|
|
864
|
+
// =============================================================================
|
|
865
|
+
// Aggregate Benchmark Results from JSON Files
|
|
866
|
+
// =============================================================================
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* Reads benchmark JSON files from a directory and generates a combined markdown report.
|
|
870
|
+
* Each subdirectory should contain a benchmark-results.json file.
|
|
871
|
+
*/
|
|
872
|
+
export async function aggregateBenchmarkResults(
|
|
873
|
+
resultsDir: string
|
|
874
|
+
): Promise<string> {
|
|
875
|
+
const entries = await readdir(resultsDir, { withFileTypes: true })
|
|
876
|
+
const summaries: Array<BenchmarkSummary> = []
|
|
877
|
+
|
|
878
|
+
for (const entry of entries) {
|
|
879
|
+
if (!entry.isDirectory()) continue
|
|
880
|
+
|
|
881
|
+
const jsonPath = join(resultsDir, entry.name, `benchmark-results.json`)
|
|
882
|
+
try {
|
|
883
|
+
const content = await readFile(jsonPath, `utf-8`)
|
|
884
|
+
const summary = JSON.parse(content) as BenchmarkSummary
|
|
885
|
+
// Reconstruct scenario objects from serialized data
|
|
886
|
+
summary.results = summary.results.map((r) => ({
|
|
887
|
+
...r,
|
|
888
|
+
scenario: r.scenario,
|
|
889
|
+
}))
|
|
890
|
+
summaries.push(summary)
|
|
891
|
+
} catch {
|
|
892
|
+
// Skip directories without valid benchmark results
|
|
893
|
+
console.error(`Skipping ${entry.name}: no valid benchmark-results.json`)
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (summaries.length === 0) {
|
|
898
|
+
return `# Client Benchmark Results\n\nNo benchmark results found.\n`
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Sort summaries by adapter name for consistent output
|
|
902
|
+
summaries.sort((a, b) => a.adapter.localeCompare(b.adapter))
|
|
903
|
+
|
|
904
|
+
// Generate combined report
|
|
905
|
+
const lines: Array<string> = []
|
|
906
|
+
lines.push(`# Client Benchmark Results`)
|
|
907
|
+
lines.push(``)
|
|
908
|
+
|
|
909
|
+
// Summary table
|
|
910
|
+
const totalPassed = summaries.reduce((sum, s) => sum + s.passed, 0)
|
|
911
|
+
const totalFailed = summaries.reduce((sum, s) => sum + s.failed, 0)
|
|
912
|
+
const totalSkipped = summaries.reduce((sum, s) => sum + s.skipped, 0)
|
|
913
|
+
|
|
914
|
+
lines.push(`| Client | Passed | Failed | Skipped | Status |`)
|
|
915
|
+
lines.push(`|--------|--------|--------|---------|--------|`)
|
|
916
|
+
for (const summary of summaries) {
|
|
917
|
+
const status = summary.failed === 0 ? `✓` : `✗`
|
|
918
|
+
lines.push(
|
|
919
|
+
`| ${summary.adapter} | ${summary.passed} | ${summary.failed} | ${summary.skipped} | ${status} |`
|
|
920
|
+
)
|
|
921
|
+
}
|
|
922
|
+
lines.push(
|
|
923
|
+
`| **Total** | **${totalPassed}** | **${totalFailed}** | **${totalSkipped}** | ${totalFailed === 0 ? `✓` : `✗`} |`
|
|
924
|
+
)
|
|
925
|
+
lines.push(``)
|
|
926
|
+
|
|
927
|
+
// Individual client details
|
|
928
|
+
for (const summary of summaries) {
|
|
929
|
+
lines.push(generateMarkdownReport(summary))
|
|
930
|
+
lines.push(``)
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
return lines.join(`\n`)
|
|
934
|
+
}
|