Files
HealthLog/src/__tests__/record-settings-route-guard.test.ts
T
Marc-André Bombeck 74ce17b7de feat(integrations): let a guardian connect a managed profile's providers
A guardian acting on a managed profile can now connect and disconnect its
Withings, WHOOP, Oura, Polar, Strava and Google Health from the profile's
integration cards, and each card says whose record it connects for. The
connect starts with a fenced fetch that mints a state naming the profile;
the provider's callback lands tokens and data there only after checking
that the browser belongs to a guardian with a live MANAGE grant on it.
Without a provider app of its own, the profile borrows the guardian's,
with an audit row. Withings and Google Health callbacks read the session
directly, since a provider redirect cannot carry the record-session header.
2026-10-21 19:49:07 +02:00

71 lines
2.5 KiB
TypeScript

import { readdir, readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { MANAGED_RECORD_SETTINGS_FIELD_ALLOWLIST } from "@/lib/record-settings";
const fromRoot = (...segments: string[]) => resolve(process.cwd(), ...segments);
const RECORD_SETTINGS_ROUTE_ROOT = fromRoot("src/app/api/record-settings");
describe("record settings route guard", () => {
it("freezes the non-empty guardian-only route inventory", async () => {
const discovered = (
await readdir(RECORD_SETTINGS_ROUTE_ROOT, {
recursive: true,
})
)
.filter((entry) => entry.endsWith("route.ts"))
.map((entry) => `app/api/record-settings/${entry}`)
.sort();
expect(discovered).toHaveLength(5);
expect(discovered).toEqual([
"app/api/record-settings/[family]/route.ts",
"app/api/record-settings/integrations/[provider]/connect/route.ts",
"app/api/record-settings/integrations/[provider]/disconnect/route.ts",
"app/api/record-settings/integrations/route.ts",
"app/api/record-settings/route.ts",
]);
});
it("resolves every record-settings route through the Guardian fence", async () => {
const routes = [
"route.ts",
"integrations/route.ts",
"integrations/[provider]/connect/route.ts",
"integrations/[provider]/disconnect/route.ts",
"[family]/route.ts",
] as const;
const sources = await Promise.all(
routes.map((route) =>
readFile(resolve(RECORD_SETTINGS_ROUTE_ROOT, route), "utf8"),
),
);
for (const source of sources) {
expect(source).toContain("requireGuardianAuth");
expect(source).toContain("resolveGuardianRecordSettingsAccess");
}
});
it("makes the field-specific route exhaustive over the approved DTO families", async () => {
const source = await readFile(
resolve(RECORD_SETTINGS_ROUTE_ROOT, "[family]/route.ts"),
"utf8",
);
const families = Object.keys(MANAGED_RECORD_SETTINGS_FIELD_ALLOWLIST);
expect(families.length).toBeGreaterThan(0);
for (const family of families) {
expect(source).toContain(`case \"${family}\"`);
}
expect(source).toContain("if (!isManagedRecordSettingsFamily(rawFamily))");
expect(source).toContain('apiError("Settings family not found", 404)');
expect(
source.indexOf("isManagedRecordSettingsFamily(rawFamily)"),
).toBeLessThan(source.indexOf("const context = await requireGuardianAuth"));
});
});