Several test files used assert.ok(Array.isArray(x)) or assert.ok(result) patterns that verify structure/existence without checking actual values. These pass even when the code returns wrong data. - web-diagnostics-contract: Array.isArray() checks → deepEqual([], []) for fields constructed as empty; DoctorFixResult uses deepEqual(["fix1"]) instead of Array.isArray + length; InstanceType<typeof GSDWorkspaceStore> for type assertions from dynamic import - skill-lifecycle: computeStaleAvoidList → deepEqual(result, []) since nonexistent path must return empty - blob-store: remove redundant assert.ok(retrieved) before deepEqual - discovery-cache: assert.ok(entry) existence check → verify models[0].id Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
import { ModelDiscoveryCache } from "./discovery-cache.js";
|
|
|
|
let testDir: string;
|
|
let cachePath: string;
|
|
|
|
beforeEach(() => {
|
|
testDir = join(tmpdir(), `discovery-cache-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
mkdirSync(testDir, { recursive: true });
|
|
cachePath = join(testDir, "discovery-cache.json");
|
|
});
|
|
|
|
afterEach(() => {
|
|
try {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
} catch {
|
|
// Cleanup best-effort
|
|
}
|
|
});
|
|
|
|
// ─── basic operations ────────────────────────────────────────────────────────
|
|
|
|
describe("ModelDiscoveryCache — basic operations", () => {
|
|
it("starts with no entries", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
assert.equal(cache.get("openai"), undefined);
|
|
});
|
|
|
|
it("stores and retrieves models", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
const models = [{ id: "gpt-4o", name: "GPT-4o" }];
|
|
cache.set("openai", models);
|
|
|
|
const entry = cache.get("openai");
|
|
assert.ok(entry);
|
|
assert.deepEqual(entry.models, models);
|
|
assert.ok(entry.fetchedAt > 0);
|
|
assert.ok(entry.ttlMs > 0);
|
|
});
|
|
|
|
it("persists to disk and reloads", () => {
|
|
const cache1 = new ModelDiscoveryCache(cachePath);
|
|
cache1.set("openai", [{ id: "gpt-4o" }]);
|
|
|
|
const cache2 = new ModelDiscoveryCache(cachePath);
|
|
const entry = cache2.get("openai");
|
|
assert.ok(entry);
|
|
assert.equal(entry.models[0].id, "gpt-4o");
|
|
});
|
|
|
|
it("clear removes a specific provider", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
cache.set("openai", [{ id: "gpt-4o" }]);
|
|
cache.set("google", [{ id: "gemini-pro" }]);
|
|
|
|
cache.clear("openai");
|
|
assert.equal(cache.get("openai"), undefined);
|
|
const googleEntry = cache.get("google");
|
|
assert.ok(googleEntry);
|
|
assert.equal(googleEntry.models[0].id, "gemini-pro");
|
|
});
|
|
|
|
it("clear without provider removes all entries", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
cache.set("openai", [{ id: "gpt-4o" }]);
|
|
cache.set("google", [{ id: "gemini-pro" }]);
|
|
|
|
cache.clear();
|
|
assert.equal(cache.get("openai"), undefined);
|
|
assert.equal(cache.get("google"), undefined);
|
|
});
|
|
});
|
|
|
|
// ─── staleness ───────────────────────────────────────────────────────────────
|
|
|
|
describe("ModelDiscoveryCache — staleness", () => {
|
|
it("newly set entries are not stale", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
cache.set("openai", [{ id: "gpt-4o" }]);
|
|
assert.equal(cache.isStale("openai"), false);
|
|
});
|
|
|
|
it("missing providers are stale", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
assert.equal(cache.isStale("unknown"), true);
|
|
});
|
|
|
|
it("entries with expired TTL are stale", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
cache.set("openai", [{ id: "gpt-4o" }], 1); // 1ms TTL
|
|
|
|
// Wait for TTL to expire
|
|
const start = Date.now();
|
|
while (Date.now() - start < 5) {
|
|
// busy wait
|
|
}
|
|
|
|
assert.equal(cache.isStale("openai"), true);
|
|
});
|
|
});
|
|
|
|
// ─── getAll ──────────────────────────────────────────────────────────────────
|
|
|
|
describe("ModelDiscoveryCache — getAll", () => {
|
|
it("returns non-stale entries by default", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
cache.set("openai", [{ id: "gpt-4o" }]);
|
|
cache.set("stale", [{ id: "old" }], 1);
|
|
|
|
// Wait for stale TTL
|
|
const start = Date.now();
|
|
while (Date.now() - start < 5) {
|
|
// busy wait
|
|
}
|
|
|
|
const all = cache.getAll();
|
|
assert.ok(all.has("openai"));
|
|
assert.ok(!all.has("stale"));
|
|
});
|
|
|
|
it("returns all entries when includeStale is true", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
cache.set("openai", [{ id: "gpt-4o" }]);
|
|
cache.set("stale", [{ id: "old" }], 1);
|
|
|
|
// Wait for stale TTL
|
|
const start = Date.now();
|
|
while (Date.now() - start < 5) {
|
|
// busy wait
|
|
}
|
|
|
|
const all = cache.getAll(true);
|
|
assert.ok(all.has("openai"));
|
|
assert.ok(all.has("stale"));
|
|
});
|
|
});
|
|
|
|
// ─── edge cases ──────────────────────────────────────────────────────────────
|
|
|
|
describe("ModelDiscoveryCache — edge cases", () => {
|
|
it("handles corrupted cache file gracefully", () => {
|
|
writeFileSync(cachePath, "not valid json", "utf-8");
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
assert.equal(cache.get("openai"), undefined);
|
|
});
|
|
|
|
it("handles wrong version gracefully", () => {
|
|
writeFileSync(cachePath, JSON.stringify({ version: 99, entries: {} }), "utf-8");
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
assert.equal(cache.get("openai"), undefined);
|
|
});
|
|
|
|
it("handles missing cache file", () => {
|
|
const cache = new ModelDiscoveryCache(join(testDir, "nonexistent", "cache.json"));
|
|
assert.equal(cache.get("openai"), undefined);
|
|
});
|
|
|
|
it("overwrites existing entry for same provider", () => {
|
|
const cache = new ModelDiscoveryCache(cachePath);
|
|
cache.set("openai", [{ id: "gpt-4o" }]);
|
|
cache.set("openai", [{ id: "gpt-4o-mini" }]);
|
|
|
|
const entry = cache.get("openai");
|
|
assert.ok(entry);
|
|
assert.equal(entry.models.length, 1);
|
|
assert.equal(entry.models[0].id, "gpt-4o-mini");
|
|
});
|
|
});
|