@streetjs/storage 1.0.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/LICENSE +21 -0
- package/README.md +719 -0
- package/dist/access.d.ts +95 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +114 -0
- package/dist/access.js.map +1 -0
- package/dist/cli/commands.d.ts +88 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +369 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/generators.d.ts +41 -0
- package/dist/cli/generators.d.ts.map +1 -0
- package/dist/cli/generators.js +119 -0
- package/dist/cli/generators.js.map +1 -0
- package/dist/directory.d.ts +88 -0
- package/dist/directory.d.ts.map +1 -0
- package/dist/directory.js +148 -0
- package/dist/directory.js.map +1 -0
- package/dist/driver.d.ts +83 -0
- package/dist/driver.d.ts.map +1 -0
- package/dist/driver.js +16 -0
- package/dist/driver.js.map +1 -0
- package/dist/drivers/azure.d.ts +239 -0
- package/dist/drivers/azure.d.ts.map +1 -0
- package/dist/drivers/azure.js +476 -0
- package/dist/drivers/azure.js.map +1 -0
- package/dist/drivers/backblaze.d.ts +105 -0
- package/dist/drivers/backblaze.d.ts.map +1 -0
- package/dist/drivers/backblaze.js +279 -0
- package/dist/drivers/backblaze.js.map +1 -0
- package/dist/drivers/gcs.d.ts +239 -0
- package/dist/drivers/gcs.d.ts.map +1 -0
- package/dist/drivers/gcs.js +454 -0
- package/dist/drivers/gcs.js.map +1 -0
- package/dist/drivers/local.d.ts +126 -0
- package/dist/drivers/local.d.ts.map +1 -0
- package/dist/drivers/local.js +321 -0
- package/dist/drivers/local.js.map +1 -0
- package/dist/drivers/memory.d.ts +104 -0
- package/dist/drivers/memory.d.ts.map +1 -0
- package/dist/drivers/memory.js +197 -0
- package/dist/drivers/memory.js.map +1 -0
- package/dist/drivers/minio.d.ts +90 -0
- package/dist/drivers/minio.d.ts.map +1 -0
- package/dist/drivers/minio.js +276 -0
- package/dist/drivers/minio.js.map +1 -0
- package/dist/drivers/r2.d.ts +100 -0
- package/dist/drivers/r2.d.ts.map +1 -0
- package/dist/drivers/r2.js +240 -0
- package/dist/drivers/r2.js.map +1 -0
- package/dist/drivers/s3-base.d.ts +270 -0
- package/dist/drivers/s3-base.d.ts.map +1 -0
- package/dist/drivers/s3-base.js +436 -0
- package/dist/drivers/s3-base.js.map +1 -0
- package/dist/drivers/s3.d.ts +111 -0
- package/dist/drivers/s3.d.ts.map +1 -0
- package/dist/drivers/s3.js +311 -0
- package/dist/drivers/s3.js.map +1 -0
- package/dist/drivers/supabase.d.ts +246 -0
- package/dist/drivers/supabase.d.ts.map +1 -0
- package/dist/drivers/supabase.js +502 -0
- package/dist/drivers/supabase.js.map +1 -0
- package/dist/errors.d.ts +115 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +131 -0
- package/dist/errors.js.map +1 -0
- package/dist/examples/storage-demo.d.ts +53 -0
- package/dist/examples/storage-demo.d.ts.map +1 -0
- package/dist/examples/storage-demo.js +242 -0
- package/dist/examples/storage-demo.js.map +1 -0
- package/dist/facade.d.ts +192 -0
- package/dist/facade.d.ts.map +1 -0
- package/dist/facade.js +898 -0
- package/dist/facade.js.map +1 -0
- package/dist/image.d.ts +94 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +224 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/events.d.ts +108 -0
- package/dist/integrations/events.d.ts.map +1 -0
- package/dist/integrations/events.js +111 -0
- package/dist/integrations/events.js.map +1 -0
- package/dist/integrations/queue.d.ts +89 -0
- package/dist/integrations/queue.d.ts.map +1 -0
- package/dist/integrations/queue.js +91 -0
- package/dist/integrations/queue.js.map +1 -0
- package/dist/integrations/realtime.d.ts +95 -0
- package/dist/integrations/realtime.d.ts.map +1 -0
- package/dist/integrations/realtime.js +90 -0
- package/dist/integrations/realtime.js.map +1 -0
- package/dist/lifecycle.d.ts +137 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +240 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/metadata.d.ts +109 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +139 -0
- package/dist/metadata.js.map +1 -0
- package/dist/multipart.d.ts +108 -0
- package/dist/multipart.d.ts.map +1 -0
- package/dist/multipart.js +200 -0
- package/dist/multipart.js.map +1 -0
- package/dist/observability.d.ts +82 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +175 -0
- package/dist/observability.js.map +1 -0
- package/dist/plugin.d.ts +82 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +100 -0
- package/dist/plugin.js.map +1 -0
- package/dist/resumable.d.ts +118 -0
- package/dist/resumable.d.ts.map +1 -0
- package/dist/resumable.js +223 -0
- package/dist/resumable.js.map +1 -0
- package/dist/search.d.ts +57 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +172 -0
- package/dist/search.js.map +1 -0
- package/dist/signed-url.d.ts +110 -0
- package/dist/signed-url.d.ts.map +1 -0
- package/dist/signed-url.js +237 -0
- package/dist/signed-url.js.map +1 -0
- package/dist/testing/index.d.ts +219 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +393 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tests/access.test.d.ts +2 -0
- package/dist/tests/access.test.d.ts.map +1 -0
- package/dist/tests/access.test.js +270 -0
- package/dist/tests/access.test.js.map +1 -0
- package/dist/tests/azure-driver.test.d.ts +2 -0
- package/dist/tests/azure-driver.test.d.ts.map +1 -0
- package/dist/tests/azure-driver.test.js +183 -0
- package/dist/tests/azure-driver.test.js.map +1 -0
- package/dist/tests/backblaze-driver.test.d.ts +2 -0
- package/dist/tests/backblaze-driver.test.d.ts.map +1 -0
- package/dist/tests/backblaze-driver.test.js +185 -0
- package/dist/tests/backblaze-driver.test.js.map +1 -0
- package/dist/tests/bridge-degradation.test.d.ts +2 -0
- package/dist/tests/bridge-degradation.test.d.ts.map +1 -0
- package/dist/tests/bridge-degradation.test.js +138 -0
- package/dist/tests/bridge-degradation.test.js.map +1 -0
- package/dist/tests/cli.test.d.ts +2 -0
- package/dist/tests/cli.test.d.ts.map +1 -0
- package/dist/tests/cli.test.js +127 -0
- package/dist/tests/cli.test.js.map +1 -0
- package/dist/tests/contract.d.ts +131 -0
- package/dist/tests/contract.d.ts.map +1 -0
- package/dist/tests/contract.js +222 -0
- package/dist/tests/contract.js.map +1 -0
- package/dist/tests/contract.test.d.ts +2 -0
- package/dist/tests/contract.test.d.ts.map +1 -0
- package/dist/tests/contract.test.js +110 -0
- package/dist/tests/contract.test.js.map +1 -0
- package/dist/tests/directory.test.d.ts +2 -0
- package/dist/tests/directory.test.d.ts.map +1 -0
- package/dist/tests/directory.test.js +125 -0
- package/dist/tests/directory.test.js.map +1 -0
- package/dist/tests/errors.test.d.ts +2 -0
- package/dist/tests/errors.test.d.ts.map +1 -0
- package/dist/tests/errors.test.js +93 -0
- package/dist/tests/errors.test.js.map +1 -0
- package/dist/tests/events-bridge.test.d.ts +2 -0
- package/dist/tests/events-bridge.test.d.ts.map +1 -0
- package/dist/tests/events-bridge.test.js +228 -0
- package/dist/tests/events-bridge.test.js.map +1 -0
- package/dist/tests/example-smoke.test.d.ts +2 -0
- package/dist/tests/example-smoke.test.d.ts.map +1 -0
- package/dist/tests/example-smoke.test.js +34 -0
- package/dist/tests/example-smoke.test.js.map +1 -0
- package/dist/tests/gcs-driver.test.d.ts +2 -0
- package/dist/tests/gcs-driver.test.d.ts.map +1 -0
- package/dist/tests/gcs-driver.test.js +197 -0
- package/dist/tests/gcs-driver.test.js.map +1 -0
- package/dist/tests/image.test.d.ts +2 -0
- package/dist/tests/image.test.d.ts.map +1 -0
- package/dist/tests/image.test.js +102 -0
- package/dist/tests/image.test.js.map +1 -0
- package/dist/tests/lifecycle.test.d.ts +2 -0
- package/dist/tests/lifecycle.test.d.ts.map +1 -0
- package/dist/tests/lifecycle.test.js +171 -0
- package/dist/tests/lifecycle.test.js.map +1 -0
- package/dist/tests/local-driver.test.d.ts +2 -0
- package/dist/tests/local-driver.test.d.ts.map +1 -0
- package/dist/tests/local-driver.test.js +153 -0
- package/dist/tests/local-driver.test.js.map +1 -0
- package/dist/tests/local-streaming.test.d.ts +2 -0
- package/dist/tests/local-streaming.test.d.ts.map +1 -0
- package/dist/tests/local-streaming.test.js +121 -0
- package/dist/tests/local-streaming.test.js.map +1 -0
- package/dist/tests/memory-driver.test.d.ts +2 -0
- package/dist/tests/memory-driver.test.d.ts.map +1 -0
- package/dist/tests/memory-driver.test.js +180 -0
- package/dist/tests/memory-driver.test.js.map +1 -0
- package/dist/tests/memory-streaming.test.d.ts +2 -0
- package/dist/tests/memory-streaming.test.d.ts.map +1 -0
- package/dist/tests/memory-streaming.test.js +92 -0
- package/dist/tests/memory-streaming.test.js.map +1 -0
- package/dist/tests/minio-driver.test.d.ts +2 -0
- package/dist/tests/minio-driver.test.d.ts.map +1 -0
- package/dist/tests/minio-driver.test.js +146 -0
- package/dist/tests/minio-driver.test.js.map +1 -0
- package/dist/tests/non-s3-integration.test.d.ts +2 -0
- package/dist/tests/non-s3-integration.test.d.ts.map +1 -0
- package/dist/tests/non-s3-integration.test.js +201 -0
- package/dist/tests/non-s3-integration.test.js.map +1 -0
- package/dist/tests/observability.registration.test.d.ts +2 -0
- package/dist/tests/observability.registration.test.d.ts.map +1 -0
- package/dist/tests/observability.registration.test.js +103 -0
- package/dist/tests/observability.registration.test.js.map +1 -0
- package/dist/tests/observability.test.d.ts +2 -0
- package/dist/tests/observability.test.d.ts.map +1 -0
- package/dist/tests/observability.test.js +240 -0
- package/dist/tests/observability.test.js.map +1 -0
- package/dist/tests/plugin.test.d.ts +2 -0
- package/dist/tests/plugin.test.d.ts.map +1 -0
- package/dist/tests/plugin.test.js +112 -0
- package/dist/tests/plugin.test.js.map +1 -0
- package/dist/tests/prop-concurrent.test.d.ts +2 -0
- package/dist/tests/prop-concurrent.test.d.ts.map +1 -0
- package/dist/tests/prop-concurrent.test.js +59 -0
- package/dist/tests/prop-concurrent.test.js.map +1 -0
- package/dist/tests/prop-copy.test.d.ts +2 -0
- package/dist/tests/prop-copy.test.d.ts.map +1 -0
- package/dist/tests/prop-copy.test.js +63 -0
- package/dist/tests/prop-copy.test.js.map +1 -0
- package/dist/tests/prop-delete.test.d.ts +2 -0
- package/dist/tests/prop-delete.test.d.ts.map +1 -0
- package/dist/tests/prop-delete.test.js +35 -0
- package/dist/tests/prop-delete.test.js.map +1 -0
- package/dist/tests/prop-directory.test.d.ts +2 -0
- package/dist/tests/prop-directory.test.d.ts.map +1 -0
- package/dist/tests/prop-directory.test.js +118 -0
- package/dist/tests/prop-directory.test.js.map +1 -0
- package/dist/tests/prop-driver-conformance.test.d.ts +2 -0
- package/dist/tests/prop-driver-conformance.test.d.ts.map +1 -0
- package/dist/tests/prop-driver-conformance.test.js +110 -0
- package/dist/tests/prop-driver-conformance.test.js.map +1 -0
- package/dist/tests/prop-driver-equivalence.test.d.ts +2 -0
- package/dist/tests/prop-driver-equivalence.test.d.ts.map +1 -0
- package/dist/tests/prop-driver-equivalence.test.js +163 -0
- package/dist/tests/prop-driver-equivalence.test.js.map +1 -0
- package/dist/tests/prop-lifecycle.test.d.ts +2 -0
- package/dist/tests/prop-lifecycle.test.d.ts.map +1 -0
- package/dist/tests/prop-lifecycle.test.js +83 -0
- package/dist/tests/prop-lifecycle.test.js.map +1 -0
- package/dist/tests/prop-list.test.d.ts +2 -0
- package/dist/tests/prop-list.test.d.ts.map +1 -0
- package/dist/tests/prop-list.test.js +53 -0
- package/dist/tests/prop-list.test.js.map +1 -0
- package/dist/tests/prop-metadata.test.d.ts +2 -0
- package/dist/tests/prop-metadata.test.d.ts.map +1 -0
- package/dist/tests/prop-metadata.test.js +87 -0
- package/dist/tests/prop-metadata.test.js.map +1 -0
- package/dist/tests/prop-move.test.d.ts +2 -0
- package/dist/tests/prop-move.test.d.ts.map +1 -0
- package/dist/tests/prop-move.test.js +67 -0
- package/dist/tests/prop-move.test.js.map +1 -0
- package/dist/tests/prop-multipart-abort.test.d.ts +2 -0
- package/dist/tests/prop-multipart-abort.test.d.ts.map +1 -0
- package/dist/tests/prop-multipart-abort.test.js +48 -0
- package/dist/tests/prop-multipart-abort.test.js.map +1 -0
- package/dist/tests/prop-multipart.test.d.ts +2 -0
- package/dist/tests/prop-multipart.test.d.ts.map +1 -0
- package/dist/tests/prop-multipart.test.js +65 -0
- package/dist/tests/prop-multipart.test.js.map +1 -0
- package/dist/tests/prop-resumable.test.d.ts +2 -0
- package/dist/tests/prop-resumable.test.d.ts.map +1 -0
- package/dist/tests/prop-resumable.test.js +78 -0
- package/dist/tests/prop-resumable.test.js.map +1 -0
- package/dist/tests/prop-search.test.d.ts +2 -0
- package/dist/tests/prop-search.test.d.ts.map +1 -0
- package/dist/tests/prop-search.test.js +120 -0
- package/dist/tests/prop-search.test.js.map +1 -0
- package/dist/tests/prop-signed-url.test.d.ts +2 -0
- package/dist/tests/prop-signed-url.test.d.ts.map +1 -0
- package/dist/tests/prop-signed-url.test.js +74 -0
- package/dist/tests/prop-signed-url.test.js.map +1 -0
- package/dist/tests/prop-streaming.test.d.ts +2 -0
- package/dist/tests/prop-streaming.test.d.ts.map +1 -0
- package/dist/tests/prop-streaming.test.js +34 -0
- package/dist/tests/prop-streaming.test.js.map +1 -0
- package/dist/tests/prop-upload-download.test.d.ts +2 -0
- package/dist/tests/prop-upload-download.test.d.ts.map +1 -0
- package/dist/tests/prop-upload-download.test.js +30 -0
- package/dist/tests/prop-upload-download.test.js.map +1 -0
- package/dist/tests/prop-validation.test.d.ts +2 -0
- package/dist/tests/prop-validation.test.d.ts.map +1 -0
- package/dist/tests/prop-validation.test.js +50 -0
- package/dist/tests/prop-validation.test.js.map +1 -0
- package/dist/tests/prop-versioning.test.d.ts +2 -0
- package/dist/tests/prop-versioning.test.d.ts.map +1 -0
- package/dist/tests/prop-versioning.test.js +74 -0
- package/dist/tests/prop-versioning.test.js.map +1 -0
- package/dist/tests/queue-bridge.test.d.ts +2 -0
- package/dist/tests/queue-bridge.test.d.ts.map +1 -0
- package/dist/tests/queue-bridge.test.js +111 -0
- package/dist/tests/queue-bridge.test.js.map +1 -0
- package/dist/tests/r2-driver.test.d.ts +2 -0
- package/dist/tests/r2-driver.test.d.ts.map +1 -0
- package/dist/tests/r2-driver.test.js +143 -0
- package/dist/tests/r2-driver.test.js.map +1 -0
- package/dist/tests/realtime-bridge.test.d.ts +2 -0
- package/dist/tests/realtime-bridge.test.d.ts.map +1 -0
- package/dist/tests/realtime-bridge.test.js +106 -0
- package/dist/tests/realtime-bridge.test.js.map +1 -0
- package/dist/tests/resumable.test.d.ts +2 -0
- package/dist/tests/resumable.test.d.ts.map +1 -0
- package/dist/tests/resumable.test.js +104 -0
- package/dist/tests/resumable.test.js.map +1 -0
- package/dist/tests/s3-base.test.d.ts +2 -0
- package/dist/tests/s3-base.test.d.ts.map +1 -0
- package/dist/tests/s3-base.test.js +289 -0
- package/dist/tests/s3-base.test.js.map +1 -0
- package/dist/tests/s3-integration.test.d.ts +2 -0
- package/dist/tests/s3-integration.test.d.ts.map +1 -0
- package/dist/tests/s3-integration.test.js +270 -0
- package/dist/tests/s3-integration.test.js.map +1 -0
- package/dist/tests/s3.test.d.ts +2 -0
- package/dist/tests/s3.test.d.ts.map +1 -0
- package/dist/tests/s3.test.js +203 -0
- package/dist/tests/s3.test.js.map +1 -0
- package/dist/tests/scaffold.test.d.ts +2 -0
- package/dist/tests/scaffold.test.d.ts.map +1 -0
- package/dist/tests/scaffold.test.js +13 -0
- package/dist/tests/scaffold.test.js.map +1 -0
- package/dist/tests/search.test.d.ts +2 -0
- package/dist/tests/search.test.d.ts.map +1 -0
- package/dist/tests/search.test.js +106 -0
- package/dist/tests/search.test.js.map +1 -0
- package/dist/tests/supabase-driver.test.d.ts +2 -0
- package/dist/tests/supabase-driver.test.d.ts.map +1 -0
- package/dist/tests/supabase-driver.test.js +196 -0
- package/dist/tests/supabase-driver.test.js.map +1 -0
- package/dist/tests/testing-utilities.test.d.ts +2 -0
- package/dist/tests/testing-utilities.test.d.ts.map +1 -0
- package/dist/tests/testing-utilities.test.js +138 -0
- package/dist/tests/testing-utilities.test.js.map +1 -0
- package/dist/tests/validation.test.d.ts +2 -0
- package/dist/tests/validation.test.d.ts.map +1 -0
- package/dist/tests/validation.test.js +135 -0
- package/dist/tests/validation.test.js.map +1 -0
- package/dist/tests/versioning.test.d.ts +2 -0
- package/dist/tests/versioning.test.d.ts.map +1 -0
- package/dist/tests/versioning.test.js +128 -0
- package/dist/tests/versioning.test.js.map +1 -0
- package/dist/types.d.ts +232 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +105 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +225 -0
- package/dist/validation.js.map +1 -0
- package/dist/versioning.d.ts +100 -0
- package/dist/versioning.d.ts.map +1 -0
- package/dist/versioning.js +179 -0
- package/dist/versioning.js.map +1 -0
- package/package.json +137 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// Unit tests for the Backblaze B2 driver submodule (task 28.5).
|
|
2
|
+
//
|
|
3
|
+
// Backblaze B2 exposes a fully S3-compatible API, so the driver is a thin
|
|
4
|
+
// specialization of the shared S3-style base: it fixes the driver name to
|
|
5
|
+
// "backblaze" and either accepts an injected S3ClientLike or builds its own
|
|
6
|
+
// client from connection config via a lazy dynamic import() of the optional
|
|
7
|
+
// peer SDK. These tests exercise the injected-client path with an in-memory
|
|
8
|
+
// fake (no external service, no SDK), assert the name/contract behavior, and
|
|
9
|
+
// confirm the self-built path throws StorageConfigError when the SDK is absent.
|
|
10
|
+
//
|
|
11
|
+
// Requirements: 2.1, 2.3, 3.3
|
|
12
|
+
import test from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import { BackblazeB2Driver, createBackblazeB2Driver, BACKBLAZE_DRIVER_NAME, } from "../drivers/backblaze.js";
|
|
15
|
+
import { StorageConfigError } from "../errors.js";
|
|
16
|
+
import { NotFoundError } from "../errors.js";
|
|
17
|
+
import { registerStorageDriverContractTests } from "./contract.js";
|
|
18
|
+
const FIXED_NOW = 1_700_000_000_000;
|
|
19
|
+
const fixedClock = () => FIXED_NOW;
|
|
20
|
+
function bytes(str) {
|
|
21
|
+
return new TextEncoder().encode(str);
|
|
22
|
+
}
|
|
23
|
+
/** A minimal in-memory S3ClientLike standing in for the B2 S3 endpoint. */
|
|
24
|
+
function makeFakeClient({ withMultipart = true } = {}) {
|
|
25
|
+
const objects = new Map();
|
|
26
|
+
const uploads = new Map();
|
|
27
|
+
let seed = 0;
|
|
28
|
+
const client = {
|
|
29
|
+
async putObject({ key, body, contentType, metadata }) {
|
|
30
|
+
objects.set(key, {
|
|
31
|
+
body: body.slice(),
|
|
32
|
+
contentType,
|
|
33
|
+
metadata: metadata ? { ...metadata } : undefined,
|
|
34
|
+
});
|
|
35
|
+
return { etag: `${key}-etag` };
|
|
36
|
+
},
|
|
37
|
+
async getObject({ key }) {
|
|
38
|
+
const obj = objects.get(key);
|
|
39
|
+
if (obj === undefined)
|
|
40
|
+
return null;
|
|
41
|
+
return {
|
|
42
|
+
body: obj.body.slice(),
|
|
43
|
+
contentType: obj.contentType,
|
|
44
|
+
etag: `${key}-etag`,
|
|
45
|
+
size: obj.body.byteLength,
|
|
46
|
+
lastModified: FIXED_NOW,
|
|
47
|
+
metadata: obj.metadata,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
async headObject({ key }) {
|
|
51
|
+
const obj = objects.get(key);
|
|
52
|
+
if (obj === undefined)
|
|
53
|
+
return null;
|
|
54
|
+
return {
|
|
55
|
+
contentType: obj.contentType,
|
|
56
|
+
etag: `${key}-etag`,
|
|
57
|
+
size: obj.body.byteLength,
|
|
58
|
+
lastModified: FIXED_NOW,
|
|
59
|
+
metadata: obj.metadata,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
async deleteObject({ key }) {
|
|
63
|
+
objects.delete(key);
|
|
64
|
+
},
|
|
65
|
+
async listObjects({ prefix }) {
|
|
66
|
+
return [...objects.entries()]
|
|
67
|
+
.filter(([key]) => key.startsWith(prefix))
|
|
68
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
69
|
+
.map(([key, obj]) => ({ key, size: obj.body.byteLength, updatedAt: FIXED_NOW }));
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
if (withMultipart) {
|
|
73
|
+
client.createMultipartUpload = async ({ key, contentType, metadata }) => {
|
|
74
|
+
const uploadId = `upload-${(seed += 1)}`;
|
|
75
|
+
uploads.set(uploadId, { key, contentType, metadata, parts: new Map() });
|
|
76
|
+
return { uploadId };
|
|
77
|
+
};
|
|
78
|
+
client.uploadPart = async ({ uploadId, partNumber, body }) => {
|
|
79
|
+
uploads.get(uploadId).parts.set(partNumber, body.slice());
|
|
80
|
+
return { etag: `${uploadId}-${partNumber}` };
|
|
81
|
+
};
|
|
82
|
+
client.completeMultipartUpload = async ({ uploadId, parts }) => {
|
|
83
|
+
const session = uploads.get(uploadId);
|
|
84
|
+
const ordered = [...parts].sort((a, b) => a.partNumber - b.partNumber);
|
|
85
|
+
const buffers = ordered.map((p) => Buffer.from(session.parts.get(p.partNumber)));
|
|
86
|
+
objects.set(session.key, {
|
|
87
|
+
body: new Uint8Array(Buffer.concat(buffers)),
|
|
88
|
+
contentType: session.contentType,
|
|
89
|
+
metadata: session.metadata,
|
|
90
|
+
});
|
|
91
|
+
uploads.delete(uploadId);
|
|
92
|
+
return { etag: `${session.key}-etag` };
|
|
93
|
+
};
|
|
94
|
+
client.abortMultipartUpload = async ({ uploadId }) => {
|
|
95
|
+
uploads.delete(uploadId);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return { client, objects, uploads };
|
|
99
|
+
}
|
|
100
|
+
// ── name ────────────────────────────────────────────────────────────────────
|
|
101
|
+
test("driver name is fixed to 'backblaze'", () => {
|
|
102
|
+
const { client } = makeFakeClient();
|
|
103
|
+
const driver = createBackblazeB2Driver(client, { clock: fixedClock });
|
|
104
|
+
assert.equal(driver.name, BACKBLAZE_DRIVER_NAME);
|
|
105
|
+
assert.equal(driver.name, "backblaze");
|
|
106
|
+
});
|
|
107
|
+
test("BackblazeB2Driver class constructs with name 'backblaze'", () => {
|
|
108
|
+
const { client } = makeFakeClient();
|
|
109
|
+
const driver = new BackblazeB2Driver(client, { clock: fixedClock });
|
|
110
|
+
assert.equal(driver.name, "backblaze");
|
|
111
|
+
});
|
|
112
|
+
test("injected-client factory is synchronous (returns a driver, not a promise)", () => {
|
|
113
|
+
const { client } = makeFakeClient();
|
|
114
|
+
const driver = createBackblazeB2Driver(client);
|
|
115
|
+
assert.equal(typeof driver.put, "function");
|
|
116
|
+
assert.notEqual(typeof driver.then, "function");
|
|
117
|
+
});
|
|
118
|
+
// ── primitives round-trip through the injected client ──────────────────────────
|
|
119
|
+
test("put/get round-trips bytes and typed metadata", async () => {
|
|
120
|
+
const { client } = makeFakeClient();
|
|
121
|
+
const driver = createBackblazeB2Driver(client, { clock: fixedClock });
|
|
122
|
+
const content = bytes("backblaze payload");
|
|
123
|
+
await driver.put("docs/file.txt", content, {
|
|
124
|
+
contentType: "text/plain",
|
|
125
|
+
owner: "user-1",
|
|
126
|
+
accessLevel: "public",
|
|
127
|
+
custom: { label: "invoice" },
|
|
128
|
+
});
|
|
129
|
+
const result = await driver.get("docs/file.txt");
|
|
130
|
+
assert.equal(result.found, true);
|
|
131
|
+
assert.deepEqual(result.bytes, content);
|
|
132
|
+
assert.equal(result.metadata.contentType, "text/plain");
|
|
133
|
+
assert.equal(result.metadata.owner, "user-1");
|
|
134
|
+
assert.equal(result.metadata.accessLevel, "public");
|
|
135
|
+
assert.deepEqual(result.metadata.custom, { label: "invoice" });
|
|
136
|
+
});
|
|
137
|
+
test("get/stat report a missing key consistently", async () => {
|
|
138
|
+
const { client } = makeFakeClient();
|
|
139
|
+
const driver = createBackblazeB2Driver(client, { clock: fixedClock });
|
|
140
|
+
assert.deepEqual(await driver.get("missing"), { found: false });
|
|
141
|
+
assert.equal(await driver.stat("missing"), null);
|
|
142
|
+
await assert.rejects(() => driver.getStream("missing"), NotFoundError);
|
|
143
|
+
});
|
|
144
|
+
test("native multipart delegates through the client when available", async () => {
|
|
145
|
+
const { client } = makeFakeClient({ withMultipart: true });
|
|
146
|
+
const driver = createBackblazeB2Driver(client, { clock: fixedClock });
|
|
147
|
+
assert.notEqual(driver.multipart, undefined);
|
|
148
|
+
const mp = driver.multipart;
|
|
149
|
+
const uploadId = await mp.create("big/file.bin", {});
|
|
150
|
+
const p1 = await mp.uploadPart(uploadId, 1, bytes("part-one-"));
|
|
151
|
+
const p2 = await mp.uploadPart(uploadId, 2, bytes("part-two"));
|
|
152
|
+
await mp.complete(uploadId, [p1, p2]);
|
|
153
|
+
const result = await driver.get("big/file.bin");
|
|
154
|
+
assert.equal(result.found, true);
|
|
155
|
+
assert.deepEqual(result.bytes, bytes("part-one-part-two"));
|
|
156
|
+
});
|
|
157
|
+
test("native capabilities are delegated when injected via options", () => {
|
|
158
|
+
const { client } = makeFakeClient();
|
|
159
|
+
const versioning = { snapshot: async () => null, list: async () => [], restore: async () => ({}), deleteVersion: async () => { } };
|
|
160
|
+
const lifecycle = { apply: async () => [] };
|
|
161
|
+
const signedUrl = { sign: async () => "url", verify: () => ({ valid: true }) };
|
|
162
|
+
const driver = createBackblazeB2Driver(client, {
|
|
163
|
+
clock: fixedClock,
|
|
164
|
+
capabilities: { versioning, lifecycle, signedUrl },
|
|
165
|
+
});
|
|
166
|
+
assert.equal(driver.versioning, versioning);
|
|
167
|
+
assert.equal(driver.lifecycle, lifecycle);
|
|
168
|
+
assert.equal(driver.signedUrl, signedUrl);
|
|
169
|
+
});
|
|
170
|
+
// ── self-built client path (lazy dynamic import of an absent optional SDK) ──────
|
|
171
|
+
test("building own client without the SDK installed throws StorageConfigError", async () => {
|
|
172
|
+
await assert.rejects(() => createBackblazeB2Driver({
|
|
173
|
+
bucket: "my-bucket",
|
|
174
|
+
endpoint: "https://s3.us-west-002.backblazeb2.com",
|
|
175
|
+
region: "us-west-002",
|
|
176
|
+
credentials: { accessKeyId: "keyId", secretAccessKey: "appKey" },
|
|
177
|
+
}), (error) => {
|
|
178
|
+
assert.ok(error instanceof StorageConfigError);
|
|
179
|
+
assert.equal(error.provider, "backblaze");
|
|
180
|
+
return true;
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
// ── shared contract-conformance suite ───────────────────────────────────────────
|
|
184
|
+
registerStorageDriverContractTests("backblaze", () => createBackblazeB2Driver(makeFakeClient().client, { clock: fixedClock }), test);
|
|
185
|
+
//# sourceMappingURL=backblaze-driver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backblaze-driver.test.js","sourceRoot":"","sources":["../../src/tests/backblaze-driver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-degradation.test.d.ts","sourceRoot":"","sources":["../../src/tests/bridge-degradation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Cross-bridge graceful-degradation unit tests for the storage facade
|
|
2
|
+
// (task 21.4).
|
|
3
|
+
//
|
|
4
|
+
// Tasks 21.1/21.2/21.3 each proved a single bridge degrades gracefully in
|
|
5
|
+
// isolation (events-bridge.test.js, queue-bridge.test.js,
|
|
6
|
+
// realtime-bridge.test.js). This file adds the focused CROSS-bridge coverage
|
|
7
|
+
// that specifically satisfies task 21.4's intent: with the Events, Queue AND
|
|
8
|
+
// Realtime bridges wired simultaneously, object and upload operations must
|
|
9
|
+
// still succeed whether every bridge is ABSENT or every bridge THROWS, and the
|
|
10
|
+
// events published must carry the affected key + metadata.
|
|
11
|
+
//
|
|
12
|
+
// Covered:
|
|
13
|
+
// - all three bridges absent → put/get/delete/putStream/resumeUpload succeed
|
|
14
|
+
// (17.3, 19.3).
|
|
15
|
+
// - all three bridges throwing simultaneously → the same operations still
|
|
16
|
+
// succeed and return correct results (17.4, 19.3).
|
|
17
|
+
// - with a live (recording) events bridge alongside a throwing queue + throwing
|
|
18
|
+
// realtime bridge, the published events still carry key + metadata; asserted
|
|
19
|
+
// for storage.uploaded (key + full metadata) and storage.deleted (key)
|
|
20
|
+
// (18.2).
|
|
21
|
+
//
|
|
22
|
+
// Uses the Node.js built-in test runner (node:test); executed via
|
|
23
|
+
// `node --test dist/tests/*.test.js`.
|
|
24
|
+
//
|
|
25
|
+
// Requirements: 17.3, 17.4, 18.2, 19.3
|
|
26
|
+
import test from "node:test";
|
|
27
|
+
import assert from "node:assert/strict";
|
|
28
|
+
import { Readable } from "node:stream";
|
|
29
|
+
import { createStorage } from "../index.js";
|
|
30
|
+
/** A bridge trio whose every method throws synchronously on use. */
|
|
31
|
+
function throwingBridges() {
|
|
32
|
+
return {
|
|
33
|
+
events: {
|
|
34
|
+
publish() {
|
|
35
|
+
throw new Error("events bus down");
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
queue: {
|
|
39
|
+
dispatch() {
|
|
40
|
+
throw new Error("queue down");
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
realtime: {
|
|
44
|
+
broadcast() {
|
|
45
|
+
throw new Error("realtime down");
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** A recording EventsLike double capturing every published (event, payload). */
|
|
51
|
+
function recordingEvents() {
|
|
52
|
+
const published = [];
|
|
53
|
+
return {
|
|
54
|
+
published,
|
|
55
|
+
publish(event, payload) {
|
|
56
|
+
published.push({ event, payload: payload });
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
test("with NO bridges configured, object + upload operations all succeed (Req 17.3, 19.3)", async () => {
|
|
61
|
+
const storage = createStorage({ provider: "memory" });
|
|
62
|
+
// Object operations.
|
|
63
|
+
const meta = await storage.put("k", "v");
|
|
64
|
+
assert.equal(meta.key, "k");
|
|
65
|
+
const got = await storage.get("k");
|
|
66
|
+
assert.equal(got.found, true);
|
|
67
|
+
await storage.delete("k");
|
|
68
|
+
assert.equal(await storage.exists("k"), false);
|
|
69
|
+
// Streaming upload.
|
|
70
|
+
const streamed = await storage.putStream("s", Readable.from([Buffer.from("stream-bytes")]));
|
|
71
|
+
assert.equal(streamed.key, "s");
|
|
72
|
+
assert.equal(await storage.exists("s"), true);
|
|
73
|
+
// Resumable upload.
|
|
74
|
+
const sessionId = await storage.startUpload("r");
|
|
75
|
+
const resumed = await storage.resumeUpload(sessionId, Readable.from([Buffer.from("resumed")]));
|
|
76
|
+
assert.equal(resumed.key, "r");
|
|
77
|
+
assert.equal(await storage.exists("r"), true);
|
|
78
|
+
});
|
|
79
|
+
test("with ALL THREE bridges throwing simultaneously, object operations still succeed (Req 17.4, 19.3)", async () => {
|
|
80
|
+
const storage = createStorage({ provider: "memory", bridges: throwingBridges() });
|
|
81
|
+
// put on a new key succeeds and returns metadata despite every bridge throwing.
|
|
82
|
+
const meta = await storage.put("k", "v", { contentType: "text/plain" });
|
|
83
|
+
assert.equal(meta.key, "k");
|
|
84
|
+
// get returns the stored bytes.
|
|
85
|
+
const got = await storage.get("k");
|
|
86
|
+
assert.equal(got.found, true);
|
|
87
|
+
// put overwriting (storage.updated path) still succeeds.
|
|
88
|
+
const updated = await storage.put("k", "v2");
|
|
89
|
+
assert.equal(updated.key, "k");
|
|
90
|
+
// delete succeeds and removes the object.
|
|
91
|
+
await storage.delete("k");
|
|
92
|
+
assert.equal(await storage.exists("k"), false);
|
|
93
|
+
});
|
|
94
|
+
test("with ALL THREE bridges throwing simultaneously, streaming + resumable uploads still succeed (Req 17.4, 19.3)", async () => {
|
|
95
|
+
const storage = createStorage({ provider: "memory", bridges: throwingBridges() });
|
|
96
|
+
// putStream broadcasts started/completed through a throwing realtime bridge —
|
|
97
|
+
// the upload must still complete.
|
|
98
|
+
const streamed = await storage.putStream("s", Readable.from([Buffer.from("stream-bytes")]));
|
|
99
|
+
assert.equal(streamed.key, "s");
|
|
100
|
+
assert.equal(await storage.exists("s"), true);
|
|
101
|
+
// resumeUpload likewise broadcasts through the throwing realtime bridge.
|
|
102
|
+
const sessionId = await storage.startUpload("r");
|
|
103
|
+
const resumed = await storage.resumeUpload(sessionId, Readable.from([Buffer.from("resumed")]));
|
|
104
|
+
assert.equal(resumed.key, "r");
|
|
105
|
+
assert.equal(await storage.exists("r"), true);
|
|
106
|
+
});
|
|
107
|
+
test("published events carry key + metadata even while queue + realtime bridges throw (Req 18.2)", async () => {
|
|
108
|
+
const events = recordingEvents();
|
|
109
|
+
const storage = createStorage({
|
|
110
|
+
provider: "memory",
|
|
111
|
+
bridges: {
|
|
112
|
+
events,
|
|
113
|
+
queue: {
|
|
114
|
+
dispatch() {
|
|
115
|
+
throw new Error("queue down");
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
realtime: {
|
|
119
|
+
broadcast() {
|
|
120
|
+
throw new Error("realtime down");
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
await storage.put("photo.png", "bytes", { contentType: "image/png" });
|
|
126
|
+
await storage.delete("photo.png");
|
|
127
|
+
assert.deepEqual(events.published.map((e) => e.event), ["storage.uploaded", "storage.deleted"]);
|
|
128
|
+
// storage.uploaded carries the affected key AND the full object metadata.
|
|
129
|
+
const uploaded = events.published[0].payload;
|
|
130
|
+
assert.equal(uploaded.key, "photo.png");
|
|
131
|
+
assert.ok(uploaded.metadata, "uploaded event must carry metadata");
|
|
132
|
+
assert.equal(uploaded.metadata.key, "photo.png");
|
|
133
|
+
assert.equal(uploaded.metadata.contentType, "image/png");
|
|
134
|
+
// storage.deleted carries the affected key.
|
|
135
|
+
const deleted = events.published[1].payload;
|
|
136
|
+
assert.equal(deleted.key, "photo.png");
|
|
137
|
+
});
|
|
138
|
+
//# sourceMappingURL=bridge-degradation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-degradation.test.js","sourceRoot":"","sources":["../../src/tests/bridge-degradation.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../src/tests/cli.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// cli.test.js — unit tests for the @streetjs/storage CLI generators and the
|
|
2
|
+
// storage:verify command (Requirements 21.2, 21.4).
|
|
3
|
+
//
|
|
4
|
+
// Covers three concerns:
|
|
5
|
+
// (1) Generator name validation — isValidGeneratorName / generateStorage
|
|
6
|
+
// reject invalid names and accept valid PascalCase identifiers.
|
|
7
|
+
// (2) Compile-clean scaffold output — the generated `contents` imports only
|
|
8
|
+
// public @streetjs/storage symbols (createStorage + StorageConfig) and
|
|
9
|
+
// wires them into a typed factory.
|
|
10
|
+
// (3) storage:verify pass/fail reporting — StorageCommands.storageVerify
|
|
11
|
+
// reports a pass for the built-in memory provider (process.exitCode is
|
|
12
|
+
// not set to 1) and a failure for an unknown/misconfigured provider
|
|
13
|
+
// (process.exitCode === 1).
|
|
14
|
+
//
|
|
15
|
+
// Uses the Node.js built-in test runner (node:test); executed via
|
|
16
|
+
// `node --test dist/tests/*.test.js`.
|
|
17
|
+
import test from "node:test";
|
|
18
|
+
import assert from "node:assert/strict";
|
|
19
|
+
import { generateStorage, isValidGeneratorName, isValidProvider, DEFAULT_STORAGE_DIR, } from "../cli/generators.js";
|
|
20
|
+
import { StorageCommands } from "../cli/commands.js";
|
|
21
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
22
|
+
/** Build a ParsedArgs object matching the shape streetjs' parseArgv produces. */
|
|
23
|
+
function parsedArgs(options = {}) {
|
|
24
|
+
const { command = null, positional = [], flags = {} } = options;
|
|
25
|
+
return { command, positional, flags };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run `fn` while capturing everything written to console.log/console.error and
|
|
29
|
+
* with process.exitCode reset, restoring both afterwards. Returns the captured
|
|
30
|
+
* output plus the exitCode observed after `fn` completes.
|
|
31
|
+
*/
|
|
32
|
+
async function captureRun(fn) {
|
|
33
|
+
const originalLog = console.log;
|
|
34
|
+
const originalError = console.error;
|
|
35
|
+
const originalExitCode = process.exitCode;
|
|
36
|
+
const logs = [];
|
|
37
|
+
const errors = [];
|
|
38
|
+
console.log = (...args) => logs.push(args.join(" "));
|
|
39
|
+
console.error = (...args) => errors.push(args.join(" "));
|
|
40
|
+
process.exitCode = undefined;
|
|
41
|
+
try {
|
|
42
|
+
await fn();
|
|
43
|
+
return { logs, errors, exitCode: process.exitCode };
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
console.log = originalLog;
|
|
47
|
+
console.error = originalError;
|
|
48
|
+
process.exitCode = originalExitCode;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ── (1) Generator name validation ──────────────────────────────────────────────
|
|
52
|
+
test("isValidGeneratorName rejects invalid names and accepts PascalCase", () => {
|
|
53
|
+
// Invalid: empty, non-identifier, leading digit, hyphen/space/dot.
|
|
54
|
+
assert.equal(isValidGeneratorName(""), false);
|
|
55
|
+
assert.equal(isValidGeneratorName("1Uploads"), false);
|
|
56
|
+
assert.equal(isValidGeneratorName("my-storage"), false);
|
|
57
|
+
assert.equal(isValidGeneratorName("my storage"), false);
|
|
58
|
+
assert.equal(isValidGeneratorName("Up.loads"), false);
|
|
59
|
+
assert.equal(isValidGeneratorName("$Uploads"), false);
|
|
60
|
+
// Valid: identifier-safe tokens including PascalCase.
|
|
61
|
+
assert.equal(isValidGeneratorName("Uploads"), true);
|
|
62
|
+
assert.equal(isValidGeneratorName("UserAvatars"), true);
|
|
63
|
+
assert.equal(isValidGeneratorName("A1"), true);
|
|
64
|
+
assert.equal(isValidGeneratorName("uploads"), true);
|
|
65
|
+
});
|
|
66
|
+
test("generateStorage throws for invalid names before producing output", () => {
|
|
67
|
+
assert.throws(() => generateStorage(""), /Invalid generator name/);
|
|
68
|
+
assert.throws(() => generateStorage("bad-name"), /Invalid generator name/);
|
|
69
|
+
assert.throws(() => generateStorage("9lives"), /Invalid generator name/);
|
|
70
|
+
});
|
|
71
|
+
test("generateStorage throws for a non-built-in provider", () => {
|
|
72
|
+
assert.throws(() => generateStorage("Uploads", DEFAULT_STORAGE_DIR, "s3"), /Invalid storage provider/);
|
|
73
|
+
assert.equal(isValidProvider("memory"), true);
|
|
74
|
+
assert.equal(isValidProvider("local"), true);
|
|
75
|
+
assert.equal(isValidProvider("s3"), false);
|
|
76
|
+
});
|
|
77
|
+
// ── (2) Compile-clean scaffold output ──────────────────────────────────────────
|
|
78
|
+
test("generateStorage emits a scaffold importing only public @streetjs/storage symbols", () => {
|
|
79
|
+
const result = generateStorage("Uploads");
|
|
80
|
+
// Path is derived from the PascalCase class name under the default dir.
|
|
81
|
+
assert.ok(result.path.endsWith("UploadsStorage.ts"), `unexpected path: ${result.path}`);
|
|
82
|
+
const contents = result.contents;
|
|
83
|
+
// Imports only the public package entry point (no deep/internal imports).
|
|
84
|
+
assert.match(contents, /import \{ createStorage \} from "@streetjs\/storage";/);
|
|
85
|
+
assert.match(contents, /import type \{ Storage, StorageConfig \} from "@streetjs\/storage";/);
|
|
86
|
+
assert.ok(!/from "\.\//.test(contents) && !/from "\.\.\//.test(contents), "scaffold must not import from relative/internal paths");
|
|
87
|
+
// Wires a typed StorageConfig and a factory that calls createStorage.
|
|
88
|
+
assert.match(contents, /: StorageConfig = \{/);
|
|
89
|
+
assert.match(contents, /provider: "memory"/);
|
|
90
|
+
assert.match(contents, /export function createUploadsStorage\(\): Storage \{/);
|
|
91
|
+
assert.match(contents, /return createStorage\(/);
|
|
92
|
+
});
|
|
93
|
+
test("generateStorage local provider scaffold supplies a root and stays on the public surface", () => {
|
|
94
|
+
const result = generateStorage("Assets", DEFAULT_STORAGE_DIR, "local");
|
|
95
|
+
const contents = result.contents;
|
|
96
|
+
assert.match(contents, /provider: "local"/);
|
|
97
|
+
assert.match(contents, /root: "\.\/var\/assets"/);
|
|
98
|
+
assert.match(contents, /import \{ createStorage \} from "@streetjs\/storage";/);
|
|
99
|
+
});
|
|
100
|
+
// ── (3) storage:verify pass/fail reporting ──────────────────────────────────────
|
|
101
|
+
test("storageVerify reports a pass for the built-in memory provider", async () => {
|
|
102
|
+
const commands = new StorageCommands();
|
|
103
|
+
const args = parsedArgs({ command: "storage:verify", flags: { provider: "memory" } });
|
|
104
|
+
const { logs, exitCode } = await captureRun(() => commands.storageVerify(args));
|
|
105
|
+
// A passing run must NOT set a failure exit code.
|
|
106
|
+
assert.notEqual(exitCode, 1);
|
|
107
|
+
const output = logs.join("\n");
|
|
108
|
+
assert.match(output, /Contract conformance for driver "memory"/);
|
|
109
|
+
assert.match(output, /— PASS\./);
|
|
110
|
+
// No FAIL check lines expected.
|
|
111
|
+
assert.ok(!/\[FAIL\]/.test(output), "memory provider should have no failing checks");
|
|
112
|
+
});
|
|
113
|
+
test("storageVerify reports failure for an unknown provider (exitCode 1)", async () => {
|
|
114
|
+
const commands = new StorageCommands();
|
|
115
|
+
const args = parsedArgs({ command: "storage:verify", flags: { provider: "does-not-exist" } });
|
|
116
|
+
const { errors, exitCode } = await captureRun(() => commands.storageVerify(args));
|
|
117
|
+
assert.equal(exitCode, 1);
|
|
118
|
+
assert.match(errors.join("\n"), /Unknown storage provider "does-not-exist"/);
|
|
119
|
+
});
|
|
120
|
+
test("storageVerify reports failure for a misconfigured local provider (missing root)", async () => {
|
|
121
|
+
const commands = new StorageCommands();
|
|
122
|
+
const args = parsedArgs({ command: "storage:verify", flags: { provider: "local" } });
|
|
123
|
+
const { errors, exitCode } = await captureRun(() => commands.storageVerify(args));
|
|
124
|
+
assert.equal(exitCode, 1);
|
|
125
|
+
assert.match(errors.join("\n"), /"local" storage provider requires a "root" directory/);
|
|
126
|
+
});
|
|
127
|
+
//# sourceMappingURL=cli.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../../src/tests/cli.test.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,oDAAoD;AACpD,EAAE;AACF,yBAAyB;AACzB,2EAA2E;AAC3E,sEAAsE;AACtE,8EAA8E;AAC9E,6EAA6E;AAC7E,yCAAyC;AACzC,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,kCAAkC;AAClC,EAAE;AACF,kEAAkE;AAClE,sCAAsC;AAEtC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AAIxC,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,iFAAiF;AAEjF,iFAAiF;AACjF,SAAS,UAAU,CACjB,UAII,EAAE;IAEN,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,UAAU,GAAG,EAAE,EAAE,KAAK,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAChE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,UAAU,CAAC,EAAiB;IAKzC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC;IAChC,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC;IACpC,MAAM,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC1C,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,OAAO,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACrD,OAAO,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,CAAC;QACX,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;IACtD,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,GAAG,WAAW,CAAC;QAC1B,OAAO,CAAC,KAAK,GAAG,aAAa,CAAC;QAC9B,OAAO,CAAC,QAAQ,GAAG,gBAAgB,CAAC;IACtC,CAAC;AACH,CAAC;AAED,kFAAkF;AAElF,IAAI,CAAC,mEAAmE,EAAE,GAAG,EAAE;IAC7E,mEAAmE;IACnE,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;IACtD,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;IACtD,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;IAEtD,sDAAsD;IACtD,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kEAAkE,EAAE,GAAG,EAAE;IAC5E,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC;IACnE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,wBAAwB,CAAC,CAAC;IAC3E,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,wBAAwB,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC9D,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CAAC,eAAe,CAAC,SAAS,EAAE,mBAAmB,EAAE,IAAuB,CAAC,EAC9E,0BAA0B,CAC3B,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;AAC7C,CAAC,CAAC,CAAC;AAEH,kFAAkF;AAElF,IAAI,CAAC,kFAAkF,EAAE,GAAG,EAAE;IAC5F,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAE1C,wEAAwE;IACxE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,oBAAoB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAExF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAEjC,0EAA0E;IAC1E,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,uDAAuD,CAAC,CAAC;IAChF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,qEAAqE,CAAC,CAAC;IAC9F,MAAM,CAAC,EAAE,CACP,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAC9D,uDAAuD,CACxD,CAAC;IAEF,sEAAsE;IACtE,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,sDAAsD,CAAC,CAAC;IAC/E,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,wBAAwB,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yFAAyF,EAAE,GAAG,EAAE;IACnG,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;IAC5C,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,uDAAuD,CAAC,CAAC;AAClF,CAAC,CAAC,CAAC;AAEH,mFAAmF;AAEnF,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;IAC/E,MAAM,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAEtF,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhF,kDAAkD;IAClD,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,0CAA0C,CAAC,CAAC;IACjE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACjC,gCAAgC;IAChC,MAAM,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,+CAA+C,CAAC,CAAC;AACvF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;IACpF,MAAM,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAE9F,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAElF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,2CAA2C,CAAC,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iFAAiF,EAAE,KAAK,IAAI,EAAE;IACjG,MAAM,QAAQ,GAAG,IAAI,eAAe,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;IAErF,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAElF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,sDAAsD,CAAC,CAAC;AAC1F,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/storage — the shared, parameterized contract-conformance suite.
|
|
3
|
+
*
|
|
4
|
+
* Requirement 2 states that every provider implements one `StorageDriver`
|
|
5
|
+
* contract with identical observable behavior: object existence, content bytes,
|
|
6
|
+
* and returned metadata must be equivalent across drivers (Requirement 2.1,
|
|
7
|
+
* 2.3), and a missing key must be reported the same way everywhere
|
|
8
|
+
* (Requirement 2.4). This module encodes the observable core of that contract
|
|
9
|
+
* as a **reusable, driver-agnostic conformance suite** so the exact same checks
|
|
10
|
+
* can be run against any {@link StorageDriver} — the zero-dependency Memory and
|
|
11
|
+
* Local drivers, the in-process testing doubles, and every cloud driver.
|
|
12
|
+
*
|
|
13
|
+
* The suite is designed to be consumed in two ways from one definition:
|
|
14
|
+
*
|
|
15
|
+
* - **Programmatically** (returns pass/fail results). {@link runStorageDriverContract}
|
|
16
|
+
* runs every check against a freshly constructed driver and returns a
|
|
17
|
+
* {@link ContractReport} describing which checks passed. This is what the
|
|
18
|
+
* `storage:verify` CLI command (task 24.2) uses to report whether the
|
|
19
|
+
* configured driver satisfies the contract, and what Property 19 (task 27.3)
|
|
20
|
+
* uses to assert that every driver conforms.
|
|
21
|
+
* - **As node:test tests**. {@link registerStorageDriverContractTests} turns each
|
|
22
|
+
* check into a `node:test` case using a caller-supplied `test` function, so a
|
|
23
|
+
* thin `*.test.js` can run the suite against Memory + Local without this
|
|
24
|
+
* support module importing `node:test` itself (keeping it importable from the
|
|
25
|
+
* CLI, which has no test runner in scope).
|
|
26
|
+
*
|
|
27
|
+
* Each check receives its **own freshly constructed driver** (from the supplied
|
|
28
|
+
* factory) so checks are fully isolated and order-independent. Assertions use
|
|
29
|
+
* `node:assert/strict`; a check throws on failure and returns normally on
|
|
30
|
+
* success. The checked behaviors are:
|
|
31
|
+
*
|
|
32
|
+
* 1. **existence** — `put` then `exists(key)` is `true`; `exists(other)` is `false`.
|
|
33
|
+
* 2. **byte round-trip** — `put(key, bytes)` then `get(key)` yields
|
|
34
|
+
* `{ found: true }` with content bytes exactly equal to the input.
|
|
35
|
+
* 3. **not-found (get)** — `get(missing)` yields `{ found: false }`.
|
|
36
|
+
* 4. **not-found (stat)** — `stat(missing)` yields `null`.
|
|
37
|
+
* 5. **metadata shape** — `put` returns the complete typed
|
|
38
|
+
* {@link StorageObjectMetadata} field set with correctly typed values.
|
|
39
|
+
*
|
|
40
|
+
* This module is a support module under `src/tests/` (not itself a `*.test.js`
|
|
41
|
+
* picked up by the default test glob); it is imported by tests and by the CLI.
|
|
42
|
+
*
|
|
43
|
+
* _Requirements: 2.1, 2.3, 2.4_
|
|
44
|
+
*/
|
|
45
|
+
import type { StorageDriver } from "../driver.js";
|
|
46
|
+
/**
|
|
47
|
+
* A single named conformance check. {@link run} performs the check against the
|
|
48
|
+
* supplied driver and throws (via `node:assert`) when the driver violates the
|
|
49
|
+
* contract; it returns normally when the check passes.
|
|
50
|
+
*/
|
|
51
|
+
export interface ContractCheck {
|
|
52
|
+
/** Stable, human-readable check name (e.g. "byte round-trip"). */
|
|
53
|
+
readonly name: string;
|
|
54
|
+
/** The acceptance-criteria reference this check exercises. */
|
|
55
|
+
readonly requirement: string;
|
|
56
|
+
/** Run the check against `driver`; throws on violation. */
|
|
57
|
+
run(driver: StorageDriver): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
/** The outcome of running a single {@link ContractCheck}. */
|
|
60
|
+
export interface ContractCheckResult {
|
|
61
|
+
readonly name: string;
|
|
62
|
+
readonly requirement: string;
|
|
63
|
+
readonly passed: boolean;
|
|
64
|
+
/** The failure message when {@link passed} is `false`. */
|
|
65
|
+
readonly error?: string;
|
|
66
|
+
}
|
|
67
|
+
/** The aggregate outcome of running the whole suite against one driver. */
|
|
68
|
+
export interface ContractReport {
|
|
69
|
+
/** The `name` of the driver under test (e.g. "memory", "local", "s3"). */
|
|
70
|
+
readonly driver: string;
|
|
71
|
+
/** `true` when every check passed. */
|
|
72
|
+
readonly passed: boolean;
|
|
73
|
+
/** Per-check results, in suite order. */
|
|
74
|
+
readonly results: readonly ContractCheckResult[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Produces a fresh {@link StorageDriver} to test. A new instance is requested
|
|
78
|
+
* for each check so the checks are isolated from one another; the factory may
|
|
79
|
+
* be async (e.g. to allocate a temp directory for the Local driver).
|
|
80
|
+
*/
|
|
81
|
+
export type StorageDriverFactory = () => StorageDriver | Promise<StorageDriver>;
|
|
82
|
+
/**
|
|
83
|
+
* A minimal `node:test`-style runner: a function taking a test name and an async
|
|
84
|
+
* body. Callers pass `test` from `node:test`; typing it structurally keeps this
|
|
85
|
+
* module free of a hard `node:test` import so it stays importable from the CLI.
|
|
86
|
+
*/
|
|
87
|
+
export type TestRunner = (name: string, fn: () => void | Promise<void>) => unknown;
|
|
88
|
+
/**
|
|
89
|
+
* The ordered set of observable-behavior checks that make up the driver
|
|
90
|
+
* contract-conformance suite. Exported so consumers can inspect, subset, or
|
|
91
|
+
* extend the list; most callers use {@link runStorageDriverContract} or
|
|
92
|
+
* {@link registerStorageDriverContractTests} instead.
|
|
93
|
+
*/
|
|
94
|
+
export declare const storageDriverContractChecks: readonly ContractCheck[];
|
|
95
|
+
/** Options for {@link runStorageDriverContract}. */
|
|
96
|
+
export interface RunContractOptions {
|
|
97
|
+
/** Override the checks to run. Defaults to {@link storageDriverContractChecks}. */
|
|
98
|
+
readonly checks?: readonly ContractCheck[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Run the contract-conformance suite against a driver and return a pass/fail
|
|
102
|
+
* {@link ContractReport}. Each check is executed against a freshly constructed
|
|
103
|
+
* driver from `makeDriver` so the checks never interfere with one another.
|
|
104
|
+
*
|
|
105
|
+
* This never throws for a contract violation — a failing check is captured as a
|
|
106
|
+
* `passed: false` entry with its error message — so a consumer such as the
|
|
107
|
+
* `storage:verify` CLI (task 24.2) can render every result. Property 19
|
|
108
|
+
* (task 27.3) uses the returned {@link ContractReport.passed} to assert
|
|
109
|
+
* conformance across drivers.
|
|
110
|
+
*
|
|
111
|
+
* @param makeDriver Factory producing a fresh driver for each check.
|
|
112
|
+
* @param options Optional override of the checks to run.
|
|
113
|
+
*/
|
|
114
|
+
export declare function runStorageDriverContract(makeDriver: StorageDriverFactory, options?: RunContractOptions): Promise<ContractReport>;
|
|
115
|
+
/**
|
|
116
|
+
* Register each contract check as an individual `node:test` case using the
|
|
117
|
+
* supplied `test` runner. A thin `*.test.js` passes `test` from `node:test` and
|
|
118
|
+
* a driver factory; each check runs against a fresh driver and fails the test if
|
|
119
|
+
* the driver violates the contract.
|
|
120
|
+
*
|
|
121
|
+
* Taking the runner as a parameter keeps this module free of a hard `node:test`
|
|
122
|
+
* import, so it remains importable from non-test contexts (e.g. the CLI's
|
|
123
|
+
* `storage:verify`).
|
|
124
|
+
*
|
|
125
|
+
* @param label Prefix for each generated test name (e.g. the driver label).
|
|
126
|
+
* @param makeDriver Factory producing a fresh driver for each check.
|
|
127
|
+
* @param runner The `test` function from `node:test` (or a compatible runner).
|
|
128
|
+
* @param checks Optional override of the checks to run.
|
|
129
|
+
*/
|
|
130
|
+
export declare function registerStorageDriverContractTests(label: string, makeDriver: StorageDriverFactory, runner: TestRunner, checks?: readonly ContractCheck[]): void;
|
|
131
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../src/tests/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAMlD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,GAAG,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAClD;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;AAEhF;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC;AAmEnF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,EAAE,SAAS,aAAa,EAuE/D,CAAC;AAIF,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,oBAAoB,EAChC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,cAAc,CAAC,CA0BzB;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,SAAS,aAAa,EAAgC,GAC7D,IAAI,CAON"}
|