@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,74 @@
|
|
|
1
|
+
// Property-based test for the storage facade's versioning restore semantics.
|
|
2
|
+
//
|
|
3
|
+
// Feature: unified-storage-framework, Property 13: Version restore reproduces
|
|
4
|
+
// prior state. With versioning enabled (`createStorage({ provider: 'memory',
|
|
5
|
+
// versioning: true, clock })`), for an arbitrary sequence of contents written
|
|
6
|
+
// to a single key, each overwrite snapshots the prior content as a retained
|
|
7
|
+
// Version. Restoring a listed Version makes exactly that Version's prior
|
|
8
|
+
// content current again (bytes equal).
|
|
9
|
+
//
|
|
10
|
+
// The listed Versions are ordered oldest-first by creation time, so with a
|
|
11
|
+
// strictly-increasing clock the i-th listed Version corresponds to the i-th
|
|
12
|
+
// written content (each of the N writes after the first snapshots the content
|
|
13
|
+
// that preceded it, yielding N-1 Versions for contents[0..N-2]).
|
|
14
|
+
//
|
|
15
|
+
// Backed by the zero-dependency in-memory provider, exercised across arbitrary
|
|
16
|
+
// keys and content sequences with fast-check at { numRuns: 100 }. Executed via
|
|
17
|
+
// the Node.js built-in test runner (`node --test dist/tests/*.test.js`).
|
|
18
|
+
//
|
|
19
|
+
// Validates: Requirements 12.1, 12.2, 12.3, 12.4, 26.5
|
|
20
|
+
import test from "node:test";
|
|
21
|
+
import assert from "node:assert/strict";
|
|
22
|
+
import fc from "fast-check";
|
|
23
|
+
import { createStorage } from "../index.js";
|
|
24
|
+
/**
|
|
25
|
+
* A strictly-increasing clock. Each successive write therefore stamps a larger
|
|
26
|
+
* creation time, so the retained Versions enumerate oldest-first in the same
|
|
27
|
+
* order they were written — letting the i-th Version be matched to the i-th
|
|
28
|
+
* written content.
|
|
29
|
+
*/
|
|
30
|
+
function makeIncrementingClock() {
|
|
31
|
+
let now = 1_700_000_000_000;
|
|
32
|
+
return () => now++;
|
|
33
|
+
}
|
|
34
|
+
/** Arbitrary object content: byte arrays (including empty) as Uint8Array. */
|
|
35
|
+
const contentArb = fc.uint8Array({ minLength: 0, maxLength: 128 });
|
|
36
|
+
/**
|
|
37
|
+
* A sequence of at least two contents written to the same key, so at least one
|
|
38
|
+
* overwrite (and therefore at least one retained Version) occurs.
|
|
39
|
+
*/
|
|
40
|
+
const contentSequenceArb = fc.array(contentArb, { minLength: 2, maxLength: 6 });
|
|
41
|
+
test("Feature: unified-storage-framework, Property 13 — version restore reproduces prior state", async () => {
|
|
42
|
+
await fc.assert(fc.asyncProperty(fc.string({ minLength: 1, maxLength: 40 }), contentSequenceArb, async (key, contents) => {
|
|
43
|
+
// A fresh, versioned storage instance per case keeps runs independent.
|
|
44
|
+
const storage = createStorage({
|
|
45
|
+
provider: "memory",
|
|
46
|
+
versioning: true,
|
|
47
|
+
clock: makeIncrementingClock(),
|
|
48
|
+
});
|
|
49
|
+
// Write the full sequence to the same key; each write after the first
|
|
50
|
+
// overwrites the prior content and snapshots it as a Version (12.1).
|
|
51
|
+
for (const content of contents) {
|
|
52
|
+
await storage.put(key, content);
|
|
53
|
+
}
|
|
54
|
+
// Each of the N writes after the first produces exactly one Version,
|
|
55
|
+
// enumerated oldest-first (12.2).
|
|
56
|
+
const versions = await storage.listVersions(key);
|
|
57
|
+
assert.equal(versions.length, contents.length - 1, "one retained Version per overwrite");
|
|
58
|
+
// Restoring the i-th listed Version reproduces exactly contents[i],
|
|
59
|
+
// the content that preceded the (i+1)-th write (12.3). Restoring does
|
|
60
|
+
// not itself create a new Version, so the Version set is stable and
|
|
61
|
+
// every listed Version remains restorable (12.4).
|
|
62
|
+
for (let i = 0; i < versions.length; i++) {
|
|
63
|
+
const meta = await storage.restoreVersion(key, versions[i].versionId);
|
|
64
|
+
assert.equal(meta.key, key, "restore returns metadata for the key");
|
|
65
|
+
const got = await storage.get(key);
|
|
66
|
+
assert.equal(got.found, true, "restored object is readable");
|
|
67
|
+
assert.deepEqual(got.bytes, contents[i], "restored content equals the Version's prior content (bytes equal)");
|
|
68
|
+
// The Version set is unchanged by a restore.
|
|
69
|
+
assert.equal((await storage.listVersions(key)).length, versions.length, "restore does not add or remove Versions");
|
|
70
|
+
}
|
|
71
|
+
await storage.close();
|
|
72
|
+
}), { numRuns: 100 });
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=prop-versioning.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prop-versioning.test.js","sourceRoot":"","sources":["../../src/tests/prop-versioning.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-bridge.test.d.ts","sourceRoot":"","sources":["../../src/tests/queue-bridge.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// Unit tests for the Queue integration bridge wired into the storage facade
|
|
2
|
+
// (task 21.2).
|
|
3
|
+
//
|
|
4
|
+
// Verifies the Requirement 17 / 28.3 graceful-degradation semantics over the
|
|
5
|
+
// zero-dependency `memory` provider:
|
|
6
|
+
//
|
|
7
|
+
// - bridgeStorageQueue dispatches the correct typed job name + payload through
|
|
8
|
+
// the structural QueueLike bridge (17.1).
|
|
9
|
+
// - a synchronous throw from the queue is fully isolated — never propagates
|
|
10
|
+
// into the caller (17.4).
|
|
11
|
+
// - an asynchronous rejection from the queue is fully isolated (17.4).
|
|
12
|
+
// - with no queue bridge configured, storage operations proceed unaffected
|
|
13
|
+
// (17.3).
|
|
14
|
+
// - configuring a queue bridge (present, throwing, or absent) never breaks the
|
|
15
|
+
// storage operation path.
|
|
16
|
+
//
|
|
17
|
+
// Uses the Node.js built-in test runner (node:test); executed via
|
|
18
|
+
// `node --test dist/tests/*.test.js`.
|
|
19
|
+
//
|
|
20
|
+
// Requirements: 17.1, 17.2, 17.3, 17.4, 28.3
|
|
21
|
+
import test from "node:test";
|
|
22
|
+
import assert from "node:assert/strict";
|
|
23
|
+
import { createStorage, bridgeStorageQueue } from "../index.js";
|
|
24
|
+
/** A recording QueueLike double capturing every dispatched (job, payload). */
|
|
25
|
+
function recordingQueue() {
|
|
26
|
+
const dispatched = [];
|
|
27
|
+
return {
|
|
28
|
+
dispatched,
|
|
29
|
+
dispatch(job, payload) {
|
|
30
|
+
dispatched.push({ job, payload: payload });
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
test("bridgeStorageQueue dispatches typed job names with key + options (Req 17.1)", () => {
|
|
35
|
+
const queue = recordingQueue();
|
|
36
|
+
const publisher = bridgeStorageQueue(queue);
|
|
37
|
+
publisher.thumbnail("photo.png", { size: 128 });
|
|
38
|
+
publisher.virusScan("upload.bin");
|
|
39
|
+
publisher.ocr("scan.tiff");
|
|
40
|
+
publisher.pdfProcess("doc.pdf");
|
|
41
|
+
publisher.transcode("video.mov");
|
|
42
|
+
publisher.imageOptimize("photo.png");
|
|
43
|
+
publisher.archive("report.csv");
|
|
44
|
+
assert.deepEqual(queue.dispatched.map((d) => d.job), [
|
|
45
|
+
"storage.thumbnail",
|
|
46
|
+
"storage.virus-scan",
|
|
47
|
+
"storage.ocr",
|
|
48
|
+
"storage.pdf-process",
|
|
49
|
+
"storage.transcode",
|
|
50
|
+
"storage.image-optimize",
|
|
51
|
+
"storage.archive",
|
|
52
|
+
]);
|
|
53
|
+
assert.equal(queue.dispatched[0].payload.key, "photo.png");
|
|
54
|
+
assert.deepEqual(queue.dispatched[0].payload.options, { size: 128 });
|
|
55
|
+
assert.equal(queue.dispatched[1].payload.key, "upload.bin");
|
|
56
|
+
});
|
|
57
|
+
test("dispatch() passes an already-named job straight through (Req 17.1)", () => {
|
|
58
|
+
const queue = recordingQueue();
|
|
59
|
+
const publisher = bridgeStorageQueue(queue);
|
|
60
|
+
publisher.dispatch("storage.archive", { key: "k" });
|
|
61
|
+
assert.equal(queue.dispatched.length, 1);
|
|
62
|
+
assert.equal(queue.dispatched[0].job, "storage.archive");
|
|
63
|
+
assert.equal(queue.dispatched[0].payload.key, "k");
|
|
64
|
+
});
|
|
65
|
+
test("a synchronous dispatch failure never throws into the caller (Req 17.4)", () => {
|
|
66
|
+
const publisher = bridgeStorageQueue({
|
|
67
|
+
dispatch() {
|
|
68
|
+
throw new Error("queue down");
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
assert.doesNotThrow(() => publisher.thumbnail("photo.png"));
|
|
72
|
+
assert.doesNotThrow(() => publisher.dispatch("storage.archive", { key: "k" }));
|
|
73
|
+
});
|
|
74
|
+
test("an asynchronous dispatch rejection never throws into the caller (Req 17.4)", async () => {
|
|
75
|
+
const publisher = bridgeStorageQueue({
|
|
76
|
+
dispatch() {
|
|
77
|
+
return Promise.reject(new Error("async queue down"));
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
assert.doesNotThrow(() => publisher.transcode("video.mov"));
|
|
81
|
+
// Give any swallowed rejection a tick to settle without an unhandled error.
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
83
|
+
});
|
|
84
|
+
test("facade wires a queue publisher when config.bridges.queue is present (Req 17.1)", async () => {
|
|
85
|
+
const queue = recordingQueue();
|
|
86
|
+
const storage = createStorage({ provider: "memory", bridges: { queue } });
|
|
87
|
+
// The bridge is constructed and held; storage operations remain unaffected.
|
|
88
|
+
const metadata = await storage.put("photo.png", "bytes", { contentType: "image/png" });
|
|
89
|
+
assert.equal(metadata.key, "photo.png");
|
|
90
|
+
assert.equal(await storage.exists("photo.png"), true);
|
|
91
|
+
});
|
|
92
|
+
test("a throwing queue never breaks storage operations (Req 17.4)", async () => {
|
|
93
|
+
const throwingQueue = {
|
|
94
|
+
dispatch() {
|
|
95
|
+
throw new Error("queue down");
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
const storage = createStorage({ provider: "memory", bridges: { queue: throwingQueue } });
|
|
99
|
+
const metadata = await storage.put("k", "v");
|
|
100
|
+
assert.equal(metadata.key, "k");
|
|
101
|
+
await storage.delete("k");
|
|
102
|
+
assert.equal(await storage.exists("k"), false);
|
|
103
|
+
});
|
|
104
|
+
test("with no queue bridge configured, operations proceed unaffected (Req 17.3)", async () => {
|
|
105
|
+
const storage = createStorage({ provider: "memory" });
|
|
106
|
+
const metadata = await storage.put("k", "v");
|
|
107
|
+
assert.equal(metadata.key, "k");
|
|
108
|
+
await storage.delete("k");
|
|
109
|
+
assert.equal(await storage.exists("k"), false);
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=queue-bridge.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-bridge.test.js","sourceRoot":"","sources":["../../src/tests/queue-bridge.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-driver.test.d.ts","sourceRoot":"","sources":["../../src/tests/r2-driver.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Unit tests for the CloudflareR2Driver submodule (task 28.3). R2 is the shared
|
|
2
|
+
// S3-style base pinned to the "r2" provider name with an R2 endpoint/credential
|
|
3
|
+
// specialization for the build-its-own-client path. A fake in-memory
|
|
4
|
+
// S3ClientLike stands in for the provider SDK so the driver is exercised with no
|
|
5
|
+
// external service and no SDK dependency.
|
|
6
|
+
//
|
|
7
|
+
// Coverage:
|
|
8
|
+
// - createCloudflareR2Driver wraps an injected S3ClientLike, name === "r2"
|
|
9
|
+
// - the CloudflareR2Driver class is constructible and pinned to "r2"
|
|
10
|
+
// - primitives round-trip typed metadata across the client boundary
|
|
11
|
+
// - the shared driver contract-conformance suite passes against the driver
|
|
12
|
+
// - connectCloudflareR2Driver validates required config (StorageConfigError)
|
|
13
|
+
// - connectCloudflareR2Driver throws StorageConfigError when the S3 SDK is
|
|
14
|
+
// absent and no client was injected (the SDK is not installed here)
|
|
15
|
+
//
|
|
16
|
+
// Requirements: 2.1, 2.3, 3.3
|
|
17
|
+
import test from "node:test";
|
|
18
|
+
import assert from "node:assert/strict";
|
|
19
|
+
import { CloudflareR2Driver, createCloudflareR2Driver, connectCloudflareR2Driver, } from "../drivers/r2.js";
|
|
20
|
+
import { StorageConfigError } from "../errors.js";
|
|
21
|
+
import { registerStorageDriverContractTests } from "./contract.js";
|
|
22
|
+
const FIXED_NOW = 1_700_000_000_000;
|
|
23
|
+
const fixedClock = () => FIXED_NOW;
|
|
24
|
+
function bytes(str) {
|
|
25
|
+
return new TextEncoder().encode(str);
|
|
26
|
+
}
|
|
27
|
+
/** A minimal in-memory S3ClientLike standing in for the R2 (S3-API) client. */
|
|
28
|
+
function makeFakeClient() {
|
|
29
|
+
const objects = new Map(); // key -> { body, contentType, metadata }
|
|
30
|
+
return {
|
|
31
|
+
async putObject({ key, body, contentType, metadata }) {
|
|
32
|
+
objects.set(key, {
|
|
33
|
+
body: body.slice(),
|
|
34
|
+
contentType,
|
|
35
|
+
metadata: metadata ? { ...metadata } : undefined,
|
|
36
|
+
});
|
|
37
|
+
return { etag: `"${key}-etag"` };
|
|
38
|
+
},
|
|
39
|
+
async getObject({ key }) {
|
|
40
|
+
const obj = objects.get(key);
|
|
41
|
+
if (obj === undefined) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
body: obj.body.slice(),
|
|
46
|
+
contentType: obj.contentType,
|
|
47
|
+
etag: `"${key}-etag"`,
|
|
48
|
+
size: obj.body.byteLength,
|
|
49
|
+
lastModified: FIXED_NOW,
|
|
50
|
+
metadata: obj.metadata,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
async headObject({ key }) {
|
|
54
|
+
const obj = objects.get(key);
|
|
55
|
+
if (obj === undefined) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
contentType: obj.contentType,
|
|
60
|
+
etag: `"${key}-etag"`,
|
|
61
|
+
size: obj.body.byteLength,
|
|
62
|
+
lastModified: FIXED_NOW,
|
|
63
|
+
metadata: obj.metadata,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
async deleteObject({ key }) {
|
|
67
|
+
objects.delete(key);
|
|
68
|
+
},
|
|
69
|
+
async listObjects({ prefix }) {
|
|
70
|
+
return [...objects.entries()]
|
|
71
|
+
.filter(([key]) => key.startsWith(prefix))
|
|
72
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
73
|
+
.map(([key, obj]) => ({ key, size: obj.body.byteLength, updatedAt: FIXED_NOW }));
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// ── name / construction ─────────────────────────────────────────────────────────
|
|
78
|
+
test("createCloudflareR2Driver wraps an injected client and names it r2", () => {
|
|
79
|
+
const driver = createCloudflareR2Driver(makeFakeClient(), { clock: fixedClock });
|
|
80
|
+
assert.equal(driver.name, "r2");
|
|
81
|
+
});
|
|
82
|
+
test("CloudflareR2Driver class is constructible and pinned to r2", async () => {
|
|
83
|
+
const driver = new CloudflareR2Driver(makeFakeClient(), { clock: fixedClock });
|
|
84
|
+
assert.equal(driver.name, "r2");
|
|
85
|
+
const meta = await driver.put("k", bytes("v"), {});
|
|
86
|
+
assert.equal(meta.key, "k");
|
|
87
|
+
});
|
|
88
|
+
// ── primitives round-trip across the client boundary ─────────────────────────────
|
|
89
|
+
test("put/get round-trips bytes and typed metadata", async () => {
|
|
90
|
+
const driver = createCloudflareR2Driver(makeFakeClient(), { clock: fixedClock });
|
|
91
|
+
const content = bytes("cloudflare r2 payload");
|
|
92
|
+
await driver.put("docs/r2.txt", content, {
|
|
93
|
+
contentType: "text/plain",
|
|
94
|
+
owner: "user-9",
|
|
95
|
+
tenant: "tenant-z",
|
|
96
|
+
accessLevel: "public",
|
|
97
|
+
custom: { region: "auto" },
|
|
98
|
+
});
|
|
99
|
+
const result = await driver.get("docs/r2.txt");
|
|
100
|
+
assert.equal(result.found, true);
|
|
101
|
+
assert.deepEqual(result.bytes, content);
|
|
102
|
+
assert.equal(result.metadata.contentType, "text/plain");
|
|
103
|
+
assert.equal(result.metadata.owner, "user-9");
|
|
104
|
+
assert.equal(result.metadata.tenant, "tenant-z");
|
|
105
|
+
assert.equal(result.metadata.accessLevel, "public");
|
|
106
|
+
assert.deepEqual(result.metadata.custom, { region: "auto" });
|
|
107
|
+
});
|
|
108
|
+
test("get on a missing key reports found:false (not an error)", async () => {
|
|
109
|
+
const driver = createCloudflareR2Driver(makeFakeClient(), { clock: fixedClock });
|
|
110
|
+
const result = await driver.get("missing");
|
|
111
|
+
assert.equal(result.found, false);
|
|
112
|
+
});
|
|
113
|
+
// ── build-its-own-client path (SDK isolation) ────────────────────────────────────
|
|
114
|
+
test("connectCloudflareR2Driver validates required config", async () => {
|
|
115
|
+
await assert.rejects(() => connectCloudflareR2Driver({
|
|
116
|
+
accountId: "",
|
|
117
|
+
bucket: "media",
|
|
118
|
+
accessKeyId: "AK",
|
|
119
|
+
secretAccessKey: "SK",
|
|
120
|
+
}), (error) => {
|
|
121
|
+
assert.ok(error instanceof StorageConfigError);
|
|
122
|
+
assert.equal(error.provider, "r2");
|
|
123
|
+
return true;
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
test("connectCloudflareR2Driver throws StorageConfigError when the S3 SDK is absent", async () => {
|
|
127
|
+
// The optional @aws-sdk/client-s3 peer is not installed in this workspace, so
|
|
128
|
+
// the lazy dynamic import must fail and surface a StorageConfigError rather
|
|
129
|
+
// than an unhandled module-resolution error.
|
|
130
|
+
await assert.rejects(() => connectCloudflareR2Driver({
|
|
131
|
+
accountId: "acct-123",
|
|
132
|
+
bucket: "media",
|
|
133
|
+
accessKeyId: "AK",
|
|
134
|
+
secretAccessKey: "SK",
|
|
135
|
+
}), (error) => {
|
|
136
|
+
assert.ok(error instanceof StorageConfigError);
|
|
137
|
+
assert.equal(error.provider, "r2");
|
|
138
|
+
return true;
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
// ── shared contract-conformance suite ────────────────────────────────────────────
|
|
142
|
+
registerStorageDriverContractTests("r2", () => createCloudflareR2Driver(makeFakeClient(), { clock: fixedClock }), test);
|
|
143
|
+
//# sourceMappingURL=r2-driver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-driver.test.js","sourceRoot":"","sources":["../../src/tests/r2-driver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-bridge.test.d.ts","sourceRoot":"","sources":["../../src/tests/realtime-bridge.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Unit tests for the Realtime integration bridge wired into the storage facade
|
|
2
|
+
// (task 21.3).
|
|
3
|
+
//
|
|
4
|
+
// Verifies the Requirement 19 / 28.3 graceful-degradation semantics over the
|
|
5
|
+
// zero-dependency `memory` provider:
|
|
6
|
+
//
|
|
7
|
+
// - bridgeStorageRealtime broadcasts the correct typed upload event name +
|
|
8
|
+
// payload on the storage upload channel through the structural RealtimeLike
|
|
9
|
+
// bridge (19.1).
|
|
10
|
+
// - a synchronous throw from the realtime layer is fully isolated — never
|
|
11
|
+
// propagates into the caller (19.3).
|
|
12
|
+
// - an asynchronous rejection from the realtime layer is fully isolated (19.3).
|
|
13
|
+
// - with no realtime bridge configured, uploads proceed unaffected (19.3).
|
|
14
|
+
// - a throwing realtime bridge never breaks the streaming/resumable upload path
|
|
15
|
+
// (19.3).
|
|
16
|
+
// - putStream broadcasts upload.started then upload.completed (19.1).
|
|
17
|
+
//
|
|
18
|
+
// Uses the Node.js built-in test runner (node:test); executed via
|
|
19
|
+
// `node --test dist/tests/*.test.js`.
|
|
20
|
+
//
|
|
21
|
+
// Requirements: 19.1, 19.2, 19.3, 28.3
|
|
22
|
+
import test from "node:test";
|
|
23
|
+
import assert from "node:assert/strict";
|
|
24
|
+
import { Readable } from "node:stream";
|
|
25
|
+
import { createStorage, bridgeStorageRealtime, STORAGE_UPLOAD_CHANNEL } from "../index.js";
|
|
26
|
+
/** A recording RealtimeLike double capturing every broadcast (channel, event, payload). */
|
|
27
|
+
function recordingRealtime() {
|
|
28
|
+
const broadcasts = [];
|
|
29
|
+
return {
|
|
30
|
+
broadcasts,
|
|
31
|
+
broadcast(channel, event, payload) {
|
|
32
|
+
broadcasts.push({ channel, event, payload: payload });
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
test("bridgeStorageRealtime broadcasts typed upload events on the upload channel (Req 19.1)", () => {
|
|
37
|
+
const realtime = recordingRealtime();
|
|
38
|
+
const publisher = bridgeStorageRealtime(realtime);
|
|
39
|
+
publisher.started("video.mp4");
|
|
40
|
+
publisher.progress("video.mp4", 512, 1024);
|
|
41
|
+
publisher.completed("video.mp4");
|
|
42
|
+
publisher.failed("video.mp4", "boom");
|
|
43
|
+
assert.deepEqual(realtime.broadcasts.map((b) => b.event), ["upload.started", "upload.progress", "upload.completed", "upload.failed"]);
|
|
44
|
+
// All broadcasts go out on the single upload channel.
|
|
45
|
+
for (const b of realtime.broadcasts) {
|
|
46
|
+
assert.equal(b.channel, STORAGE_UPLOAD_CHANNEL);
|
|
47
|
+
assert.equal(b.payload.key, "video.mp4");
|
|
48
|
+
}
|
|
49
|
+
assert.equal(realtime.broadcasts[1].payload.bytesTransferred, 512);
|
|
50
|
+
assert.equal(realtime.broadcasts[1].payload.totalBytes, 1024);
|
|
51
|
+
assert.equal(realtime.broadcasts[3].payload.error, "boom");
|
|
52
|
+
});
|
|
53
|
+
test("broadcast() passes an already-named event straight through (Req 19.1)", () => {
|
|
54
|
+
const realtime = recordingRealtime();
|
|
55
|
+
const publisher = bridgeStorageRealtime(realtime);
|
|
56
|
+
publisher.broadcast("upload.completed", { key: "k" });
|
|
57
|
+
assert.equal(realtime.broadcasts.length, 1);
|
|
58
|
+
assert.equal(realtime.broadcasts[0].event, "upload.completed");
|
|
59
|
+
assert.equal(realtime.broadcasts[0].payload.key, "k");
|
|
60
|
+
});
|
|
61
|
+
test("a synchronous broadcast failure never throws into the caller (Req 19.3)", () => {
|
|
62
|
+
const publisher = bridgeStorageRealtime({
|
|
63
|
+
broadcast() {
|
|
64
|
+
throw new Error("realtime down");
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
assert.doesNotThrow(() => publisher.started("k"));
|
|
68
|
+
assert.doesNotThrow(() => publisher.completed("k"));
|
|
69
|
+
assert.doesNotThrow(() => publisher.broadcast("upload.failed", { key: "k" }));
|
|
70
|
+
});
|
|
71
|
+
test("an asynchronous broadcast rejection never throws into the caller (Req 19.3)", async () => {
|
|
72
|
+
const publisher = bridgeStorageRealtime({
|
|
73
|
+
broadcast() {
|
|
74
|
+
return Promise.reject(new Error("async realtime down"));
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
assert.doesNotThrow(() => publisher.progress("k", 1, 2));
|
|
78
|
+
// Give any swallowed rejection a tick to settle without an unhandled error.
|
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
80
|
+
});
|
|
81
|
+
test("facade broadcasts upload.started then upload.completed on putStream (Req 19.1)", async () => {
|
|
82
|
+
const realtime = recordingRealtime();
|
|
83
|
+
const storage = createStorage({ provider: "memory", bridges: { realtime } });
|
|
84
|
+
await storage.putStream("big.bin", Readable.from([Buffer.from("hello world")]));
|
|
85
|
+
assert.deepEqual(realtime.broadcasts.map((b) => b.event), ["upload.started", "upload.completed"]);
|
|
86
|
+
assert.equal(realtime.broadcasts[0].payload.key, "big.bin");
|
|
87
|
+
assert.equal(await storage.exists("big.bin"), true);
|
|
88
|
+
});
|
|
89
|
+
test("a throwing realtime bridge never breaks the upload path (Req 19.3)", async () => {
|
|
90
|
+
const throwingRealtime = {
|
|
91
|
+
broadcast() {
|
|
92
|
+
throw new Error("realtime down");
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
const storage = createStorage({ provider: "memory", bridges: { realtime: throwingRealtime } });
|
|
96
|
+
const metadata = await storage.putStream("k", Readable.from([Buffer.from("data")]));
|
|
97
|
+
assert.equal(metadata.key, "k");
|
|
98
|
+
assert.equal(await storage.exists("k"), true);
|
|
99
|
+
});
|
|
100
|
+
test("with no realtime bridge configured, uploads proceed unaffected (Req 19.3)", async () => {
|
|
101
|
+
const storage = createStorage({ provider: "memory" });
|
|
102
|
+
const metadata = await storage.putStream("k", Readable.from([Buffer.from("data")]));
|
|
103
|
+
assert.equal(metadata.key, "k");
|
|
104
|
+
assert.equal(await storage.exists("k"), true);
|
|
105
|
+
});
|
|
106
|
+
//# sourceMappingURL=realtime-bridge.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-bridge.test.js","sourceRoot":"","sources":["../../src/tests/realtime-bridge.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resumable.test.d.ts","sourceRoot":"","sources":["../../src/tests/resumable.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Unit tests for the resumable upload manager wired into the storage facade
|
|
2
|
+
// (task 11.1).
|
|
3
|
+
//
|
|
4
|
+
// Verifies the Requirement 7 semantics over the zero-dependency `memory`
|
|
5
|
+
// provider (which has no native `resumable` capability, so the manager
|
|
6
|
+
// simulates offset-tracked sessions over the driver primitives):
|
|
7
|
+
//
|
|
8
|
+
// - startUpload creates a session and returns a session id (7.1).
|
|
9
|
+
// - resumeUpload with the full content creates an object byte-identical to an
|
|
10
|
+
// equivalent single put / uninterrupted upload (7.2, 7.3).
|
|
11
|
+
// - an interrupted resume followed by a second resume with the full content
|
|
12
|
+
// continues from the last persisted offset and yields identical bytes (7.2,
|
|
13
|
+
// 7.3).
|
|
14
|
+
// - cancelUpload before completion discards the session and creates no object
|
|
15
|
+
// (7.4), and a subsequent resume of that session is rejected.
|
|
16
|
+
// - cancelUpload of an already-completing session is ignored so the object is
|
|
17
|
+
// still created (7.5).
|
|
18
|
+
//
|
|
19
|
+
// Uses the Node.js built-in test runner (node:test); executed via
|
|
20
|
+
// `node --test dist/tests/*.test.js`.
|
|
21
|
+
//
|
|
22
|
+
// Requirements: 7.1, 7.2, 7.3, 7.4, 7.5
|
|
23
|
+
import test from "node:test";
|
|
24
|
+
import assert from "node:assert/strict";
|
|
25
|
+
import { Readable } from "node:stream";
|
|
26
|
+
import { createStorage } from "../facade.js";
|
|
27
|
+
import { StorageError } from "../errors.js";
|
|
28
|
+
/** A fixed clock for deterministic timestamps. */
|
|
29
|
+
const fixedClock = () => 1_700_000_000_000;
|
|
30
|
+
test("startUpload returns a session id (Req 7.1)", async () => {
|
|
31
|
+
const storage = createStorage({ provider: "memory", clock: fixedClock });
|
|
32
|
+
const sessionId = await storage.startUpload("uploads/file.bin", { contentType: "text/plain" });
|
|
33
|
+
assert.equal(typeof sessionId, "string");
|
|
34
|
+
assert.ok(sessionId.length > 0);
|
|
35
|
+
});
|
|
36
|
+
test("resumeUpload with the full content equals a single put (Req 7.2, 7.3)", async () => {
|
|
37
|
+
const storage = createStorage({ provider: "memory", clock: fixedClock });
|
|
38
|
+
const payload = Buffer.from("the quick brown fox jumps over the lazy dog");
|
|
39
|
+
const sessionId = await storage.startUpload("k", { contentType: "text/plain" });
|
|
40
|
+
const meta = await storage.resumeUpload(sessionId, Readable.from([payload]));
|
|
41
|
+
assert.equal(meta.key, "k");
|
|
42
|
+
assert.equal(meta.size, payload.byteLength);
|
|
43
|
+
assert.equal(meta.contentType, "text/plain");
|
|
44
|
+
const got = await storage.get("k");
|
|
45
|
+
assert.equal(got.found, true);
|
|
46
|
+
assert.deepEqual(Buffer.from(got.bytes), payload);
|
|
47
|
+
// Byte-identical to an equivalent single, uninterrupted put.
|
|
48
|
+
const reference = createStorage({ provider: "memory", clock: fixedClock });
|
|
49
|
+
const single = await reference.put("k", new Uint8Array(payload), { contentType: "text/plain" });
|
|
50
|
+
assert.equal(meta.checksum, single.checksum);
|
|
51
|
+
assert.equal(meta.size, single.size);
|
|
52
|
+
});
|
|
53
|
+
test("interrupted resume continues from the persisted offset (Req 7.2, 7.3)", async () => {
|
|
54
|
+
const storage = createStorage({ provider: "memory", clock: fixedClock });
|
|
55
|
+
const payload = Buffer.from("0123456789ABCDEFGHIJ");
|
|
56
|
+
const sessionId = await storage.startUpload("resumable/obj", {});
|
|
57
|
+
// First resume is interrupted partway: the source stream errors after
|
|
58
|
+
// emitting the first 8 bytes, so only those 8 bytes are persisted.
|
|
59
|
+
const firstHalf = payload.subarray(0, 8);
|
|
60
|
+
const failing = new Readable({
|
|
61
|
+
read() {
|
|
62
|
+
this.push(firstHalf);
|
|
63
|
+
this.destroy(new Error("network interruption"));
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
await assert.rejects(() => storage.resumeUpload(sessionId, failing), /network interruption/);
|
|
67
|
+
// No final object exists yet.
|
|
68
|
+
assert.equal(await storage.exists("resumable/obj"), false);
|
|
69
|
+
// Resume again with the FULL content: the manager skips the persisted prefix
|
|
70
|
+
// and appends the remainder, completing the upload.
|
|
71
|
+
const meta = await storage.resumeUpload(sessionId, Readable.from([payload]));
|
|
72
|
+
assert.equal(meta.size, payload.byteLength);
|
|
73
|
+
const got = await storage.get("resumable/obj");
|
|
74
|
+
assert.equal(got.found, true);
|
|
75
|
+
assert.deepEqual(Buffer.from(got.bytes), payload);
|
|
76
|
+
});
|
|
77
|
+
test("cancelUpload before completion discards the session (Req 7.4)", async () => {
|
|
78
|
+
const storage = createStorage({ provider: "memory", clock: fixedClock });
|
|
79
|
+
const sessionId = await storage.startUpload("cancel/me", {});
|
|
80
|
+
await storage.cancelUpload(sessionId);
|
|
81
|
+
// No object was created.
|
|
82
|
+
assert.equal(await storage.exists("cancel/me"), false);
|
|
83
|
+
// A cancelled session can no longer be resumed.
|
|
84
|
+
await assert.rejects(() => storage.resumeUpload(sessionId, Readable.from([Buffer.from("data")])), StorageError);
|
|
85
|
+
});
|
|
86
|
+
test("cancelUpload while completing lets the upload finish (Req 7.5)", async () => {
|
|
87
|
+
const storage = createStorage({ provider: "memory", clock: fixedClock });
|
|
88
|
+
const payload = Buffer.from("committing payload");
|
|
89
|
+
const sessionId = await storage.startUpload("committing/obj", {});
|
|
90
|
+
// Start the resume but do not await it yet; a paused stream lets us interleave
|
|
91
|
+
// a cancel. The stream emits its content then ends, driving the session into
|
|
92
|
+
// its completing phase.
|
|
93
|
+
const resumePromise = storage.resumeUpload(sessionId, Readable.from([payload]));
|
|
94
|
+
// Cancel concurrently. Because the manager marks the session completing once
|
|
95
|
+
// the stream is consumed, this cancel is either a no-op (session gone) or
|
|
96
|
+
// ignored (already completing) — either way the object must be created.
|
|
97
|
+
const finalMeta = await resumePromise;
|
|
98
|
+
await storage.cancelUpload(sessionId);
|
|
99
|
+
assert.equal(finalMeta.key, "committing/obj");
|
|
100
|
+
const got = await storage.get("committing/obj");
|
|
101
|
+
assert.equal(got.found, true);
|
|
102
|
+
assert.deepEqual(Buffer.from(got.bytes), payload);
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=resumable.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resumable.test.js","sourceRoot":"","sources":["../../src/tests/resumable.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3-base.test.d.ts","sourceRoot":"","sources":["../../src/tests/s3-base.test.ts"],"names":[],"mappings":""}
|