chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:39:12 +08:00
commit d8dcd5f6d1
8604 changed files with 2479390 additions and 0 deletions
@@ -0,0 +1,66 @@
import test from "node:test";
import assert from "node:assert/strict";
import fc from "fast-check";
import { configureProperties } from "../../helpers/propertyConfig.ts";
import { validateComboDAG, resolveNestedComboModels } from "../../../open-sse/services/combo.ts";
configureProperties();
// ComboLike shape: { name: string, models: unknown[] }
// normalizeModelEntry extracts .model (string) from each entry — plain strings work directly.
// "combo:X" notation is NOT used; the resolver checks if models[i] matches combo.name in allCombos.
test("validateComboDAG throws on self-cycle", () => {
// c0 references itself directly — must throw
const combos = [{ name: "c0", models: ["c0"] }];
assert.throws(() => validateComboDAG("c0", combos));
});
test("validateComboDAG throws on indirect cycle (a->b->a)", () => {
const combos = [
{ name: "a", models: ["b"] },
{ name: "b", models: ["a"] },
];
assert.throws(() => validateComboDAG("a", combos));
});
test("validateComboDAG does not throw on acyclic graph", () => {
const combos = [
{ name: "a", models: ["b", "openai/gpt-4o"] },
{ name: "b", models: ["anthropic/claude-3-5-sonnet"] },
];
assert.doesNotThrow(() => validateComboDAG("a", combos));
});
test("resolveNestedComboModels never throws and is cycle-safe", () => {
// Arbitrary combo graph: names c0..cN where each combo's models reference
// other combo names by their exact name string (no "combo:" prefix needed).
const graphArb = fc.integer({ min: 1, max: 5 }).chain((n) => {
const names = Array.from({ length: n }, (_, i) => `c${i}`);
const comboArr = names.map((name, i) => {
// Each combo references up to 2 other combos by name plus one real model
const refs = names.filter((_, j) => j !== i).slice(0, 2);
return { name, models: [...refs, "openai/gpt-4o"] };
});
return fc.constant(comboArr);
});
fc.assert(
fc.property(graphArb, (combos) => {
// resolveNestedComboModels must always return an array (never throw, never loop)
const out = resolveNestedComboModels(combos[0], combos);
assert.ok(Array.isArray(out), "must return array");
})
);
});
test("resolveNestedComboModels returns [] on direct cycle (cycle-safety)", () => {
// Cycle: c0->c1->c0
const combos = [
{ name: "c0", models: ["c1"] },
{ name: "c1", models: ["c0"] },
];
const out = resolveNestedComboModels(combos[0], combos);
// When a cycle is detected, the visited guard returns [] — no infinite loop
assert.ok(Array.isArray(out));
});
@@ -0,0 +1,39 @@
import test from "node:test";
import assert from "node:assert/strict";
import { fakeUpstreamStream } from "../../helpers/fakeUpstreamStream.ts";
test("fakeUpstreamStream emits pushed chunks then closes", async () => {
const { stream, push, close } = fakeUpstreamStream();
push("a");
push("b");
close();
const reader = stream.getReader();
const dec = new TextDecoder();
let out = "";
for (;;) {
const { done, value } = await reader.read();
if (done) break;
out += dec.decode(value);
}
assert.equal(out, "ab");
});
test("fakeUpstreamStream signals cancel when consumer aborts", async () => {
const { stream, onCancel } = fakeUpstreamStream();
let cancelled = false;
onCancel(() => {
cancelled = true;
});
const reader = stream.getReader();
await reader.cancel("client-abort");
assert.equal(cancelled, true);
});
test("fakeUpstreamStream propagates error to reader", async () => {
const { stream, error } = fakeUpstreamStream();
error(new Error("upstream boom"));
const reader = stream.getReader();
await assert.rejects(async () => {
await reader.read();
}, /upstream boom/);
});
+18
View File
@@ -0,0 +1,18 @@
import test from "node:test";
import assert from "node:assert/strict";
import { loadTranslationFixtures, loadSseSequences } from "../../helpers/translationFixtures.ts";
test("fixtures: each pair file has at least one well-formed case", () => {
const cases = loadTranslationFixtures();
assert.ok(cases.length >= 4);
for (const c of cases) {
assert.ok(c.name && c.sourceFormat && c.targetFormat && c.input);
}
});
test("fixtures: sse sequences have chunks and expectedText", () => {
const seqs = loadSseSequences();
assert.ok(seqs.length >= 1);
for (const s of seqs) {
assert.ok(Array.isArray(s.chunks) && typeof s.expectedText === "string");
}
});
@@ -0,0 +1,41 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { goldenSnapshot } from "../../helpers/goldenSnapshot.ts";
// Use an isolated tmpdir so the selftest does not pollute tests/snapshots/
let tmpDir: string;
test("goldenSnapshot writes on first run then matches", (t) => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "golden-selftest-"));
// First run: UPDATE_GOLDEN=1 → writes
process.env.UPDATE_GOLDEN = "1";
goldenSnapshot("selftest/sample", { b: 2, a: 1 }, tmpDir);
delete process.env.UPDATE_GOLDEN;
// Re-run without flag: same value (keys re-ordered) → should pass
assert.doesNotThrow(() => goldenSnapshot("selftest/sample", { a: 1, b: 2 }, tmpDir));
// Mismatch: different value → should throw
assert.throws(() => goldenSnapshot("selftest/sample", { a: 1, b: 3 }, tmpDir));
// Cleanup
fs.rmSync(tmpDir, { recursive: true, force: true });
});
test("goldenSnapshot first-run (no UPDATE_GOLDEN) writes and passes", () => {
const td = fs.mkdtempSync(path.join(os.tmpdir(), "golden-first-run-"));
try {
// File does not exist yet: should write and not throw
assert.doesNotThrow(() => goldenSnapshot("test/value", { x: 42 }, td));
// File now exists: same value should pass
assert.doesNotThrow(() => goldenSnapshot("test/value", { x: 42 }, td));
// File exists: different value should throw
assert.throws(() => goldenSnapshot("test/value", { x: 99 }, td));
} finally {
fs.rmSync(td, { recursive: true, force: true });
}
});
@@ -0,0 +1,42 @@
import test from "node:test";
import assert from "node:assert/strict";
import fc from "fast-check";
import { configureProperties } from "../../helpers/propertyConfig.ts";
import { sanitizeErrorMessage } from "../../../open-sse/utils/error.ts";
configureProperties();
test("sanitizeErrorMessage never leaks a file path / stack frame", () => {
// sanitizeErrorMessage takes only the FIRST LINE of input (observed real behavior:
// it splits on \n and processes only the part before the first newline).
// Stack frames are on subsequent lines and thus already stripped.
// The invariant we test: single-line content containing "at /path/file.ts" has the
// absolute path replaced with "<path>", so the output never contains "at /".
const firstLineWithPath = fc
.string()
.map((s) => s.replace(/\n/g, " ")) // ensure single line
.chain((prefix) =>
fc
.string()
.map((s) => s.replace(/\n/g, " "))
.map((suffix) => `${prefix} at /home/app/open-sse/foo.ts:42:10 ${suffix}`)
);
fc.assert(
fc.property(firstLineWithPath, (input) => {
const out = sanitizeErrorMessage(input);
assert.ok(!out.includes("at /"), `leaked path in: ${JSON.stringify(out)}`);
})
);
});
test("sanitizeErrorMessage terminates on long adversarial input (ReDoS guard)", () => {
fc.assert(
fc.property(fc.integer({ min: 1000, max: 20000 }), (len) => {
const start = process.hrtime.bigint();
sanitizeErrorMessage("a".repeat(len) + "@" + "b".repeat(len) + ".com " + "1".repeat(len));
const ms = Number(process.hrtime.bigint() - start) / 1e6;
assert.ok(ms < 250, `too slow: ${ms}ms for len=${len}`);
})
);
});
@@ -0,0 +1,63 @@
import test from "node:test";
import assert from "node:assert/strict";
import fc from "fast-check";
import { configureProperties } from "../../helpers/propertyConfig.ts";
import { parseSSELine } from "../../../open-sse/utils/streamHelpers.ts";
import { loadSseSequences } from "../../helpers/translationFixtures.ts";
configureProperties();
// parseSSELine is line-oriented: it processes one line at a time.
// The split-mid-line scenario (splitting in the middle of "data: {...}") belongs to
// the stream-layer harness (Task 11). Here we test line-level reconstruction invariance:
// given a complete SSE event line, parseSSELine extracts the correct content regardless
// of what other lines surround it.
function extractTextFromLines(rawContent: string): string {
let text = "";
for (const line of rawContent.split("\n")) {
const parsed = parseSSELine(line);
if (parsed && !(parsed as { done?: boolean }).done) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const delta = (parsed as any)?.choices?.[0]?.delta?.content;
if (typeof delta === "string") text += delta;
}
}
return text;
}
test("SSE text reconstruction is invariant to chunk boundaries (line-level)", () => {
const seqs = loadSseSequences();
// Property: joining all chunks and re-splitting by line yields the same text
// as processing the chunks individually. This tests that parseSSELine is stateless
// and position-invariant (no dependency on which "chunk" a line comes from).
fc.assert(
fc.property(fc.integer({ min: 0, max: seqs.length - 1 }), (idx) => {
const seq = seqs[idx];
const full = seq.chunks.join("");
const reconstructed = extractTextFromLines(full);
assert.equal(reconstructed, seq.expectedText);
})
);
});
test("parseSSELine returns null for non-data lines", () => {
// Non-data lines (comment, event:, id:, retry:, empty) must return null
const nonDataLines = ["", ": comment", "event: update", "id: 42", "retry: 5000"];
for (const line of nonDataLines) {
assert.equal(parseSSELine(line), null, `expected null for line: ${JSON.stringify(line)}`);
}
});
test("parseSSELine returns {done:true} for [DONE] sentinel", () => {
const parsed = parseSSELine("data: [DONE]");
assert.deepEqual(parsed, { done: true });
});
test("parseSSELine correctly parses valid JSON data line", () => {
const line = 'data: {"choices":[{"delta":{"content":"hello"}}]}';
const parsed = parseSSELine(line) as Record<string, unknown>;
assert.ok(parsed && typeof parsed === "object");
// eslint-disable-next-line @typescript-eslint/no-explicit-any
assert.equal((parsed as any)?.choices?.[0]?.delta?.content, "hello");
});
@@ -0,0 +1,19 @@
import test from "node:test";
import { goldenSnapshot } from "../../helpers/goldenSnapshot.ts";
import { loadTranslationFixtures } from "../../helpers/translationFixtures.ts";
import { translateRequest } from "../../../open-sse/translator/index.ts";
// Golden-file tests: freeze translateRequest output per fixture.
// Regenerate with: UPDATE_GOLDEN=1 node --import tsx/esm --test tests/unit/correctness/translation.golden.test.ts
for (const c of loadTranslationFixtures()) {
test(`golden: ${c.name}`, () => {
const out = translateRequest(
c.sourceFormat,
c.targetFormat,
(c.input as { model?: string }).model ?? "m",
c.input,
true
);
goldenSnapshot(`translation/${c.name}`, out);
});
}
@@ -0,0 +1,78 @@
import test from "node:test";
import assert from "node:assert/strict";
import fc from "fast-check";
import { configureProperties } from "../../helpers/propertyConfig.ts";
import { translateRequest } from "../../../open-sse/translator/index.ts";
import { FORMATS } from "../../../open-sse/translator/formats.ts";
configureProperties();
// Non-empty, non-whitespace content required: translator drops messages with empty/whitespace
// content (observed real behavior — empty-content messages produce invalid Claude/Gemini requests).
const messageArb = fc.record({
role: fc.constantFrom("user", "assistant", "system"),
content: fc.string({ minLength: 1 }).filter((s) => s.trim().length > 0),
});
const bodyArb = fc.record({
model: fc.string({ minLength: 1 }),
messages: fc.array(messageArb, { minLength: 1, maxLength: 6 }),
});
test("translateRequest openai->claude never throws and keeps messages", () => {
fc.assert(
fc.property(bodyArb, (body) => {
const out = translateRequest(
FORMATS.OPENAI,
FORMATS.CLAUDE,
body.model,
body,
true
) as Record<string, unknown>;
assert.ok(out && typeof out === "object");
// Either messages array is non-empty, or content went to system field (system-only body)
const hasMessages = Array.isArray(out.messages) && (out.messages as unknown[]).length > 0;
const hasSystem = Array.isArray(out.system) && (out.system as unknown[]).length > 0;
assert.ok(hasMessages || hasSystem, "output must have messages or non-empty system");
})
);
});
test("translateRequest round-trip openai->claude->openai preserves message count for well-formed input", () => {
// Generate bodies with properly alternating user/assistant roles (no consecutive same role),
// optionally preceded by a system message. This is the well-formed case where Claude's
// merge-consecutive-same-role normalization does NOT collapse messages.
// Invariant-calibration note: arbitrary bodies with consecutive same-role messages get merged
// by the OpenAI→Claude translator (correct behavior — Claude doesn't allow consecutive same-role).
// We test only the well-formed alternating case for an exact-count invariant.
const altBodyArb = fc.record({
model: fc.string({ minLength: 1 }),
messages: fc.integer({ min: 1, max: 5 }).chain((n) => {
// Build alternating user/assistant sequence, starting with user
const msgs = Array.from({ length: n }, (_, i) => ({
role: (i % 2 === 0 ? "user" : "assistant") as "user" | "assistant",
content: "msg" + i,
}));
return fc.constant(msgs);
}),
});
fc.assert(
fc.property(altBodyArb, (body) => {
const claude = translateRequest(
FORMATS.OPENAI,
FORMATS.CLAUDE,
body.model,
body,
true
) as Record<string, unknown>;
const back = translateRequest(
FORMATS.CLAUDE,
FORMATS.OPENAI,
body.model,
claude,
true
) as Record<string, unknown>;
assert.ok(Array.isArray(back.messages));
assert.equal((back.messages as unknown[]).length, body.messages.length);
})
);
});