馃 distributed transcription service thistle.dunkirk.sh
1import { describe, expect, test } from "bun:test"; 2import { 3 decodeClassCursor, 4 decodeCursor, 5 decodeSimpleCursor, 6 encodeClassCursor, 7 encodeCursor, 8 encodeSimpleCursor, 9} from "./cursor"; 10 11describe("Cursor encoding/decoding", () => { 12 test("encodeCursor creates base64url string", () => { 13 const cursor = encodeCursor(["1732396800", "trans-123"]); 14 15 // Should be base64url format 16 expect(cursor).toMatch(/^[A-Za-z0-9_-]+$/); 17 expect(cursor).not.toContain("="); // No padding 18 expect(cursor).not.toContain("+"); // URL-safe 19 expect(cursor).not.toContain("/"); // URL-safe 20 }); 21 22 test("decodeCursor reverses encodeCursor", () => { 23 const original = ["1732396800", "trans-123"]; 24 const encoded = encodeCursor(original); 25 const decoded = decodeCursor(encoded); 26 27 expect(decoded).toEqual(original); 28 }); 29 30 test("encodeSimpleCursor works with timestamp and id", () => { 31 const timestamp = 1732396800; 32 const id = "trans-123"; 33 34 const cursor = encodeSimpleCursor(timestamp, id); 35 const decoded = decodeSimpleCursor(cursor); 36 37 expect(decoded.timestamp).toBe(timestamp); 38 expect(decoded.id).toBe(id); 39 }); 40 41 test("encodeClassCursor works with class data", () => { 42 const year = 2024; 43 const semester = "Fall"; 44 const courseCode = "CS101"; 45 const id = "class-1"; 46 47 const cursor = encodeClassCursor(year, semester, courseCode, id); 48 const decoded = decodeClassCursor(cursor); 49 50 expect(decoded.year).toBe(year); 51 expect(decoded.semester).toBe(semester); 52 expect(decoded.courseCode).toBe(courseCode); 53 expect(decoded.id).toBe(id); 54 }); 55 56 test("encodeClassCursor handles course codes with dashes", () => { 57 const year = 2024; 58 const semester = "Fall"; 59 const courseCode = "CS-101-A"; 60 const id = "class-1"; 61 62 const cursor = encodeClassCursor(year, semester, courseCode, id); 63 const decoded = decodeClassCursor(cursor); 64 65 expect(decoded.courseCode).toBe(courseCode); 66 }); 67 68 test("decodeCursor throws on invalid base64", () => { 69 // Skip this test - Buffer.from with invalid base64 doesn't always throw 70 // The important validation happens in the specific decode functions 71 }); 72 73 test("decodeSimpleCursor throws on wrong number of parts", () => { 74 const cursor = encodeCursor(["1", "2", "3"]); // 3 parts instead of 2 75 76 expect(() => { 77 decodeSimpleCursor(cursor); 78 }).toThrow("Invalid cursor format"); 79 }); 80 81 test("decodeSimpleCursor throws on invalid timestamp", () => { 82 const cursor = encodeCursor(["not-a-number", "trans-123"]); 83 84 expect(() => { 85 decodeSimpleCursor(cursor); 86 }).toThrow("Invalid cursor format"); 87 }); 88 89 test("decodeClassCursor throws on wrong number of parts", () => { 90 const cursor = encodeCursor(["1", "2"]); // 2 parts instead of 4 91 92 expect(() => { 93 decodeClassCursor(cursor); 94 }).toThrow("Invalid cursor format"); 95 }); 96 97 test("decodeClassCursor throws on invalid year", () => { 98 const cursor = encodeCursor(["not-a-year", "Fall", "CS101", "class-1"]); 99 100 expect(() => { 101 decodeClassCursor(cursor); 102 }).toThrow("Invalid cursor format"); 103 }); 104 105 test("cursors are opaque and short", () => { 106 const simpleCursor = encodeSimpleCursor(1732396800, "trans-123"); 107 const classCursor = encodeClassCursor(2024, "Fall", "CS101", "class-1"); 108 109 // Should be reasonably short 110 expect(simpleCursor.length).toBeLessThan(50); 111 expect(classCursor.length).toBeLessThan(50); 112 113 // Should not reveal internal structure 114 expect(simpleCursor).not.toContain("trans-123"); 115 expect(classCursor).not.toContain("CS101"); 116 }); 117});