馃 distributed transcription service
thistle.dunkirk.sh
1import { describe, expect, test } from "bun:test";
2import {
3 encodeCursor,
4 decodeCursor,
5 encodeSimpleCursor,
6 decodeSimpleCursor,
7 encodeClassCursor,
8 decodeClassCursor,
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});