Files
community-rule/tests/unit/methodRecommendations.test.ts
T
adilalloandCursor bcbc6f30d9 Import the remade decks on stable ids and recommend one card per job, so a saved draft still loads after a chip is renamed.
The real-group profiles stay as a test, so a change that moves a pick fails.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-04 14:22:54 -07:00

146 lines
3.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const findManyMock = vi.fn();
vi.mock("../../lib/server/env", () => ({
isDatabaseConfigured: () => true,
}));
vi.mock("../../lib/server/db", () => ({
prisma: {
methodFacet: {
findMany: (...args: unknown[]) => findManyMock(...args),
},
},
}));
import {
listMethodRecommendations,
scoreTemplatesByFacets,
} from "../../lib/server/methodRecommendations";
beforeEach(() => {
findManyMock.mockReset();
});
describe("listMethodRecommendations", () => {
it("returns empty rankings when no facets are requested", async () => {
const result = await listMethodRecommendations({
section: "communication",
facets: {},
});
expect(result).toEqual({ rankedSlugs: [], matchesBySlug: {} });
expect(findManyMock).not.toHaveBeenCalled();
});
it("ranks from the job rule and scores maturity once", async () => {
const once = await listMethodRecommendations({
section: "decisionApproaches",
facets: {
size: ["thirteenToOneHundred"],
orgType: ["workersCoop"],
maturity: ["established"],
},
});
const several = await listMethodRecommendations({
section: "decisionApproaches",
facets: {
size: ["thirteenToOneHundred"],
orgType: ["workersCoop"],
maturity: ["established", "earlyStage", "enterprise"],
},
});
expect(once?.rankedSlugs).toEqual(several?.rankedSlugs);
expect(once?.rankedSlugs[0]).toBeTruthy();
expect(findManyMock).not.toHaveBeenCalled();
});
it("uses only the main kind for eligibility", async () => {
const result = await listMethodRecommendations({
section: "decisionApproaches",
facets: {
size: ["thirteenToOneHundred"],
orgType: ["workersCoop", "forProfit"],
scale: ["local"],
maturity: ["established"],
},
});
expect(result?.rankedSlugs).toContain("consent-sociocracy");
expect(findManyMock).not.toHaveBeenCalled();
});
});
describe("scoreTemplatesByFacets (CR-88 §9.1)", () => {
it("aggregates per-method matches per template", async () => {
findManyMock.mockResolvedValueOnce([
{
section: "communication",
slug: "loomio",
group: "size",
value: "twoToFive",
},
{
section: "decisionApproaches",
slug: "consensus-decision-making",
group: "orgType",
value: "workersCoop",
},
]);
const result = await scoreTemplatesByFacets({
facets: {
size: ["twoToFive"],
orgType: ["workersCoop"],
},
templateMethods: [
{
templateSlug: "consensus",
methods: [
{ section: "communication", slug: "loomio" },
{
section: "decisionApproaches",
slug: "consensus-decision-making",
},
],
},
{
templateSlug: "monarch",
methods: [
{
section: "decisionApproaches",
slug: "benevolent-dictator",
},
],
},
],
});
expect(result).toEqual([
{
templateSlug: "consensus",
score: 2,
matchedFacets: [
"communication:loomio:size:twoToFive",
"decisionApproaches:consensus-decision-making:orgType:workersCoop",
],
},
{ templateSlug: "monarch", score: 0, matchedFacets: [] },
]);
});
it("returns 0-score entries for every template when facets are empty", async () => {
const result = await scoreTemplatesByFacets({
facets: {},
templateMethods: [
{ templateSlug: "consensus", methods: [] },
{ templateSlug: "monarch", methods: [] },
],
});
expect(result).toEqual([
{ templateSlug: "consensus", score: 0, matchedFacets: [] },
{ templateSlug: "monarch", score: 0, matchedFacets: [] },
]);
expect(findManyMock).not.toHaveBeenCalled();
});
});