Initial commit (unpacked platform)
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { PLAN_DEFINITIONS } from "@app/shared-types";
|
||||
import {
|
||||
canUseAiScan,
|
||||
computeEntitlements,
|
||||
resolvePlanFromProductId,
|
||||
signEntitlementToken,
|
||||
trialEndsAt,
|
||||
verifyEntitlementToken,
|
||||
} from "../src/index.js";
|
||||
|
||||
const NOW = new Date("2026-08-02T12:00:00Z");
|
||||
const SECRET = "test-secret-with-length";
|
||||
|
||||
describe("entitlements (spec §45–47)", () => {
|
||||
it("aktiv prenumeration ger planens förmåner", () => {
|
||||
const ent = computeEntitlements(
|
||||
{ plan: "family", status: "active", expiresAt: new Date("2026-09-01") },
|
||||
null,
|
||||
{ aiScansUsedThisMonth: 10 },
|
||||
NOW,
|
||||
);
|
||||
expect(ent.plan).toBe("family");
|
||||
expect(ent.maxHouseholdMembers).toBe(6);
|
||||
expect(ent.weekPlanning).toBe(true);
|
||||
});
|
||||
it("grace period behåller åtkomst (spec §44)", () => {
|
||||
const ent = computeEntitlements(
|
||||
{
|
||||
plan: "household",
|
||||
status: "in_grace",
|
||||
expiresAt: new Date("2026-08-01"),
|
||||
gracePeriodExpiresAt: new Date("2026-08-10"),
|
||||
},
|
||||
null,
|
||||
{ aiScansUsedThisMonth: 0 },
|
||||
NOW,
|
||||
);
|
||||
expect(ent.status).toBe("in_grace");
|
||||
expect(ent.weekPlanning).toBe(true);
|
||||
});
|
||||
it("utgången prenumeration utan grace → free", () => {
|
||||
const ent = computeEntitlements(
|
||||
{ plan: "family", status: "active", expiresAt: new Date("2026-07-01") },
|
||||
null,
|
||||
{ aiScansUsedThisMonth: 0 },
|
||||
NOW,
|
||||
);
|
||||
expect(ent.plan).toBe("free");
|
||||
});
|
||||
it("trial ger full tillgång i 7 dagar (spec §46)", () => {
|
||||
const start = new Date("2026-08-01T00:00:00Z");
|
||||
const ent = computeEntitlements(
|
||||
null,
|
||||
{ startedAt: start, endsAt: trialEndsAt(start) },
|
||||
{ aiScansUsedThisMonth: 3 },
|
||||
NOW,
|
||||
);
|
||||
expect(ent.status).toBe("trial");
|
||||
expect(ent.weekPlanning).toBe(true);
|
||||
});
|
||||
it("free tier: 10 skanningar och användbar grund (spec §46)", () => {
|
||||
const ent = computeEntitlements(null, null, { aiScansUsedThisMonth: 9 }, NOW);
|
||||
expect(ent.plan).toBe("free");
|
||||
expect(ent.aiScansPerMonth).toBe(10);
|
||||
expect(canUseAiScan(ent)).toBe(true);
|
||||
expect(canUseAiScan({ ...ent, aiScansUsedThisMonth: 10 })).toBe(false);
|
||||
});
|
||||
it("mappar productId → plan (härlett ur brand-konfig, aldrig hårdkodat)", () => {
|
||||
expect(resolvePlanFromProductId(PLAN_DEFINITIONS.family.appleProductId)).toBe("family");
|
||||
expect(resolvePlanFromProductId(PLAN_DEFINITIONS.household.googleProductId)).toBe("household");
|
||||
expect(resolvePlanFromProductId("okänd")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("signerad entitlement-token (spec §44)", () => {
|
||||
const ent = computeEntitlements(
|
||||
{ plan: "family", status: "active", expiresAt: new Date("2026-09-01") },
|
||||
null,
|
||||
{ aiScansUsedThisMonth: 0 },
|
||||
NOW,
|
||||
);
|
||||
|
||||
it("signerar och verifierar", () => {
|
||||
const token = signEntitlementToken("user-1", ent, SECRET, { now: NOW });
|
||||
const result = verifyEntitlementToken(token, SECRET, NOW);
|
||||
expect(result.valid).toBe(true);
|
||||
if (result.valid) {
|
||||
expect(result.payload.sub).toBe("user-1");
|
||||
expect(result.payload.plan).toBe("family");
|
||||
expect(result.withinGrace).toBe(false);
|
||||
}
|
||||
});
|
||||
it("avvisar manipulerad token", () => {
|
||||
const token = signEntitlementToken("user-1", ent, SECRET, { now: NOW });
|
||||
const tampered = token.slice(0, -4) + "AAAA";
|
||||
expect(verifyEntitlementToken(tampered, SECRET, NOW).valid).toBe(false);
|
||||
});
|
||||
it("avvisar fel secret", () => {
|
||||
const token = signEntitlementToken("user-1", ent, SECRET, { now: NOW });
|
||||
expect(verifyEntitlementToken(token, "annan-secret-123", NOW).valid).toBe(false);
|
||||
});
|
||||
it("grace: giltig efter exp men inom graceExp, ogiltig därefter – ALDRIG permanent boolean", () => {
|
||||
const token = signEntitlementToken("user-1", ent, SECRET, {
|
||||
now: NOW,
|
||||
ttlHours: 24,
|
||||
graceDays: 7,
|
||||
});
|
||||
const afterExp = new Date(NOW.getTime() + 3 * 86_400_000);
|
||||
const inGrace = verifyEntitlementToken(token, SECRET, afterExp);
|
||||
expect(inGrace.valid).toBe(true);
|
||||
if (inGrace.valid) expect(inGrace.withinGrace).toBe(true);
|
||||
|
||||
const afterGrace = new Date(NOW.getTime() + 9 * 86_400_000);
|
||||
const expired = verifyEntitlementToken(token, SECRET, afterGrace);
|
||||
expect(expired.valid).toBe(false);
|
||||
if (!expired.valid) expect(expired.reason).toBe("expired_beyond_grace");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user