Files
suaveplan/packages/networking/drive/drive-plugin-chunks/src/plugin.ingest-failed.test.ts
2026-05-27 23:47:53 +01:00

150 lines
5.0 KiB
TypeScript

import {
getPublicKey,
randomPrivateKey,
} from "@suaveplan/crypto-curve/ed25519";
import { Drive, type DriveStorage } from "@suaveplan/drive";
import { MemoryStorage } from "@suaveplan/storage/memory";
import { describe, expect, it } from "@suaveplan/testing/runner";
import { CHUNKS_CODES, ChunksError } from "./error-codes.js";
import { chunksPlugin } from "./plugin.js";
function makeFailingStorage(failOn: (key: string) => boolean): DriveStorage {
const inner = new MemoryStorage();
return Object.assign(inner, {
id: "default",
set(key: string, value: Uint8Array): Promise<void> {
if (failOn(key)) {
throw new Error(`disk full for ${key}`);
}
return MemoryStorage.prototype.set.call(inner, key, value);
},
}) as unknown as DriveStorage;
}
function makeFailingStorageNonError(
failOn: (key: string) => boolean
): DriveStorage {
const inner = new MemoryStorage();
return Object.assign(inner, {
id: "default",
set(key: string, value: Uint8Array): Promise<void> {
if (failOn(key)) {
const literal: unknown = "literal-throwable";
throw literal;
}
return MemoryStorage.prototype.set.call(inner, key, value);
},
}) as unknown as DriveStorage;
}
describe("CHUNKS-INGEST-FAILED — non-ChunksError storage failures wrap as INGEST_FAILED", () => {
it("wraps a manifest-write Error into ChunksError(INGEST_FAILED)", {
timeout: 5000,
}, async () => {
const privKey = randomPrivateKey();
const pubKey = getPublicKey(privKey);
const storage = makeFailingStorage((k) => k.startsWith("manifest:"));
const drive = new Drive(storage);
await drive.initialize();
const chunks = await drive.usePlugin(
chunksPlugin({
serverPrivKey: privKey,
serverPubKey: pubKey,
chunkSize: 1024,
})
);
await drive.write("test.txt", new TextEncoder().encode("hello"));
try {
await chunks.ingest("test.txt");
throw new Error("expected ingest to throw");
} catch (e) {
expect(e).toBeInstanceOf(ChunksError);
expect((e as ChunksError).code).toBe(CHUNKS_CODES.INGEST_FAILED);
expect((e as ChunksError).message).toContain("disk full");
}
await drive.dispose();
});
it("passes through a ChunksError thrown inside the ingest path without re-wrapping", {
timeout: 5000,
}, async () => {
const privKey = randomPrivateKey();
const pubKey = getPublicKey(privKey);
const inner = new MemoryStorage();
const chunksErrorStorage = Object.assign(inner, {
id: "default",
set(key: string, value: Uint8Array): Promise<void> {
if (key.startsWith("manifest:")) {
throw new ChunksError(
CHUNKS_CODES.SIG_INVALID,
"synthetic: bypass wrapping"
);
}
return MemoryStorage.prototype.set.call(inner, key, value);
},
}) as unknown as DriveStorage;
const drive = new Drive(chunksErrorStorage);
await drive.initialize();
const chunks = await drive.usePlugin(
chunksPlugin({
serverPrivKey: privKey,
serverPubKey: pubKey,
chunkSize: 1024,
})
);
await drive.write("test.txt", new TextEncoder().encode("hello"));
try {
await chunks.ingest("test.txt");
throw new Error("expected ingest to throw");
} catch (e) {
expect(e).toBeInstanceOf(ChunksError);
expect((e as ChunksError).code).toBe(CHUNKS_CODES.SIG_INVALID);
expect((e as ChunksError).message).toContain(
"synthetic: bypass wrapping"
);
}
await drive.dispose();
});
it("wraps a non-Error throwable into ChunksError(INGEST_FAILED)", {
timeout: 5000,
}, async () => {
const privKey = randomPrivateKey();
const pubKey = getPublicKey(privKey);
const storage = makeFailingStorageNonError((k) =>
k.startsWith("manifest:")
);
const drive = new Drive(storage);
await drive.initialize();
const chunks = await drive.usePlugin(
chunksPlugin({
serverPrivKey: privKey,
serverPubKey: pubKey,
chunkSize: 1024,
})
);
await drive.write("test.txt", new TextEncoder().encode("hello"));
try {
await chunks.ingest("test.txt");
throw new Error("expected ingest to throw");
} catch (e) {
expect(e).toBeInstanceOf(ChunksError);
expect((e as ChunksError).code).toBe(CHUNKS_CODES.INGEST_FAILED);
expect((e as ChunksError).message).toContain("literal-throwable");
}
await drive.dispose();
});
});