The real-group profiles stay as a test, so a change that moves a pick fails. Co-authored-by: Cursor <cursoragent@cursor.com>
146 lines
3.9 KiB
TypeScript
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();
|
|
});
|
|
});
|