atproto user agency toolkit for individuals and groups
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 205 lines 6.5 kB view raw
1import { describe, it, expect, beforeEach, afterEach } from "vitest"; 2import { mkdtempSync, rmSync } from "node:fs"; 3import { tmpdir } from "node:os"; 4import { join } from "node:path"; 5import Database from "better-sqlite3"; 6 7import { PolicyStorage } from "./storage.js"; 8import type { StoredPolicy } from "./types.js"; 9import { toStoredPolicy } from "./types.js"; 10import { 11 DEFAULT_REPLICATION, 12 DEFAULT_SYNC, 13 DEFAULT_RETENTION, 14} from "./types.js"; 15import type { Policy } from "./types.js"; 16 17function makePolicy(overrides: Partial<Policy> & { id: string }): Policy { 18 return { 19 name: overrides.id, 20 target: { type: "list", dids: ["did:plc:test"] }, 21 replication: { ...DEFAULT_REPLICATION }, 22 sync: { ...DEFAULT_SYNC }, 23 retention: { ...DEFAULT_RETENTION }, 24 priority: 50, 25 enabled: true, 26 ...overrides, 27 }; 28} 29 30function makeStoredPolicy(id: string, did: string = "did:plc:test"): StoredPolicy { 31 return toStoredPolicy( 32 makePolicy({ id, target: { type: "list", dids: [did] } }), 33 "archive", 34 { source: "manual", createdBy: "test" }, 35 ); 36} 37 38describe("PolicyStorage", () => { 39 let tmpDir: string; 40 let db: InstanceType<typeof Database>; 41 let storage: PolicyStorage; 42 43 beforeEach(() => { 44 tmpDir = mkdtempSync(join(tmpdir(), "policy-storage-test-")); 45 db = new Database(join(tmpDir, "test.db")); 46 storage = new PolicyStorage(db); 47 storage.initSchema(); 48 }); 49 50 afterEach(() => { 51 db.close(); 52 rmSync(tmpDir, { recursive: true, force: true }); 53 }); 54 55 describe("round-trip", () => { 56 it("upsert and load a policy", () => { 57 const policy = makeStoredPolicy("test-1", "did:plc:alice"); 58 storage.upsertPolicy(policy); 59 60 const loaded = storage.getPolicy("test-1"); 61 expect(loaded).not.toBeNull(); 62 expect(loaded!.id).toBe("test-1"); 63 expect(loaded!.type).toBe("archive"); 64 expect(loaded!.state).toBe("active"); 65 expect(loaded!.source).toBe("manual"); 66 expect(loaded!.consent).toBe("unconsented"); 67 expect(loaded!.createdBy).toBe("test"); 68 expect(loaded!.target).toEqual({ type: "list", dids: ["did:plc:alice"] }); 69 expect(loaded!.replication).toEqual(DEFAULT_REPLICATION); 70 expect(loaded!.sync).toEqual(DEFAULT_SYNC); 71 expect(loaded!.retention).toEqual(DEFAULT_RETENTION); 72 expect(loaded!.priority).toBe(50); 73 expect(loaded!.enabled).toBe(true); 74 }); 75 76 it("upsert updates an existing policy", () => { 77 const policy = makeStoredPolicy("test-1"); 78 storage.upsertPolicy(policy); 79 80 const updated = { ...policy, name: "Updated Name", priority: 99 }; 81 storage.upsertPolicy(updated); 82 83 const loaded = storage.getPolicy("test-1"); 84 expect(loaded!.name).toBe("Updated Name"); 85 expect(loaded!.priority).toBe(99); 86 }); 87 88 it("preserves JSON fields correctly", () => { 89 const policy = toStoredPolicy( 90 makePolicy({ 91 id: "json-test", 92 replication: { minCopies: 3, preferredPeers: ["did:plc:peer1"] }, 93 retention: { maxAgeSec: 86400, keepHistory: true }, 94 }), 95 "saas", 96 ); 97 storage.upsertPolicy(policy); 98 99 const loaded = storage.getPolicy("json-test")!; 100 expect(loaded.replication.minCopies).toBe(3); 101 expect(loaded.replication.preferredPeers).toEqual(["did:plc:peer1"]); 102 expect(loaded.retention.maxAgeSec).toBe(86400); 103 expect(loaded.retention.keepHistory).toBe(true); 104 }); 105 }); 106 107 describe("loadPolicies", () => { 108 it("loads all policies when no state filter", () => { 109 storage.upsertPolicy(makeStoredPolicy("p1")); 110 storage.upsertPolicy(makeStoredPolicy("p2")); 111 const all = storage.loadPolicies(); 112 expect(all).toHaveLength(2); 113 }); 114 115 it("filters by state", () => { 116 const active = makeStoredPolicy("active"); 117 const suspended = { ...makeStoredPolicy("suspended"), state: "suspended" as const }; 118 storage.upsertPolicy(active); 119 storage.upsertPolicy(suspended); 120 121 const result = storage.loadPolicies(["active"]); 122 expect(result).toHaveLength(1); 123 expect(result[0]!.id).toBe("active"); 124 }); 125 126 it("loadActivePolicies returns only active", () => { 127 storage.upsertPolicy(makeStoredPolicy("a1")); 128 storage.upsertPolicy({ ...makeStoredPolicy("t1"), state: "terminated" }); 129 const active = storage.loadActivePolicies(); 130 expect(active).toHaveLength(1); 131 expect(active[0]!.id).toBe("a1"); 132 }); 133 }); 134 135 describe("state transitions", () => { 136 it("transitions active → suspended", () => { 137 storage.upsertPolicy(makeStoredPolicy("p1")); 138 const result = storage.transitionState("p1", "suspended"); 139 expect(result).toBe(true); 140 141 const loaded = storage.getPolicy("p1")!; 142 expect(loaded.state).toBe("suspended"); 143 expect(loaded.suspendedAt).not.toBeNull(); 144 }); 145 146 it("transitions to terminated sets terminatedAt", () => { 147 storage.upsertPolicy(makeStoredPolicy("p1")); 148 storage.transitionState("p1", "terminated"); 149 const loaded = storage.getPolicy("p1")!; 150 expect(loaded.state).toBe("terminated"); 151 expect(loaded.terminatedAt).not.toBeNull(); 152 }); 153 154 it("transitions to active sets activatedAt", () => { 155 const policy = { ...makeStoredPolicy("p1"), state: "suspended" as const }; 156 storage.upsertPolicy(policy); 157 storage.transitionState("p1", "active"); 158 const loaded = storage.getPolicy("p1")!; 159 expect(loaded.state).toBe("active"); 160 expect(loaded.activatedAt).not.toBeNull(); 161 }); 162 163 it("returns false for non-existent policy", () => { 164 expect(storage.transitionState("nope", "suspended")).toBe(false); 165 }); 166 }); 167 168 describe("deletePolicy", () => { 169 it("deletes and returns true", () => { 170 storage.upsertPolicy(makeStoredPolicy("p1")); 171 expect(storage.deletePolicy("p1")).toBe(true); 172 expect(storage.getPolicy("p1")).toBeNull(); 173 }); 174 175 it("returns false for non-existent", () => { 176 expect(storage.deletePolicy("nope")).toBe(false); 177 }); 178 }); 179 180 describe("hasPolicy", () => { 181 it("returns true for existing", () => { 182 storage.upsertPolicy(makeStoredPolicy("p1")); 183 expect(storage.hasPolicy("p1")).toBe(true); 184 }); 185 186 it("returns false for non-existent", () => { 187 expect(storage.hasPolicy("nope")).toBe(false); 188 }); 189 }); 190 191 describe("count", () => { 192 it("counts all policies", () => { 193 storage.upsertPolicy(makeStoredPolicy("p1")); 194 storage.upsertPolicy(makeStoredPolicy("p2")); 195 expect(storage.count()).toBe(2); 196 }); 197 198 it("counts by state", () => { 199 storage.upsertPolicy(makeStoredPolicy("p1")); 200 storage.upsertPolicy({ ...makeStoredPolicy("p2"), state: "terminated" }); 201 expect(storage.count("active")).toBe(1); 202 expect(storage.count("terminated")).toBe(1); 203 }); 204 }); 205});