Files
wehub-resource-sync b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:23:53 +08:00

225 lines
5.7 KiB
TypeScript

import type { CollectionCommentSettings } from "emdash";
import { describe, it, expect } from "vitest";
import type { Category } from "../src/categories.js";
import { computeDecision } from "../src/decision.js";
import type { GuardResult } from "../src/guard.js";
const defaultCategories: Category[] = [
{ id: "S1", name: "Violence", description: "Violence", action: "block", builtin: true },
{ id: "S2", name: "Fraud", description: "Fraud", action: "hold", builtin: true },
{ id: "S6", name: "Advice", description: "Advice", action: "ignore", builtin: true },
];
const defaultCollectionSettings: CollectionCommentSettings = {
commentsEnabled: true,
commentsModeration: "all",
commentsClosedAfterDays: 90,
commentsAutoApproveUsers: true,
};
const defaultSettings = { autoApproveClean: true };
describe("computeDecision", () => {
it("auto-approves authenticated CMS users", () => {
const result = computeDecision(
undefined,
undefined,
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
true,
);
expect(result.status).toBe("approved");
expect(result.reason).toContain("CMS user");
});
it("blocks when AI detects a 'block' category", () => {
const guard: GuardResult = { safe: false, categories: ["S1"] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
false,
);
expect(result.status).toBe("spam");
expect(result.reason).toContain("S1");
});
it("holds when AI detects a 'hold' category", () => {
const guard: GuardResult = { safe: false, categories: ["S2"] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
false,
);
expect(result.status).toBe("pending");
expect(result.reason).toContain("S2");
});
it("ignores categories with action 'ignore'", () => {
const guard: GuardResult = { safe: false, categories: ["S6"] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
false,
);
// Should not block or hold — falls through to autoApproveClean
expect(result.status).toBe("approved");
});
it("block takes precedence over hold when both flagged", () => {
const guard: GuardResult = { safe: false, categories: ["S1", "S2"] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
false,
);
expect(result.status).toBe("spam");
});
it("holds on AI error (fail-safe)", () => {
const result = computeDecision(
undefined,
"AI service unavailable",
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
false,
);
expect(result.status).toBe("pending");
expect(result.reason).toContain("AI error");
});
it("approves clean comments when autoApproveClean is true", () => {
const guard: GuardResult = { safe: true, categories: [] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
{ autoApproveClean: true },
defaultCollectionSettings,
0,
false,
);
expect(result.status).toBe("approved");
expect(result.reason).toContain("clean");
});
it("falls back to collection settings when autoApproveClean is false", () => {
const guard: GuardResult = { safe: true, categories: [] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
{ autoApproveClean: false },
{ ...defaultCollectionSettings, commentsModeration: "all" },
0,
false,
);
expect(result.status).toBe("pending");
});
it("respects collection moderation 'none' as fallback", () => {
const guard: GuardResult = { safe: true, categories: [] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
{ autoApproveClean: false },
{ ...defaultCollectionSettings, commentsModeration: "none" },
0,
false,
);
expect(result.status).toBe("approved");
});
it("respects 'first_time' moderation with returning commenter", () => {
const guard: GuardResult = { safe: true, categories: [] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
{ autoApproveClean: false },
{ ...defaultCollectionSettings, commentsModeration: "first_time" },
3,
false,
);
expect(result.status).toBe("approved");
});
it("holds first-time commenters under 'first_time' moderation", () => {
const guard: GuardResult = { safe: true, categories: [] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
{ autoApproveClean: false },
{ ...defaultCollectionSettings, commentsModeration: "first_time" },
0,
false,
);
expect(result.status).toBe("pending");
});
it("holds when AI returns unknown category ID (fail-safe)", () => {
const guard: GuardResult = { safe: false, categories: ["S99"] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
false,
);
expect(result.status).toBe("pending");
expect(result.reason).toContain("S99");
});
it("holds when AI returns mix of unknown and ignore categories", () => {
const guard: GuardResult = { safe: false, categories: ["S6", "S99"] };
const result = computeDecision(
guard,
undefined,
defaultCategories,
defaultSettings,
defaultCollectionSettings,
0,
false,
);
expect(result.status).toBe("pending");
expect(result.reason).toContain("S99");
});
it("handles missing guard (no AI)", () => {
const result = computeDecision(
undefined,
undefined,
defaultCategories,
{ autoApproveClean: false },
{ ...defaultCollectionSettings, commentsModeration: "none" },
0,
false,
);
expect(result.status).toBe("approved");
});
});