Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions convex/skillCards.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,61 @@ describe("skillCards attach", () => {
process.env.SECURITY_SCAN_WORKER_TOKEN = previousToken;
});

it("deletes the stored card blob when attach fails", async () => {
const previousToken = process.env.SECURITY_SCAN_WORKER_TOKEN;
process.env.SECURITY_SCAN_WORKER_TOKEN = "test-worker-token";
const store = vi.fn(async () => "_storage:new-card");
const deleteStorage = vi.fn(async () => undefined);
const runMutation = vi.fn(async () => {
throw new Error("Lease mismatch");
});

await expect(
completeHandler(
{
storage: { store, delete: deleteStorage },
runMutation,
},
{
token: "test-worker-token",
jobId: "skillCardGenerationJobs:1",
leaseToken: "stale-lease",
markdown: "# Card\n",
},
),
).rejects.toThrow(/Lease mismatch/);

expect(store).toHaveBeenCalledOnce();
expect(deleteStorage).toHaveBeenCalledWith("_storage:new-card");
process.env.SECURITY_SCAN_WORKER_TOKEN = previousToken;
});

it("keeps the stored card blob when attach succeeds", async () => {
const previousToken = process.env.SECURITY_SCAN_WORKER_TOKEN;
process.env.SECURITY_SCAN_WORKER_TOKEN = "test-worker-token";
const store = vi.fn(async () => "_storage:new-card");
const deleteStorage = vi.fn(async () => undefined);
const runMutation = vi.fn(async () => ({ ok: true, bundleFingerprint: "fp" }));

await expect(
completeHandler(
{
storage: { store, delete: deleteStorage },
runMutation,
},
{
token: "test-worker-token",
jobId: "skillCardGenerationJobs:1",
leaseToken: "lease",
markdown: "# Card\n",
},
),
).resolves.toEqual({ ok: true, bundleFingerprint: "fp" });

expect(deleteStorage).not.toHaveBeenCalled();
process.env.SECURITY_SCAN_WORKER_TOKEN = previousToken;
});

it("replaces skill-card.md, preserves source and prior bundle fingerprints, and inserts current bundle fingerprint", async () => {
const version = makeSettledVersion({
files: [
Expand Down
29 changes: 17 additions & 12 deletions convex/skillCards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,18 +529,23 @@ export const completeSkillCardJob = action({
const storageId = await ctx.storage.store(
new Blob([args.markdown], { type: "text/markdown; charset=utf-8" }),
);
return await runMutationRef(ctx, internalRefs.skillCards.attachCardAndSucceedJobInternal, {
jobId: args.jobId,
leaseToken: args.leaseToken,
runId: args.runId,
cardFile: {
path: SKILL_CARD_FILE_PATH,
size: encoded.byteLength,
storageId,
sha256,
contentType: "text/markdown; charset=utf-8",
},
});
try {
return await runMutationRef(ctx, internalRefs.skillCards.attachCardAndSucceedJobInternal, {
jobId: args.jobId,
leaseToken: args.leaseToken,
runId: args.runId,
cardFile: {
path: SKILL_CARD_FILE_PATH,
size: encoded.byteLength,
storageId,
sha256,
contentType: "text/markdown; charset=utf-8",
},
});
} catch (error) {
await ctx.storage.delete(storageId).catch(() => undefined);
throw error;
}
},
});

Expand Down
Loading