mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-29 21:07:40 +01:00
Slack catalog discovery now checks live task, user, endpoint and credentials without calculating an unused compatibility revision. Full authority callers retain the unchanged revision and search-token path. Validation: 44 affected cases, full repository typecheck/build, current-head full CI/E2E and security checks, Greptile Base 5/5 with no unresolved findings. Identical production catalog output with revision statements reduced from two to zero in real-PostgreSQL comparisons. Hosted latency acceptance remains pending. Co-Authored-By: Paperclip <noreply@paperclip.ing>
69 lines
3.0 KiB
TypeScript
69 lines
3.0 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import type { Db } from "@paperclipai/db";
|
|
import {
|
|
registerSlackTaskAuthority,
|
|
resolveSlackTaskAuthorization,
|
|
resolveSlackTaskAuthority,
|
|
type SlackTaskAuthority,
|
|
type SlackTaskBinding,
|
|
} from "./slack-authority.js";
|
|
|
|
const binding: SlackTaskBinding = {
|
|
companyId: "company",
|
|
agentId: "agent",
|
|
issueId: "issue",
|
|
runId: "run",
|
|
};
|
|
|
|
describe("Slack authority registration", () => {
|
|
it("requires a live registration for authorization and revision resolution", async () => {
|
|
const db = {} as Db;
|
|
await expect(resolveSlackTaskAuthorization(db, binding)).rejects.toMatchObject({ status: 403 });
|
|
await expect(resolveSlackTaskAuthority(db, binding)).rejects.toMatchObject({ status: 403 });
|
|
});
|
|
|
|
it("retains existing resolver compatibility without caching its result", async () => {
|
|
const db = {} as Db;
|
|
const first = { revision: "first" } as SlackTaskAuthority;
|
|
const second = { revision: "second" } as SlackTaskAuthority;
|
|
const resolve = vi.fn().mockResolvedValueOnce(first).mockResolvedValueOnce(second).mockResolvedValueOnce(second);
|
|
const unregister = registerSlackTaskAuthority(db, resolve);
|
|
try {
|
|
expect(await resolveSlackTaskAuthorization(db, binding)).toBe(first);
|
|
expect(await resolveSlackTaskAuthorization(db, binding)).toBe(second);
|
|
expect(await resolveSlackTaskAuthority(db, binding)).toBe(second);
|
|
expect(resolve.mock.calls).toEqual([[binding], [binding], [binding]]);
|
|
expect(resolve.mock.contexts).toEqual([undefined, undefined, undefined]);
|
|
} finally { unregister(); }
|
|
});
|
|
|
|
it("uses live authorization separately from the revision resolver", async () => {
|
|
const db = {} as Db;
|
|
const full = { revision: "current" } as SlackTaskAuthority;
|
|
const resolve = vi.fn(async () => full);
|
|
const authorize = vi.fn(async () => full);
|
|
const unregister = registerSlackTaskAuthority(db, resolve, authorize);
|
|
try {
|
|
expect(await resolveSlackTaskAuthorization(db, binding)).toBe(full);
|
|
expect(authorize).toHaveBeenCalledWith(binding);
|
|
expect(resolve).not.toHaveBeenCalled();
|
|
expect(await resolveSlackTaskAuthority(db, binding)).toBe(full);
|
|
expect(resolve).toHaveBeenCalledWith(binding);
|
|
} finally { unregister(); }
|
|
});
|
|
|
|
it("does not unregister a replacement service's authorization", async () => {
|
|
const db = {} as Db;
|
|
const old = { revision: "old" } as SlackTaskAuthority;
|
|
const current = { revision: "current" } as SlackTaskAuthority;
|
|
const unregisterOld = registerSlackTaskAuthority(db, async () => old, async () => old);
|
|
const unregisterCurrent = registerSlackTaskAuthority(db, async () => current, async () => current);
|
|
try {
|
|
unregisterOld();
|
|
expect(await resolveSlackTaskAuthorization(db, binding)).toBe(current);
|
|
expect(await resolveSlackTaskAuthority(db, binding)).toBe(current);
|
|
} finally { unregisterCurrent(); }
|
|
await expect(resolveSlackTaskAuthorization(db, binding)).rejects.toMatchObject({ status: 403 });
|
|
});
|
|
});
|