Initial Commit
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* DMP-18 / DMP-19 — `ChunkErrorEnvelope` shape and codes;
|
||||
* `GENERATION_STALE` includes responder generation.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import {
|
||||
DMP_CODES,
|
||||
DmpError,
|
||||
decodeErrorPayload,
|
||||
encodeErrorPayload,
|
||||
} from "../src/index.js";
|
||||
import type {
|
||||
ChunkErrorCode,
|
||||
ChunkErrorPayload,
|
||||
} from "../src/types/protocol.types.js";
|
||||
|
||||
const FILE_ID = "9".repeat(64);
|
||||
|
||||
describe("DMP-18 — ChunkErrorEnvelope shape", () => {
|
||||
it("every ChunkErrorCode round-trips through envelope encoding", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const codes: readonly ChunkErrorCode[] = [
|
||||
"NOT_FOUND",
|
||||
"ACCESS_DENIED",
|
||||
"BANDWIDTH_EXCEEDED",
|
||||
"GENERATION_STALE",
|
||||
"INTERNAL",
|
||||
];
|
||||
for (const code of codes) {
|
||||
const payload: ChunkErrorPayload = {
|
||||
fileId: FILE_ID,
|
||||
correlationId: `req-${code}`,
|
||||
code,
|
||||
...(code === "GENERATION_STALE"
|
||||
? { responderGeneration: 7 }
|
||||
: {}),
|
||||
};
|
||||
const wire = encodeErrorPayload(payload);
|
||||
const round = decodeErrorPayload(wire);
|
||||
expect(round.code).toBe(code);
|
||||
expect(round.correlationId).toBe(`req-${code}`);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects malformed wire bytes", { timeout: 5000 }, () => {
|
||||
try {
|
||||
decodeErrorPayload(new TextEncoder().encode("not json"));
|
||||
throw new Error("expected rejection");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(DmpError);
|
||||
expect((err as DmpError).code).toBe(DMP_CODES.ENVELOPE_MALFORMED);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("DMP-19 — GENERATION_STALE includes responder generation", () => {
|
||||
it("GENERATION_STALE error includes responder's current generation", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const wire = encodeErrorPayload({
|
||||
fileId: FILE_ID,
|
||||
correlationId: "req-1",
|
||||
code: "GENERATION_STALE",
|
||||
responderGeneration: 42,
|
||||
});
|
||||
const decoded = decodeErrorPayload(wire);
|
||||
expect(decoded.responderGeneration).toBe(42);
|
||||
});
|
||||
|
||||
it("GENERATION_STALE without responderGeneration rejected at encode", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
try {
|
||||
encodeErrorPayload({
|
||||
fileId: FILE_ID,
|
||||
correlationId: "req-1",
|
||||
code: "GENERATION_STALE",
|
||||
});
|
||||
throw new Error("expected rejection");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(DmpError);
|
||||
expect((err as DmpError).code).toBe(DMP_CODES.ENVELOPE_MALFORMED);
|
||||
}
|
||||
});
|
||||
|
||||
it("GENERATION_STALE without responderGeneration rejected at decode", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const wire = new TextEncoder().encode(
|
||||
JSON.stringify({
|
||||
fileId: FILE_ID,
|
||||
correlationId: "req-1",
|
||||
code: "GENERATION_STALE",
|
||||
})
|
||||
);
|
||||
try {
|
||||
decodeErrorPayload(wire);
|
||||
throw new Error("expected rejection");
|
||||
} catch (err) {
|
||||
expect(err).toBeInstanceOf(DmpError);
|
||||
expect((err as DmpError).code).toBe(DMP_CODES.ENVELOPE_MALFORMED);
|
||||
}
|
||||
});
|
||||
|
||||
it("optional retryAfterMs field round-trips", { timeout: 5000 }, () => {
|
||||
const wire = encodeErrorPayload({
|
||||
fileId: FILE_ID,
|
||||
correlationId: "req-2",
|
||||
code: "BANDWIDTH_EXCEEDED",
|
||||
retryAfterMs: 5_000,
|
||||
});
|
||||
const decoded = decodeErrorPayload(wire);
|
||||
expect(decoded.retryAfterMs).toBe(5_000);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user