Initial Commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
|
||||
describe("dep-graph", () => {
|
||||
it("package has correct name", { timeout: 5000 }, async () => {
|
||||
const pkg = await import("../package.json", { with: { type: "json" } });
|
||||
expect(pkg.default.name).toBe("@suaveplan/storage-s3");
|
||||
});
|
||||
|
||||
it("exports S3 class", { timeout: 30000 }, async () => {
|
||||
const { S3 } = await import("../src/index.js");
|
||||
expect(typeof S3).toBe("function");
|
||||
});
|
||||
|
||||
it("exports S3StorageError", { timeout: 30000 }, async () => {
|
||||
const { S3StorageError } = await import("../src/index.js");
|
||||
expect(typeof S3StorageError).toBe("function");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Vitest global-setup module — drives the Garage S3 fixture for storage-s3
|
||||
* integration tests.
|
||||
*
|
||||
* Uses the public `createIntegrationSetup()` factory from
|
||||
* `@suaveplan/testing-integration`. The Bun-NAPI shim that lets testcontainers
|
||||
* load under Bun is auto-installed by the re-exported
|
||||
* `@suaveplan/testing-integration/setup/global-setup` module — no per-consumer
|
||||
* wiring is needed.
|
||||
*/
|
||||
import { createIntegrationSetup } from "@suaveplan/testing-integration";
|
||||
import { GarageS3Fixture } from "@suaveplan/testing-integration-garage-s3";
|
||||
|
||||
// Default `ghcr.io/deuxfleurs/garage:v2.0.0` returns 403 for unauthenticated
|
||||
// pulls — mirror to Docker Hub.
|
||||
export const fixtures = {
|
||||
"garage-s3": new GarageS3Fixture({ image: "dxflrs/garage:v2.0.0" }),
|
||||
};
|
||||
|
||||
export const { inject } = createIntegrationSetup(fixtures);
|
||||
|
||||
// Re-export Vitest's setup/teardown contract from testing-integration's
|
||||
// public global-setup module. Vitest discovers them by name when this file
|
||||
// is wired as `test.globalSetup` in `vitest.integration.config.ts`.
|
||||
export {
|
||||
setup,
|
||||
teardown,
|
||||
} from "@suaveplan/testing-integration/setup/global-setup";
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Garage-backed integration test for `S3.setPartial`.
|
||||
*
|
||||
* Pins STORAGE-01..03 (set-partial semantics) against a real S3 wire so the
|
||||
* read-modify-write fallback survives AWS-SDK body encoding, content-length,
|
||||
* and range-read round-trips that an in-memory FakeS3Client cannot exercise.
|
||||
*
|
||||
* Skips the suite when the GarageS3Fixture failed to start (no Docker /
|
||||
* env-bridge incomplete). Set `GARAGE_S3_ENDPOINT`,
|
||||
* `GARAGE_S3_ADMIN_ENDPOINT`, `GARAGE_S3_ACCESS_KEY_ID`,
|
||||
* `GARAGE_S3_SECRET_ACCESS_KEY` to bypass container startup and use an
|
||||
* existing Garage instance.
|
||||
*/
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
inject as vitestInject,
|
||||
} from "@suaveplan/testing/runner";
|
||||
import { S3 } from "../../src/s3/s3.js";
|
||||
import { inject } from "./setup.js";
|
||||
|
||||
const failed =
|
||||
(vitestInject as (k: string) => string[] | null | undefined)(
|
||||
"__integration_failed__"
|
||||
) ?? [];
|
||||
const garageUnavailable = failed.includes("garage-s3");
|
||||
|
||||
describe.skipIf(garageUnavailable)("S3.setPartial — Garage backed", () => {
|
||||
let storage: S3;
|
||||
let key: string;
|
||||
|
||||
beforeEach(() => {
|
||||
const cfg = inject("garage-s3");
|
||||
storage = new S3({
|
||||
bucket: cfg.bucket,
|
||||
region: cfg.region,
|
||||
endpoint: cfg.endpoint,
|
||||
accessKeyId: cfg.accessKeyId,
|
||||
secretAccessKey: cfg.secretAccessKey,
|
||||
});
|
||||
key = `set-partial/${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await storage.delete(key).catch(() => undefined);
|
||||
});
|
||||
|
||||
it("creates a sparse zero-filled object when key does not exist", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
await storage.setPartial(key, 4, new Uint8Array([0xaa, 0xbb]));
|
||||
const got = await storage.get(key);
|
||||
expect(got).toEqual(
|
||||
new Uint8Array([0x00, 0x00, 0x00, 0x00, 0xaa, 0xbb])
|
||||
);
|
||||
});
|
||||
|
||||
it("extends an existing shorter object with zero gap", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
await storage.set(key, new Uint8Array([0x01, 0x02]));
|
||||
await storage.setPartial(key, 5, new Uint8Array([0xcc]));
|
||||
const got = await storage.get(key);
|
||||
expect(got).toEqual(
|
||||
new Uint8Array([0x01, 0x02, 0x00, 0x00, 0x00, 0xcc])
|
||||
);
|
||||
});
|
||||
|
||||
it("overwrites overlap and preserves surrounding bytes", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
await storage.set(key, new Uint8Array([1, 2, 3, 4, 5, 6]));
|
||||
await storage.setPartial(key, 2, new Uint8Array([0xff, 0xff]));
|
||||
const got = await storage.get(key);
|
||||
expect(got).toEqual(new Uint8Array([1, 2, 0xff, 0xff, 5, 6]));
|
||||
});
|
||||
|
||||
it("zero-length writes are a no-op", { timeout: 60_000 }, async () => {
|
||||
await storage.set(key, new Uint8Array([0x01, 0x02, 0x03]));
|
||||
await storage.setPartial(key, 1, new Uint8Array(0));
|
||||
const got = await storage.get(key);
|
||||
expect(got).toEqual(new Uint8Array([0x01, 0x02, 0x03]));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Integration-test wiring for storage-s3.
|
||||
*
|
||||
* - `globalSetup` — absolute path to `./global-setup.ts`. Vitest loads this
|
||||
* file as the global-setup module; it imports the bun-napi-shim before
|
||||
* testcontainers loads, then registers the Garage fixture via the public
|
||||
* `createIntegrationSetup()` factory.
|
||||
* - `inject` — typed accessor for the published fixture configs. Re-exported
|
||||
* from `./global-setup.js` (where the typed registry was constructed).
|
||||
*/
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export { inject } from "./global-setup.js";
|
||||
|
||||
// Vitest's `globalSetup` MUST point at storage-s3's own `global-setup.ts`
|
||||
// (which loads `bun-napi-shim` first), NOT the bare path returned by
|
||||
// `createIntegrationSetup` — the testing-integration core global-setup has
|
||||
// no shim and would crash Bun before testcontainers starts.
|
||||
export const globalSetup = fileURLToPath(
|
||||
new URL("./global-setup.ts", import.meta.url)
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
// Polyfills for test environment
|
||||
Reference in New Issue
Block a user