Files
clawhub/convex/lib/skillSearchDigest.test.ts
T
Patrick Erichsen 0f84533e9c feat: add permanent skills.sh mirror storage (#3227)
* feat: add staged skills.sh mirror storage

* ci: allow guarded CLAW-563 Test deploy

* ci: expose guarded Test deploy diagnostics

* ci: defer branch guard to deploy step

* ci: deploy CLAW-563 PR head to Test

* ci: admit CLAW-563 PR Test job

* fix: make mirror source recovery durable

* ci: trigger labeled mirror load

* feat: activate mirror search queries

* fix: tighten mirror source typing

* fix: bypass protected Test mirror proof

* feat: attribute skill metrics by source

* feat: present stars as bookmarks

* style: format mirror proof changes

* fix: bypass protected mirror readback

* fix: resume mirror past missing scanner pages

* fix: fetch skills.sh mirror audits from api

* fix: validate structural skills.sh identities

* fix: resolve ambiguous skills.sh mirror identities

* feat: stabilize skills.sh mirror ingestion

* fix: account mirror identity conflicts in proof

* fix: quarantine invalid skills.sh detail ids

* fix: resume skills.sh mirror proof

* fix: preserve skills.sh mirror provenance

* fix: recover exact skills.sh mirror runs

* fix: recover stale skills.sh mirror runs

* fix: normalize skills.sh mirror topic facets

* feat: prove complete skills.sh leaderboard mirror

* fix: canonicalize skills.sh source page hashes

* test: enable skills.sh rollout in mirror tests

* ci: skip unrelated Test deploy pull requests

* fix: preserve Vercel preview marker in Test deploy

* fix: tighten Test deploy and metric reconciliation

* fix: bound mirror detail proof pages

* fix: delegate controlled mirror rate limits

* fix: preserve mirror reconciliation progress

* fix: release mirror retry responses

* fix: preserve stale mirror replay state

* fix: authenticate mirror source starts

* fix: delegate mirror identity rate limits

* ci: trigger mirror proof when labeled

* ci: couple mirror deploy and proof opt-in

* fix: admit permanent Vercel Test runtime

* fix: pass Test target to Vercel runtime

* test: align bookmark sync browser labels

* fix: preserve skills.sh source accounting

* fix: preflight active mirror runs

* fix: bind mirror snapshot accounting

* fix: reject truncated replay hashes

* fix: preserve live mirror overlay metadata
2026-07-24 14:32:00 -05:00

500 lines
16 KiB
TypeScript

/* @vitest-environment node */
import { describe, expect, it, vi } from "vitest";
import { toPublicSkill } from "./public";
import { computeRecommendationScore, RECOMMENDATION_SCORE_VERSION } from "./recommendationScore";
import {
digestToHydratableSkill,
extractDigestFields,
extractValidatedDigestFields,
digestToOwnerInfo,
upsertSkillSearchDigest,
} from "./skillSearchDigest";
function makeSkillDoc(overrides: Record<string, unknown> = {}) {
return {
_id: "skills:abc" as never,
_creationTime: 1000,
slug: "test-skill",
displayName: "Test Skill",
summary: "A test skill summary",
resourceId: "res123",
ownerUserId: "users:owner" as never,
canonicalSkillId: undefined,
forkOf: undefined,
latestVersionId: "skillVersions:v1" as never,
latestVersionSummary: {
version: "1.0.0",
createdAt: 1000,
changelog: "Initial release",
},
tags: {} as Record<string, never>,
categories: ["operations"],
topics: ["supply-chain", "vetting"],
softDeletedAt: undefined,
badges: undefined,
moderationStatus: "active" as const,
moderationNotes: undefined,
moderationReason: undefined,
moderationVerdict: undefined,
moderationReasonCodes: undefined,
moderationEvidence: undefined,
moderationSummary: undefined,
moderationEngineVersion: undefined,
moderationEvaluatedAt: undefined,
moderationSourceVersionId: undefined,
quality: undefined,
isSuspicious: false,
moderationFlags: ["flagged.test"],
lastReviewedAt: undefined,
scanLastCheckedAt: undefined,
scanCheckCount: undefined,
hiddenAt: undefined,
hiddenBy: undefined,
reportCount: 0,
lastReportedAt: undefined,
batch: undefined,
statsDownloads: 42,
statsStars: 5,
statsInstallsCurrent: 10,
statsInstallsAllTime: 100,
statsSkillsShInstalls: 8,
statsGithubStars: 250,
stats: {
downloads: 42,
installsCurrent: 10,
installsAllTime: 100,
stars: 5,
versions: 3,
comments: 1,
},
createdAt: 1000,
updatedAt: 2000,
...overrides,
};
}
describe("extractDigestFields", () => {
it("extracts the correct subset of fields", () => {
const skill = makeSkillDoc();
const digest = extractDigestFields(skill as never);
expect(digest.skillId).toBe("skills:abc");
expect(digest.slug).toBe("test-skill");
expect(digest.displayName).toBe("Test Skill");
expect(digest.summary).toBe("A test skill summary");
expect(digest.ownerUserId).toBe("users:owner");
expect(digest.categories).toEqual(["operations"]);
expect(digest.topics).toEqual(["supply-chain", "vetting"]);
expect(digest.statsDownloads).toBe(42);
expect(digest.statsStars).toBe(5);
expect(digest.statsInstallsCurrent).toBe(10);
expect(digest.statsInstallsAllTime).toBe(100);
expect(digest.statsSkillsShInstalls).toBe(8);
expect(digest.statsGithubStars).toBe(250);
expect(digest.recommendedScore).toBe(
computeRecommendationScore({ downloads: 42, installs: 100, stars: 5 }),
);
expect(digest.recommendedScoreVersion).toBe(RECOMMENDATION_SCORE_VERSION);
expect(digest.stats).toEqual({
downloads: 42,
installsCurrent: 10,
installsAllTime: 100,
stars: 5,
versions: 3,
comments: 1,
});
expect(digest.moderationFlags).toEqual(["flagged.test"]);
expect(digest.moderationVerdict).toBeUndefined();
expect(digest.isSuspicious).toBe(false);
expect(digest.createdAt).toBe(1000);
expect(digest.updatedAt).toBe(2000);
});
it("fills digest rank stats from legacy nested stats", () => {
const skill = makeSkillDoc({
statsDownloads: undefined,
statsStars: undefined,
statsInstallsCurrent: undefined,
statsInstallsAllTime: undefined,
stats: {
downloads: 42,
installsCurrent: 10,
installsAllTime: 100,
stars: 5,
versions: 3,
comments: 1,
},
});
const digest = extractDigestFields(skill as never);
expect(digest.statsDownloads).toBe(42);
expect(digest.statsStars).toBe(5);
expect(digest.statsInstallsCurrent).toBe(10);
expect(digest.statsInstallsAllTime).toBe(100);
expect(digest.recommendedScore).toBe(
computeRecommendationScore({ downloads: 42, installs: 100, stars: 5 }),
);
expect(digest.recommendedScoreVersion).toBe(RECOMMENDATION_SCORE_VERSION);
});
it("projects current inferred catalog metadata when author metadata is omitted", () => {
const digest = extractDigestFields(
makeSkillDoc({
categories: undefined,
topics: undefined,
inferredCategories: ["development"],
inferredTopics: ["TypeScript", "Code Review"],
inferredFromVersionId: "skillVersions:v1",
}) as never,
);
expect(digest.categories).toEqual(["development"]);
expect(digest.topics).toEqual(["TypeScript", "Code Review"]);
});
it("does not project stale inferred catalog metadata", () => {
const digest = extractDigestFields(
makeSkillDoc({
latestVersionId: "skillVersions:v2",
categories: undefined,
topics: undefined,
inferredCategories: ["development"],
inferredTopics: ["TypeScript"],
inferredFromVersionId: "skillVersions:v1",
}) as never,
);
expect(digest.categories).toEqual(["other"]);
expect(digest.topics).toEqual([]);
});
it("omits large fields not needed for search", () => {
const skill = makeSkillDoc({
moderationEvidence: [
{ code: "test", severity: "info", file: "a.ts", line: 1, message: "m", evidence: "e" },
],
quality: {
score: 80,
decision: "pass",
trustTier: "medium",
similarRecentCount: 0,
reason: "ok",
signals: {},
evaluatedAt: 1000,
},
latestVersionSummary: { version: "1.0.0", createdAt: 1000, changelog: "big text" },
moderationNotes: "some notes",
moderationSummary: "summary text",
});
const digest = extractDigestFields(skill as never);
expect(digest).not.toHaveProperty("moderationEvidence");
expect(digest).not.toHaveProperty("quality");
expect(digest).toHaveProperty("latestVersionSummary");
expect(digest).not.toHaveProperty("moderationNotes");
expect(digest).not.toHaveProperty("moderationSummary");
expect(digest).not.toHaveProperty("resourceId");
});
it("extractDigestFields does not include owner profile fields", () => {
const skill = makeSkillDoc();
const digest = extractDigestFields(skill as never);
expect(digest).not.toHaveProperty("ownerHandle");
expect(digest).not.toHaveProperty("ownerName");
expect(digest).not.toHaveProperty("ownerDisplayName");
expect(digest).not.toHaveProperty("ownerImage");
});
it("produces a digest that works with toPublicSkill when shaped as Doc<skills>", () => {
const skill = makeSkillDoc();
const digest = extractDigestFields(skill as never);
// Simulate what hydrateResults does: spread digest with _id and _creationTime
const fakeDoc = { ...digest, _id: digest.skillId, _creationTime: digest.createdAt };
// toPublicSkill expects specific fields — verify the shape matches
expect(fakeDoc._id).toBe("skills:abc");
expect(fakeDoc._creationTime).toBe(1000);
expect(fakeDoc.slug).toBe("test-skill");
expect(fakeDoc.displayName).toBe("Test Skill");
expect(fakeDoc.ownerUserId).toBe("users:owner");
expect(fakeDoc.tags).toEqual({});
expect(fakeDoc.stats).toBeDefined();
});
it("preserves suspicious state through digest hydration", () => {
const digest = extractDigestFields(makeSkillDoc({ isSuspicious: true }) as never);
const hydratable = digestToHydratableSkill(digest as never);
expect(hydratable.isSuspicious).toBe(true);
});
it("preserves catalog metadata through public hydration", () => {
const digest = extractDigestFields(makeSkillDoc() as never);
const publicSkill = toPublicSkill(digestToHydratableSkill(digest as never));
expect(publicSkill?.categories).toEqual(["operations"]);
expect(publicSkill?.topics).toEqual(["supply-chain", "vetting"]);
});
it("preserves malicious moderation verdicts through digest hydration", () => {
const digest = extractDigestFields(makeSkillDoc({ moderationVerdict: "malicious" }) as never);
const hydratable = digestToHydratableSkill(digest as never);
expect(hydratable.moderationVerdict).toBe("malicious");
expect(toPublicSkill(hydratable)).toBeNull();
});
});
describe("extractValidatedDigestFields", () => {
it("records latest-version ownership when the version belongs to the skill", async () => {
const digest = await extractValidatedDigestFields(
{
db: {
get: async () => ({
_id: "skillVersions:v1",
skillId: "skills:abc",
softDeletedAt: undefined,
}),
},
} as never,
makeSkillDoc() as never,
);
expect(digest.latestVersionId).toBe("skillVersions:v1");
expect(digest.latestVersionSkillId).toBe("skills:abc");
expect(digest.latestVersionSummary).toMatchObject({ version: "1.0.0" });
expect(digest.publicVersion).toEqual({
status: "available",
versionId: "skillVersions:v1",
});
});
it("clears stale latest-version metadata when the version belongs to another skill", async () => {
const digest = await extractValidatedDigestFields(
{
db: {
get: async () => ({ skillId: "skills:other", softDeletedAt: undefined }),
},
} as never,
makeSkillDoc() as never,
);
expect(digest.latestVersionId).toBeUndefined();
expect(digest.latestVersionSkillId).toBeUndefined();
expect(digest.latestVersionSummary).toBeUndefined();
expect(digest.publicVersion).toEqual({ status: "unavailable" });
});
});
describe("upsertSkillSearchDigest", () => {
it("syncs one indexed topic row per valid stored topic", async () => {
const insert = vi.fn(async (table: string) => `${table}:inserted`);
const fields = extractDigestFields(
makeSkillDoc({ topics: ["supply-chain", "Official", "vetting"] }) as never,
);
const query = vi.fn((_table: string) => ({
withIndex: vi.fn(() => ({
unique: vi.fn(async () => null),
collect: vi.fn(async () => []),
})),
}));
await upsertSkillSearchDigest({ db: { query, insert } } as never, fields);
expect(insert).toHaveBeenCalledWith("skillSearchDigest", fields);
expect(insert).toHaveBeenCalledWith(
"skillTopicSearchDigest",
expect.objectContaining({
skillId: "skills:abc",
topic: "supply-chain",
normalizedDisplayName: "test skill",
recommendedScore: expect.any(Number),
}),
);
expect(insert).toHaveBeenCalledWith(
"skillTopicSearchDigest",
expect.objectContaining({ skillId: "skills:abc", topic: "vetting" }),
);
});
it("removes indexed topic rows when topics are cleared", async () => {
const fields = extractDigestFields(makeSkillDoc({ topics: undefined }) as never);
const deleteRow = vi.fn(async () => {});
const query = vi.fn((table: string) => ({
withIndex: vi.fn(() => ({
unique: vi.fn(async () =>
table === "skillSearchDigest"
? {
...fields,
_id: "skillSearchDigest:abc",
_creationTime: 1,
topics: ["supply-chain"],
}
: null,
),
collect: vi.fn(async () =>
table === "skillTopicSearchDigest"
? [
{
_id: "skillTopicSearchDigest:supply-chain",
skillId: fields.skillId,
topic: "supply-chain",
},
]
: [],
),
})),
}));
await upsertSkillSearchDigest(
{
db: {
query,
insert: vi.fn(),
patch: vi.fn(),
delete: deleteRow,
},
} as never,
fields,
);
expect(deleteRow).toHaveBeenCalledWith("skillTopicSearchDigest:supply-chain");
});
it("syncs a compact curated projection for official or highlighted skills", async () => {
const fields = extractDigestFields(
makeSkillDoc({ badges: { official: { byUserId: "users:admin", at: 3 } } }) as never,
);
const insert = vi.fn(async (table: string) => `${table}:inserted`);
const query = vi.fn(() => ({
withIndex: vi.fn(() => ({
unique: vi.fn(async () => null),
collect: vi.fn(async () => []),
})),
}));
await upsertSkillSearchDigest({ db: { query, insert } } as never, fields);
expect(insert).toHaveBeenCalledWith(
"curatedSkillSearchDigest",
expect.objectContaining({
skillId: "skills:abc",
categories: ["operations"],
topics: ["supply-chain", "vetting"],
statsDownloads: 42,
}),
);
});
it("removes the curated projection when the last curated badge is cleared", async () => {
const fields = extractDigestFields(makeSkillDoc({ badges: {} }) as never);
const deleteRow = vi.fn(async () => {});
const query = vi.fn((table: string) => ({
withIndex: vi.fn(() => ({
unique: vi.fn(async () => {
if (table === "skillSearchDigest") {
return {
...fields,
badges: { official: { byUserId: "users:admin", at: 3 } },
_id: "skillSearchDigest:abc",
};
}
if (table === "curatedSkillSearchDigest") {
return { skillId: fields.skillId, _id: "curatedSkillSearchDigest:abc" };
}
return null;
}),
collect: vi.fn(async () => []),
})),
}));
await upsertSkillSearchDigest(
{
db: {
query,
insert: vi.fn(),
patch: vi.fn(),
delete: deleteRow,
},
} as never,
fields,
);
expect(deleteRow).toHaveBeenCalledWith("curatedSkillSearchDigest:abc");
});
});
describe("digestToOwnerInfo", () => {
it("returns owner info when ownerHandle is present", () => {
const digest = {
ownerUserId: "users:owner" as never,
ownerHandle: "jdoe",
ownerName: "John",
ownerDisplayName: "John Doe",
ownerImage: "https://example.com/avatar.png",
};
const result = digestToOwnerInfo(digest);
expect(result).not.toBeNull();
expect(result!.ownerHandle).toBe("jdoe");
expect(result!.owner).toEqual({
_id: "publishers:missing",
_creationTime: 0,
kind: "user",
handle: "jdoe",
displayName: "John Doe",
image: "https://example.com/avatar.png",
bio: undefined,
linkedUserId: "users:owner",
});
});
it("returns null when ownerHandle is undefined (pre-backfill)", () => {
const digest = {
ownerUserId: "users:owner" as never,
ownerHandle: undefined,
ownerName: undefined,
ownerDisplayName: undefined,
ownerImage: undefined,
};
expect(digestToOwnerInfo(digest)).toBeNull();
});
it("uses userId as fallback handle when ownerHandle is empty string", () => {
const digest = {
ownerUserId: "users:owner" as never,
ownerHandle: "",
ownerName: "No Handle User",
ownerDisplayName: "No Handle",
ownerImage: "https://example.com/avatar.png",
};
const result = digestToOwnerInfo(digest);
expect(result).not.toBeNull();
expect(result!.ownerHandle).toBe("users:owner");
expect(result!.owner).toEqual({
_id: "publishers:missing",
_creationTime: 0,
kind: "user",
handle: "users:owner",
displayName: "No Handle",
image: "https://example.com/avatar.png",
bio: undefined,
linkedUserId: "users:owner",
});
});
it("returns null owner for deactivated user (empty handle, no profile data)", () => {
const digest = {
ownerUserId: "users:deactivated" as never,
ownerHandle: "",
ownerName: undefined,
ownerDisplayName: undefined,
ownerImage: undefined,
};
const result = digestToOwnerInfo(digest);
expect(result).not.toBeNull();
expect(result!.ownerHandle).toBe("users:deactivated");
expect(result!.owner).toBeNull();
});
});