learn and share notes on atproto (wip) 馃 malfestio.stormlightlabs.org/
readability solid axum atproto srs
at main 203 lines 5.8 kB view raw
1import "fake-indexeddb/auto"; 2import { afterEach, beforeEach, describe, expect, it } from "vitest"; 3import { db, generateLocalId, isLocalId, type LocalDeck, type LocalNote } from "../db"; 4 5describe("db", () => { 6 beforeEach(async () => { 7 await db.decks.clear(); 8 await db.cards.clear(); 9 await db.notes.clear(); 10 await db.syncQueue.clear(); 11 }); 12 13 afterEach(async () => { 14 await db.decks.clear(); 15 await db.cards.clear(); 16 await db.notes.clear(); 17 await db.syncQueue.clear(); 18 }); 19 20 describe("generateLocalId", () => { 21 it("should generate unique IDs with local_ prefix", () => { 22 const id1 = generateLocalId(); 23 const id2 = generateLocalId(); 24 25 expect(id1).toMatch(/^local_\d+_[a-z0-9]+$/); 26 expect(id2).toMatch(/^local_\d+_[a-z0-9]+$/); 27 expect(id1).not.toBe(id2); 28 }); 29 }); 30 31 describe("isLocalId", () => { 32 it("should return true for local IDs", () => { 33 expect(isLocalId("local_123_abc")).toBe(true); 34 expect(isLocalId("local_")).toBe(true); 35 }); 36 37 it("should return false for server IDs", () => { 38 expect(isLocalId("deck-123")).toBe(false); 39 expect(isLocalId("uuid-abc-def")).toBe(false); 40 }); 41 }); 42 43 describe("decks table", () => { 44 it("should insert and retrieve decks", async () => { 45 const deck: LocalDeck = { 46 id: "deck-1", 47 ownerDid: "did:plc:test", 48 title: "Test Deck", 49 description: "A test deck", 50 tags: ["test"], 51 visibility: { type: "Private" }, 52 syncStatus: "local_only", 53 localVersion: 1, 54 updatedAt: new Date().toISOString(), 55 }; 56 57 await db.decks.put(deck); 58 const retrieved = await db.decks.get("deck-1"); 59 60 expect(retrieved).toBeDefined(); 61 expect(retrieved?.title).toBe("Test Deck"); 62 expect(retrieved?.syncStatus).toBe("local_only"); 63 }); 64 65 it("should query decks by ownerDid", async () => { 66 await db.decks.bulkPut([{ 67 id: "deck-1", 68 ownerDid: "did:alice", 69 title: "Alice Deck 1", 70 description: "", 71 tags: [], 72 visibility: { type: "Private" }, 73 syncStatus: "synced", 74 localVersion: 1, 75 updatedAt: new Date().toISOString(), 76 }, { 77 id: "deck-2", 78 ownerDid: "did:alice", 79 title: "Alice Deck 2", 80 description: "", 81 tags: [], 82 visibility: { type: "Public" }, 83 syncStatus: "pending_push", 84 localVersion: 2, 85 updatedAt: new Date().toISOString(), 86 }, { 87 id: "deck-3", 88 ownerDid: "did:bob", 89 title: "Bob Deck", 90 description: "", 91 tags: [], 92 visibility: { type: "Private" }, 93 syncStatus: "local_only", 94 localVersion: 1, 95 updatedAt: new Date().toISOString(), 96 }]); 97 98 const aliceDecks = await db.decks.where("ownerDid").equals("did:alice").toArray(); 99 expect(aliceDecks).toHaveLength(2); 100 expect(aliceDecks.map((d) => d.title)).toContain("Alice Deck 1"); 101 expect(aliceDecks.map((d) => d.title)).toContain("Alice Deck 2"); 102 }); 103 104 it("should query decks by syncStatus", async () => { 105 await db.decks.bulkPut([{ 106 id: "deck-1", 107 ownerDid: "did:test", 108 title: "Synced", 109 description: "", 110 tags: [], 111 visibility: { type: "Private" }, 112 syncStatus: "synced", 113 localVersion: 1, 114 updatedAt: new Date().toISOString(), 115 }, { 116 id: "deck-2", 117 ownerDid: "did:test", 118 title: "Conflict", 119 description: "", 120 tags: [], 121 visibility: { type: "Private" }, 122 syncStatus: "conflict", 123 localVersion: 1, 124 updatedAt: new Date().toISOString(), 125 }]); 126 127 const conflicts = await db.decks.where("syncStatus").equals("conflict").toArray(); 128 expect(conflicts).toHaveLength(1); 129 expect(conflicts[0].title).toBe("Conflict"); 130 }); 131 }); 132 133 describe("notes table", () => { 134 it("should insert and retrieve notes", async () => { 135 const note: LocalNote = { 136 id: "note-1", 137 ownerDid: "did:plc:test", 138 title: "Test Note", 139 body: "This is a test note", 140 tags: ["test"], 141 visibility: { type: "Private" }, 142 links: [], 143 syncStatus: "local_only", 144 localVersion: 1, 145 updatedAt: new Date().toISOString(), 146 }; 147 148 await db.notes.put(note); 149 const retrieved = await db.notes.get("note-1"); 150 151 expect(retrieved).toBeDefined(); 152 expect(retrieved?.title).toBe("Test Note"); 153 expect(retrieved?.body).toBe("This is a test note"); 154 }); 155 }); 156 157 describe("syncQueue table", () => { 158 it("should auto-increment IDs", async () => { 159 const id1 = await db.syncQueue.add({ 160 entityType: "deck", 161 entityId: "deck-1", 162 operation: "push", 163 createdAt: new Date().toISOString(), 164 retryCount: 0, 165 }); 166 167 const id2 = await db.syncQueue.add({ 168 entityType: "note", 169 entityId: "note-1", 170 operation: "push", 171 createdAt: new Date().toISOString(), 172 retryCount: 0, 173 }); 174 175 expect(id2).toBeGreaterThan(id1!); 176 }); 177 178 it("should count pending items", async () => { 179 await db.syncQueue.bulkAdd([{ 180 entityType: "deck", 181 entityId: "deck-1", 182 operation: "push", 183 createdAt: new Date().toISOString(), 184 retryCount: 0, 185 }, { 186 entityType: "deck", 187 entityId: "deck-2", 188 operation: "push", 189 createdAt: new Date().toISOString(), 190 retryCount: 0, 191 }, { 192 entityType: "note", 193 entityId: "note-1", 194 operation: "push", 195 createdAt: new Date().toISOString(), 196 retryCount: 0, 197 }]); 198 199 const count = await db.syncQueue.count(); 200 expect(count).toBe(3); 201 }); 202 }); 203});