Files
tech-log-frontend/tests/unit/realtime/result.test.ts
T

169 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
isRealtimeResult,
realtimeFailure,
realtimeSuccess,
snapshotRealtimeResult,
} from "../../../src/adapters/realtime/result.ts";
function isString(value: unknown): value is string {
return typeof value === "string";
}
describe("realtime result boundary", () => {
it("uses an immutable one-shot snapshot instead of rereading a mutable result", () => {
const source = { ok: true, value: "captured" };
const captured = snapshotRealtimeResult(source, (value): value is string => {
source.value = "changed-during-validation";
return value === "captured";
});
source.value = "changed-after-validation";
expect(captured).toEqual(
realtimeSuccess("captured"),
);
expect(Object.isFrozen(captured)).toBe(true);
expect(isRealtimeResult(source, isString)).toBe(false);
});
it("canonicalizes failure fields before the source can change", () => {
const source = {
ok: false,
error: {
kind: "FORBIDDEN",
operation: "RECOVER",
retryable: false,
},
};
const captured = snapshotRealtimeResult(
source,
(_value): _value is never => false,
);
source.error.kind = "PROVIDER_UNAVAILABLE";
source.error.operation = "CONNECT";
source.error.retryable = true;
expect(captured).toEqual(
realtimeFailure("FORBIDDEN", "RECOVER", false),
);
expect(
captured && !captured.ok
? Object.isFrozen(captured.error)
: false,
).toBe(true);
});
it("rejects accessors without invoking them", () => {
let outerReads = 0;
const outerAccessor = Object.defineProperties(
{},
{
ok: {
enumerable: true,
get() {
outerReads += 1;
return true;
},
},
value: {
enumerable: true,
value: "safe",
},
},
);
let nestedReads = 0;
const nestedAccessor = Object.defineProperties(
{},
{
kind: {
enumerable: true,
get() {
nestedReads += 1;
return "FORBIDDEN";
},
},
operation: {
enumerable: true,
value: "RECOVER",
},
retryable: {
enumerable: true,
value: false,
},
},
);
expect(
snapshotRealtimeResult(outerAccessor, isString),
).toBeNull();
expect(
snapshotRealtimeResult(
{ ok: false, error: nestedAccessor },
(_value): _value is never => false,
),
).toBeNull();
expect(outerReads).toBe(0);
expect(nestedReads).toBe(0);
});
it("consumes proxy fields only from one descriptor snapshot", () => {
const propertyReads: PropertyKey[] = [];
const descriptorReads = new Map<PropertyKey, number>();
const source = new Proxy(
{ ok: true, value: "descriptor-value" },
{
get(_target, key) {
propertyReads.push(key);
return key === "value" ? "get-trap-value" : false;
},
getOwnPropertyDescriptor(target, key) {
descriptorReads.set(
key,
(descriptorReads.get(key) ?? 0) + 1,
);
return Reflect.getOwnPropertyDescriptor(target, key);
},
},
);
expect(snapshotRealtimeResult(source, isString)).toEqual(
realtimeSuccess("descriptor-value"),
);
expect(propertyReads).toEqual([]);
expect(descriptorReads).toEqual(
new Map<PropertyKey, number>([
["ok", 1],
["value", 1],
]),
);
});
it("fails closed when a proxy is revoked", () => {
const revocable = Proxy.revocable(
{ ok: true, value: "safe" },
{},
);
revocable.revoke();
expect(
snapshotRealtimeResult(revocable.proxy, isString),
).toBeNull();
expect(isRealtimeResult(revocable.proxy, isString)).toBe(false);
});
it.each([
{ ok: true, value: "safe", extra: true },
Object.assign(
Object.create({ inherited: true }) as Record<string, unknown>,
{ ok: true, value: "safe" },
),
Object.assign(
{ ok: true, value: "safe" },
{ [Symbol("hidden")]: true },
),
])("rejects extra, inherited and symbol-key shapes", (source) => {
expect(snapshotRealtimeResult(source, isString)).toBeNull();
});
});