Files
HealthLog/src/lib/auth/__tests__/native-client.test.ts
T
Marc-André Bombeck 1f4ba5939d style: apply prettier across the tree
One-shot prettier pass so the new format:check gate starts from a clean
baseline. Formatting only — no behaviour change. Generator-owned and
vendored artifacts stay untouched via .prettierignore.
2026-06-19 22:20:31 +02:00

138 lines
4.0 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
classifyClient,
resolveTokenPolicy,
shouldIssueBearerToken,
isCookielessNativeCaller,
} from "../native-client";
function h(pairs: Record<string, string>): Headers {
return new Headers(pairs);
}
describe("native-client classification", () => {
it.each([
["browser Chrome", "Mozilla/5.0 (Macintosh) Chrome/142", "web"],
[
"browser Safari",
"Mozilla/5.0 (iPhone; CPU iPhone OS 18_0) Safari/605",
"web",
],
["HealthLog-iOS", "HealthLog-iOS/1.4.0", "native"],
["HealthLog-iPad", "HealthLog-iPad/1.4.0", "native"],
["n8n", "n8n/1.62.0", "native"],
["Health-Connect", "Health-Connect/2025.1", "native"],
["unknown UA", "curl/8.5", "native"],
["empty UA", "", "native"],
] as const)("%s → %s", (_name, ua, expected) => {
expect(classifyClient(h({ "user-agent": ua }))).toBe(expected);
});
it("X-Client-Type header overrides UA", () => {
const browserAsNative = h({
"user-agent": "Mozilla/5.0",
"x-client-type": "native",
});
expect(classifyClient(browserAsNative)).toBe("native");
const iosAsWeb = h({
"user-agent": "HealthLog-iOS/1.0",
"x-client-type": "web",
});
expect(classifyClient(iosAsWeb)).toBe("web");
});
});
describe("resolveTokenPolicy", () => {
it("web → 90d access, no refresh", () => {
const p = resolveTokenPolicy(h({ "user-agent": "Mozilla/5.0" }));
expect(p).toEqual({
policy: "web",
accessTokenDays: 90,
refreshTokenDays: null,
tokenLabel: "web",
});
});
it("native → 1d access + 60d refresh", () => {
const p = resolveTokenPolicy(h({ "user-agent": "HealthLog-iOS/1.4" }));
expect(p).toEqual({
policy: "native",
accessTokenDays: 1,
refreshTokenDays: 60,
tokenLabel: "native",
});
});
});
describe("shouldIssueBearerToken", () => {
it("emits Bearer for native UAs and X-Client-Type:native", () => {
expect(shouldIssueBearerToken(h({ "user-agent": "HealthLog-iOS/1" }))).toBe(
true,
);
expect(shouldIssueBearerToken(h({ "user-agent": "n8n/1" }))).toBe(true);
expect(
shouldIssueBearerToken(
h({ "x-client-type": "native", "user-agent": "Mozilla/5.0" }),
),
).toBe(true);
});
it("does NOT emit Bearer for plain browser sessions", () => {
expect(
shouldIssueBearerToken(h({ "user-agent": "Mozilla/5.0 Safari" })),
).toBe(false);
expect(shouldIssueBearerToken(h({}))).toBe(false);
});
});
describe("isCookielessNativeCaller (M-3 refresh-token issuance gate)", () => {
it("true for a genuine native caller (native UA, no cookie)", () => {
expect(
isCookielessNativeCaller(h({ "user-agent": "HealthLog-iOS/1.4" })),
).toBe(true);
expect(isCookielessNativeCaller(h({ "x-client-type": "native" }))).toBe(
true,
);
// Blank UA classifies native and carries no cookie.
expect(isCookielessNativeCaller(h({}))).toBe(true);
});
it("false for a plain web browser (not native)", () => {
expect(
isCookielessNativeCaller(h({ "user-agent": "Mozilla/5.0 Safari" })),
).toBe(false);
});
it("false for a browser spoofing X-Client-Type:native via Mozilla UA", () => {
// classifyClient honours the header (native), but the Mozilla UA is the
// browser tell that suppresses refresh-token issuance.
expect(
isCookielessNativeCaller(
h({ "x-client-type": "native", "user-agent": "Mozilla/5.0 Chrome" }),
),
).toBe(false);
});
it("false when an inbound session cookie is present (browser round-trip)", () => {
expect(
isCookielessNativeCaller(
h({
"x-client-type": "native",
cookie: "foo=bar; healthlog_session=abc123; baz=qux",
}),
),
).toBe(false);
});
it("true for a native caller whose cookie header lacks the session cookie", () => {
expect(
isCookielessNativeCaller(
h({
"x-client-type": "native",
cookie: "other=1; hl_onboarding=pending",
}),
),
).toBe(true);
});
});