| import { mkdtempSync, readFileSync, rmSync } from "node:fs"; |
| import os from "node:os"; |
| import path from "node:path"; |
| import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"; |
|
|
| |
| |
| const tmpRoot = mkdtempSync(path.join(os.tmpdir(), "paperclip-firm-test-")); |
| process.env.PAPERCLIP_FIRM_HOME = tmpRoot; |
| process.env.PAPERCLIP_FIRM_CLI = "paperclip-nonexistent-firm-cli-xyz"; |
|
|
| const { firmService } = await import("../services/firm.ts"); |
| const { companyContextCache, COMPANY_SCOPE_KEY } = await import("../services/company-context-cache.ts"); |
|
|
| const COMPANY = "11111111-1111-1111-1111-111111111111"; |
|
|
| describe("firmService (plain-text fallback)", () => { |
| beforeAll(async () => { |
| |
| const detection = await firmService().detectCli(true); |
| expect(detection.available).toBe(false); |
| }); |
|
|
| afterAll(() => { |
| rmSync(tmpRoot, { recursive: true, force: true }); |
| }); |
|
|
| beforeEach(() => { |
| companyContextCache.clear(); |
| }); |
|
|
| it("ensureWorkspace is idempotent and records the company name", async () => { |
| const firm = firmService(); |
| const first = await firm.ensureWorkspace(COMPANY, "Acme Inc"); |
| expect(first.companyId).toBe(COMPANY); |
| expect(first.companyName).toBe("Acme Inc"); |
| expect(await firm.isInitialized(COMPANY)).toBe(true); |
|
|
| const second = await firm.ensureWorkspace(COMPANY, "Acme Inc"); |
| expect(second.createdAt).toBe(first.createdAt); |
| }); |
|
|
| it("links projects and lists them", async () => { |
| const firm = firmService(); |
| await firm.linkProject({ companyId: COMPANY, projectId: "proj-a", name: "Website", repoUrl: "https://example.com/web.git" }); |
| await firm.linkProject({ companyId: COMPANY, projectId: "proj-b", name: "API" }); |
|
|
| const records = await firm.listProjectRecords(COMPANY); |
| expect(records.map((r) => r.name).sort()).toEqual(["API", "Website"]); |
| expect(records.find((r) => r.projectId === "proj-a")?.repoUrl).toBe("https://example.com/web.git"); |
| }); |
|
|
| it("renders a snapshot from plain-text records and warms the cache", async () => { |
| const firm = firmService(); |
| const meta = await firm.writeSnapshot(COMPANY); |
| expect(meta.cliUsed).toBe(false); |
| expect(meta.projectCount).toBe(2); |
|
|
| const contextFile = readFileSync(path.join(firm.companyFirmDir(COMPANY), "context.md"), "utf-8"); |
| expect(contextFile).toContain("Acme Inc"); |
| expect(contextFile).toContain("Website"); |
| expect(contextFile).toContain("API"); |
|
|
| |
| expect(companyContextCache.peek(COMPANY, COMPANY_SCOPE_KEY)?.content).toContain("Website"); |
| }); |
|
|
| it("getContext returns warm content and is fresh right after a snapshot", async () => { |
| const firm = firmService(); |
| await firm.writeSnapshot(COMPANY); |
|
|
| const ctx = await firm.getContext({ companyId: COMPANY }); |
| expect(ctx).toContain("Acme Inc"); |
| expect(await firm.isStale(COMPANY)).toBe(false); |
| }); |
|
|
| it("getContext hydrates from disk on a cold cache", async () => { |
| const firm = firmService(); |
| await firm.writeSnapshot(COMPANY); |
| companyContextCache.clear(); |
|
|
| const ctx = await firm.getContext({ companyId: COMPANY }); |
| expect(ctx).toContain("Acme Inc"); |
| expect(companyContextCache.peek(COMPANY, COMPANY_SCOPE_KEY)?.content).toContain("Acme Inc"); |
| }); |
|
|
| it("getContext returns null for a company with no workspace", async () => { |
| const firm = firmService(); |
| const ctx = await firm.getContext({ companyId: "no-such-company" }); |
| expect(ctx).toBeNull(); |
| }); |
|
|
| it("isStale is true when there is no snapshot, and respects maxAge", async () => { |
| const firm = firmService(); |
| const fresh = "22222222-2222-2222-2222-222222222222"; |
| expect(await firm.isStale(fresh)).toBe(true); |
|
|
| await firm.ensureWorkspace(fresh, "Beta"); |
| await firm.writeSnapshot(fresh); |
| |
| expect(await firm.isStale(fresh, 1_000, Date.now() + 10_000)).toBe(true); |
| }); |
|
|
| it("lists only initialized company workspaces", async () => { |
| const firm = firmService(); |
| await firm.ensureWorkspace(COMPANY, "Acme Inc"); |
| const ids = await firm.listInitializedCompanyIds(); |
| expect(ids).toContain(COMPANY); |
| }); |
| }); |
|
|