1import { describe, it, expect } from "vitest";
2import { OrgPolicySchema, OrgPolicyUpdateSchema, parseStoredPolicy, type OrgPolicy } from "./policy.schema.js";
3import { effectiveMaxFileBytes, planMaxFileBytes } from "./org-policy.service.js";
4import { PLAN_USAGE_LIMITS } from "../files/quota.js";
5
6
7const withCap = (mb: number): OrgPolicy => parseStoredPolicy({ maxFileSizeMB: mb });
8
9describe("OrgPolicy schema", () => {
10 it("defaults are the PERMISSIVE baseline (backward compat — rule 6)", () => {
11 const p = OrgPolicySchema.parse({});
12 expect(p).toEqual({
13 require2FA: false,
14 ssoEnforced: false,
15 disableGuestPublicShare: false,
16 allowExternalEmbeds: true
17
18 });
19 });
20
21 it("parseStoredPolicy tolerates null / non-object / unknown keys", () => {
22 expect(parseStoredPolicy(null)).toEqual(OrgPolicySchema.parse({}));
23 expect(parseStoredPolicy(undefined)).toEqual(OrgPolicySchema.parse({}));
24 expect(parseStoredPolicy("junk")).toEqual(OrgPolicySchema.parse({}));
25 const p = parseStoredPolicy({ require2FA: true, futureField: 42 });
26 expect(p.require2FA).toBe(true);
27 expect("futureField" in p).toBe(false);
28 });
29
30 it("parseStoredPolicy parses a full stored policy", () => {
31 const p = parseStoredPolicy({
32 require2FA: true,
33 ssoEnforced: true,
34 disableGuestPublicShare: true,
35 maxFileSizeMB: 100,
36 sessionLifetimeHours: 12,
37 allowExternalEmbeds: false
38 });
39 expect(p.require2FA).toBe(true);
40 expect(p.ssoEnforced).toBe(true);
41 expect(p.disableGuestPublicShare).toBe(true);
42 expect(p.maxFileSizeMB).toBe(100);
43 expect(p.sessionLifetimeHours).toBe(12);
44 expect(p.allowExternalEmbeds).toBe(false);
45 });
46
47 it("maxFileSizeMB bounds (1..10240)", () => {
48 expect(OrgPolicySchema.parse({ maxFileSizeMB: 1 }).maxFileSizeMB).toBe(1);
49 expect(OrgPolicySchema.parse({ maxFileSizeMB: 10240 }).maxFileSizeMB).toBe(10240);
50 expect(() => OrgPolicySchema.parse({ maxFileSizeMB: 0 })).toThrow();
51 expect(() => OrgPolicySchema.parse({ maxFileSizeMB: 10241 })).toThrow();
52 expect(() => OrgPolicySchema.parse({ maxFileSizeMB: 1.5 })).toThrow();
53 });
54
55 it("sessionLifetimeHours bounds (1..720)", () => {
56 expect(OrgPolicySchema.parse({ sessionLifetimeHours: 1 }).sessionLifetimeHours).toBe(1);
57 expect(OrgPolicySchema.parse({ sessionLifetimeHours: 720 }).sessionLifetimeHours).toBe(720);
58 expect(() => OrgPolicySchema.parse({ sessionLifetimeHours: 0 })).toThrow();
59 expect(() => OrgPolicySchema.parse({ sessionLifetimeHours: 721 })).toThrow();
60 });
61
62 it("update schema requires at least one field, allows null (clear)", () => {
63 expect(() => OrgPolicyUpdateSchema.parse({})).toThrow();
64 expect(OrgPolicyUpdateSchema.parse({ require2FA: true })).toEqual({ require2FA: true });
65 expect(OrgPolicyUpdateSchema.parse({ maxFileSizeMB: null }).maxFileSizeMB).toBeNull();
66 });
67});
68
69describe("plan / effective max-file-size caps", () => {
70 it("planMaxFileBytes honors the plan (free 50MB, paid 500MB, unknown → free)", () => {
71 expect(planMaxFileBytes("free")).toBe(PLAN_USAGE_LIMITS.free!.maxFileSize);
72 expect(planMaxFileBytes("paid")).toBe(PLAN_USAGE_LIMITS.paid!.maxFileSize);
73 expect(planMaxFileBytes("nope")).toBe(PLAN_USAGE_LIMITS.free!.maxFileSize);
74 });
75
76 it("org policy caps the plan (TIGHTEN only)", () => {
77
78 expect(effectiveMaxFileBytes(withCap(10), "free")).toBe(10 * 1024 * 1024);
79
80 expect(effectiveMaxFileBytes(withCap(200), "paid")).toBe(200 * 1024 * 1024);
81 });
82
83 it("an org cap ABOVE the plan cannot raise it (plan ceiling wins)", () => {
84
85 expect(effectiveMaxFileBytes(withCap(500), "free")).toBe(PLAN_USAGE_LIMITS.free!.maxFileSize);
86 });
87
88 it("no org cap (null / unset) → the plan cap (current behavior)", () => {
89 expect(effectiveMaxFileBytes(null, "free")).toBe(PLAN_USAGE_LIMITS.free!.maxFileSize);
90 expect(effectiveMaxFileBytes(parseStoredPolicy({}), "paid")).toBe(
91 PLAN_USAGE_LIMITS.paid!.maxFileSize
92 );
93 });
94});
95