Test file
Public evidence redaction
The saved provider reads on the home page: allowlisted fields and routes only, one workspace, an invoice shown only once it was read back, a cache and a rate limit.
tests/showcase.test.ts · 513 lines
import { describe, it, expect, vi } from "vitest";
import Fastify from "fastify";
import { PGlite } from "@electric-sql/pglite";
import { createRequire } from "node:module";
import { randomUUID } from "node:crypto";
import { createShowcase } from "../packages/db/src/showcase";
import {
observeReads,
observeGrant,
} from "../packages/db/src/showcase-observer";
import {
captureRead,
redactResponse,
safeEvidence,
safeScopes,
} from "../packages/db/src/showcase-model";
import { registerShowcase } from "../apps/api/src/showcase";
import type { Store } from "../packages/db/src/index";
type Database = Parameters<typeof createShowcase>[0];
// drizzle-orm is a dependency of packages/db, not of the tests.
const { drizzle } = createRequire(
new URL("../packages/db/package.json", import.meta.url),
)("drizzle-orm/pglite") as { drizzle: (client: PGlite) => Database };
const providers = [
"shopify",
"square",
"xero",
"quickbooks",
"stripe",
] as const;
describe("what a saved read may keep", () => {
const privateFields = {
access_token: "SECRET",
refresh_token: "REFRESH",
email: "person@example.test",
address: "PRIVATE_ADDRESS",
id: "PRIVATE_ID",
unknown: { token: "SECRET" },
};
const privateValues = Object.values(privateFields).filter(
(value) => typeof value === "string",
);
// Each provider's real response shape, with private fields at every level.
const samples = {
shopify: {
...privateFields,
data: {
shop: {
...privateFields,
currencyCode: "GBP",
plan: { partnerDevelopment: true, ...privateFields },
},
},
},
square: {
merchant: {
...privateFields,
currency: "USD",
country: "US",
status: "ACTIVE",
},
},
xero: {
Organisations: [
{
...privateFields,
IsDemoCompany: true,
CountryCode: "US",
BaseCurrency: "USD",
},
],
},
quickbooks: {
Invoice: {
...privateFields,
TotalAmt: 24,
Balance: 24,
CurrencyRef: { value: "GBP", ...privateFields },
TxnTaxDetail: { TotalTax: 4, ...privateFields },
},
},
stripe: {
...privateFields,
object: "list",
livemode: false,
data: [
{
...privateFields,
object: "payment_intent",
amount: 2400,
currency: "gbp",
status: "succeeded",
livemode: false,
},
],
},
} as const;
it.each(providers)(
"%s keeps only allowlisted fields, routes and scopes",
(provider) => {
const kept = JSON.stringify(redactResponse(provider, samples[provider]));
for (const value of privateValues) expect(kept).not.toContain(value);
expect(kept).not.toMatch(
/access_token|refresh_token|email|address|unknown|PRIVATE_ID/,
);
// A path that isn't on the provider's list is refused whole.
const unlisted = {
request: { method: "GET", path: "/PRIVATE_ID?token=SECRET" },
status: 200,
response: samples[provider],
};
expect(safeEvidence(provider, unlisted)).toBeNull();
const scopes = ["SECRET", "person@example.test", "write_everything"];
expect(safeScopes(provider, scopes)).toEqual([]);
},
);
it("captures no token exchange, no write and no route off the list", () => {
const capture = (
provider: "quickbooks" | "square",
url: string,
method: string,
body: unknown,
) =>
captureRead(
provider,
new URL(url),
method,
body,
new Headers(),
new Date(),
);
expect(
capture(
"quickbooks",
"https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
"POST",
{ access_token: "SECRET" },
),
).toBeNull();
expect(
capture(
"quickbooks",
"https://sandbox-quickbooks.api.intuit.com/v3/company/123/invoice",
"POST",
samples.quickbooks,
),
).toBeNull();
expect(
capture(
"square",
"https://connect.squareup.com/v2/merchants/me",
"GET",
samples.square,
),
).toBeNull();
});
it("counts every record but keeps three, with numeric headroom and no query string", () => {
const contacts = Array.from({ length: 7 }, () => ({
ContactStatus: "ACTIVE",
EmailAddress: "PRIVATE_EMAIL",
ContactID: "PRIVATE_ID",
}));
const result = captureRead(
"xero",
new URL(
"https://api.xero.com/api.xro/2.0/Contacts?page=1&secret=PRIVATE_SECRET",
),
"GET",
{ Contacts: contacts },
new Headers({
"x-minlimit-remaining": "59",
"x-daylimit-remaining": "4999",
authorization: "PRIVATE_TOKEN",
}),
new Date("2026-10-13T12:00:00Z"),
);
expect(result?.evidence.recordsInResponse).toBe(7);
expect(result?.evidence.response.Contacts).toHaveLength(3);
expect(result?.limits).toEqual({ minuteRemaining: 59, dayRemaining: 4999 });
expect(result?.evidence.request.path).not.toContain("?");
expect(JSON.stringify(result)).not.toContain("PRIVATE");
});
});
it("reads only the configured workspace, redacts stored evidence again and shows only a read-back invoice", async () => {
const client = new PGlite();
const db = drizzle(client);
const tenant = randomUUID();
const other = randomUUID();
const connection = randomUUID();
const otherConnection = randomUUID();
try {
// Just the tables the showcase reads, so this tests its SQL, not the
// code that writes them.
await client.exec(`
CREATE TABLE shopify_setups
(id uuid, tenant_id uuid, status text, credentials text);
CREATE TABLE square_setups
(id uuid, tenant_id uuid, status text, credentials text);
CREATE TABLE xero_setups
(id uuid, tenant_id uuid, status text, credentials text);
CREATE TABLE quickbooks_setups
(id uuid, tenant_id uuid, status text, credentials text);
CREATE TABLE stripe_setups
(id uuid, tenant_id uuid, status text, key_fingerprint text);
CREATE TABLE showcase_observations
(tenant_id uuid, provider text, connection_id uuid, data jsonb);
CREATE TABLE billing_delivery_audit
(tenant_id uuid, delivery_id uuid, action text, at timestamptz);
CREATE TABLE billing_deliveries
(id uuid DEFAULT gen_random_uuid(), tenant_id uuid,
connection_id uuid, status text, external_id text,
snapshot jsonb, updated_at timestamptz);
`);
await client.query(
`INSERT INTO quickbooks_setups VALUES
($1, $2, 'prepared', 'PRIVATE_TOKEN'),
($3, $4, 'prepared', 'OTHER_TOKEN')`,
[connection, tenant, otherConnection, other],
);
const evidence = {
request: {
method: "GET",
path: "/v3/company/{redacted}/invoice/{redacted}",
authorization: "PRIVATE_TOKEN",
},
status: 200,
response: {
Invoice: {
TotalAmt: 24,
CurrencyRef: { value: "GBP" },
PrivateNote: "PRIVATE_NOTE",
CustomerRef: { value: "PRIVATE_CUSTOMER" },
secret: "PRIVATE_TOKEN",
},
},
recordsInResponse: 1,
sampledAt: "2026-10-13T12:00:00Z",
};
const ours = {
evidence,
recordCount: 1,
scopes: ["com.intuit.quickbooks.accounting", "PRIVATE_TOKEN"],
credentials: "PRIVATE_TOKEN",
};
const theirs = {
recordCount: 999,
evidence: { ...evidence, response: { Invoice: { TotalAmt: 999 } } },
};
await client.query(
`INSERT INTO showcase_observations VALUES
($1, 'quickbooks', $2, $3),
($4, 'quickbooks', $5, $6)`,
[
tenant,
connection,
JSON.stringify(ours),
other,
otherConnection,
JSON.stringify(theirs),
],
);
const invoice = {
id: "183",
currency: "GBP",
totalCents: 2400,
privateNote: "PRIVATE_NOTE",
customerId: "PRIVATE_CUSTOMER",
};
const otherInvoice = { id: "999", currency: "GBP", totalCents: 99900 };
// 184 is still waiting for review, so it isn't evidence.
await client.query(
`INSERT INTO billing_deliveries
(tenant_id, connection_id, status, external_id, snapshot, updated_at)
VALUES
($1, $2, 'posted', '183', $3, now()),
($4, $5, 'posted', '999', $6, now()),
($1, $2, 'needs_review', '184', $3, now())`,
[
tenant,
connection,
JSON.stringify(invoice),
other,
otherConnection,
JSON.stringify(otherInvoice),
],
);
const quickbooks = async () =>
(await createShowcase(db, tenant).read()).providers.find(
(provider) => provider.provider === "quickbooks",
);
// Posted isn't enough: it must have been read back from QuickBooks.
expect((await quickbooks())?.invoice).toBeNull();
await client.query(
`INSERT INTO billing_delivery_audit
SELECT tenant_id, id, 'balance.refreshed', now()
FROM billing_deliveries WHERE external_id = '183'`,
);
const result = await createShowcase(db, tenant).read();
expect(result.configured).toBe(true);
expect(result.providers).toHaveLength(5);
const shown = result.providers.find(
(provider) => provider.provider === "quickbooks",
);
expect(shown?.invoice).toMatchObject({
amountCents: 2400,
readbackMatch: true,
});
// Its own ID stays private: this answer is public.
expect(shown?.invoice).not.toHaveProperty("id");
expect(shown?.recordsInLastRead).toBe(1);
const published = JSON.stringify(result);
for (const secret of [
tenant,
other,
connection,
otherConnection,
"PRIVATE_TOKEN",
"OTHER_TOKEN",
"PRIVATE_NOTE",
"PRIVATE_CUSTOMER",
'"999"',
'"184"',
])
expect(published).not.toContain(secret);
expect(published).not.toMatch(
/"(?:tenantId|connectionId|credentials|authorization|CustomerRef)"\s*:/,
);
expect(await createShowcase(db).read()).toEqual({
configured: false,
message: "Live sandboxes not configured in this environment",
providers: [],
});
expect(() => createShowcase(db, "invalid")).toThrow();
} finally {
await client.close();
}
});
it("serves a cache without calling providers, loads it once for concurrent requests, and limits each peer", async () => {
let clock = Date.parse("2026-10-13T12:00:00Z");
const read = vi.fn(async () => ({
configured: false as const,
message: "Live sandboxes not configured in this environment",
providers: [],
}));
const provider = vi.fn(() => {
throw new Error("Must never call a provider");
});
const store = {
showcase: { read },
quickbooks: { company: provider },
xero: { catalog: provider },
} as unknown as Store;
const app = Fastify({ trustProxy: false });
registerShowcase(app, store, () => clock);
const status = async (headers: Record<string, string> = {}) =>
(await app.inject({ url: "/api/showcase/sandboxes", headers })).statusCode;
try {
const first = await Promise.all(
Array.from({ length: 10 }, () =>
app.inject({ url: "/api/showcase/sandboxes?tenant=PRIVATE_OVERRIDE" }),
),
);
expect(first.every((response) => response.statusCode === 200)).toBe(true);
expect(read).toHaveBeenCalledTimes(1);
// A query string can't pick the workspace.
expect(read).toHaveBeenCalledWith();
expect(provider).not.toHaveBeenCalled();
expect(first[0].body).not.toContain("PRIVATE_OVERRIDE");
// 60 requests a minute per peer. With trustProxy off, a forwarded-for
// header doesn't make a new peer.
for (let request = 10; request < 60; request++)
expect(await status({ "x-forwarded-for": `198.51.100.${request}` })).toBe(
200,
);
expect(await status()).toBe(429);
clock += 60001;
expect(await status()).toBe(200);
expect(read).toHaveBeenCalledTimes(1);
// The cache lasts five minutes.
clock += 300000;
expect(await status()).toBe(200);
expect(read).toHaveBeenCalledTimes(2);
const write = await app.inject({
method: "POST",
url: "/api/showcase/sandboxes",
});
expect(write.statusCode).toBe(404);
} finally {
await app.close();
}
});
it("answers a generic error when saved evidence fails, never its details", async () => {
const app = Fastify();
registerShowcase(app, {
showcase: {
read: async () => {
throw new Error("PRIVATE_TOKEN PRIVATE_TENANT SQL");
},
},
} as unknown as Store);
try {
const response = await app.inject({ url: "/api/showcase/sandboxes" });
expect(response.statusCode).toBe(503);
expect(response.body).not.toContain("PRIVATE");
} finally {
await app.close();
}
});
it("records an owner's read and grant without tokens, leaving the response for the caller", async () => {
const client = new PGlite();
const db = drizzle(client);
const connection = { tenantId: randomUUID(), id: randomUUID() };
const at = new Date("2026-10-13T12:00:00Z");
const organisations = {
Organisations: [
{
OrganisationID: "PRIVATE_ORG",
Name: "PRIVATE_NAME",
IsDemoCompany: true,
CountryCode: "US",
BaseCurrency: "USD",
},
],
};
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
const body = String(input).includes("oauth")
? { access_token: "PRIVATE_TOKEN" }
: organisations;
return new Response(JSON.stringify(body), {
headers: {
"x-minlimit-remaining": "58",
"x-daylimit-remaining": "4998",
},
});
});
try {
await client.exec(
`CREATE TABLE showcase_observations
(tenant_id uuid, provider text, connection_id uuid, data jsonb,
PRIMARY KEY (tenant_id, provider, connection_id))`,
);
await db.transaction(async (tx) => {
const observed = observeReads(
tx,
connection,
"xero",
fetcher as typeof fetch,
() => at,
);
await observed("https://identity.xero.com/connect/token", {
method: "POST",
body: "PRIVATE_REQUEST_SECRET",
});
const response = await observed(
"https://api.xero.com/api.xro/2.0/Organisation",
{
headers: {
authorization: "Bearer PRIVATE_TOKEN",
"xero-tenant-id": "PRIVATE_ORG",
},
},
);
// The caller still gets the whole, unread response.
const read = await response.json();
expect(read.Organisations[0].Name).toBe("PRIVATE_NAME");
await observeGrant(
tx,
connection,
"xero",
["offline_access", "accounting.contacts.read", "PRIVATE_SCOPE"],
at,
);
});
const { rows } = await client.query<{ data: Record<string, unknown> }>(
"SELECT data FROM showcase_observations",
);
expect(rows).toHaveLength(1);
const [{ data }] = rows;
expect(data.lastRefreshAt).toBe(at.toISOString());
expect(data.lastReadAt).toBe(at.toISOString());
expect(data.recordCount).toBe(1);
expect(data.scopes).toEqual(["offline_access", "accounting.contacts.read"]);
expect(JSON.stringify(rows)).not.toContain("PRIVATE");
expect(fetcher).toHaveBeenCalledTimes(2);
} finally {
await client.close();
}
});