at://Press
1import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
3// Use in-memory SQLite for tests
4process.env.DRAFTS_DB_PATH = ":memory:";
5
6import { listDrafts, getDraft, saveDraft, deleteDraft, generateRkey, closeDb } from "../../src/lib/drafts";
7
8describe("drafts", () => {
9 beforeEach(() => {
10 // Reset the DB connection so each test gets a fresh in-memory DB
11 closeDb();
12 });
13
14 afterEach(() => {
15 closeDb();
16 });
17
18 describe("generateRkey", () => {
19 it("generates valid rkeys matching RKEY_REGEX", () => {
20 const rkey = generateRkey();
21 expect(rkey).toMatch(/^[a-zA-Z0-9._~-]{1,512}$/);
22 expect(rkey).toHaveLength(13);
23 });
24
25 it("generates unique rkeys", () => {
26 const keys = new Set(Array.from({ length: 100 }, () => generateRkey()));
27 expect(keys.size).toBe(100);
28 });
29 });
30
31 describe("saveDraft / getDraft", () => {
32 it("saves and retrieves a draft", () => {
33 const rkey = saveDraft({
34 title: "Test Draft",
35 content: "Some content",
36 });
37
38 const draft = getDraft(rkey);
39 expect(draft).not.toBeNull();
40 expect(draft!.title).toBe("Test Draft");
41 expect(draft!.content).toBe("Some content");
42 expect(draft!.visibility).toBe("author");
43 expect(draft!.rkey).toBe(rkey);
44 });
45
46 it("saves with explicit rkey", () => {
47 const rkey = saveDraft({
48 rkey: "my-custom-rkey",
49 title: "Custom Key",
50 content: "Content",
51 });
52
53 expect(rkey).toBe("my-custom-rkey");
54 const draft = getDraft("my-custom-rkey");
55 expect(draft!.title).toBe("Custom Key");
56 });
57
58 it("updates an existing draft via upsert", () => {
59 saveDraft({
60 rkey: "update-test",
61 title: "Original Title",
62 content: "Original Content",
63 });
64
65 saveDraft({
66 rkey: "update-test",
67 title: "Updated Title",
68 content: "Updated Content",
69 });
70
71 const draft = getDraft("update-test");
72 expect(draft!.title).toBe("Updated Title");
73 expect(draft!.content).toBe("Updated Content");
74 });
75
76 it("stores and retrieves blobs as JSON", () => {
77 const blobs = [
78 {
79 $type: "blob" as const,
80 ref: { $link: "bafkreitest123" },
81 mimeType: "image/png",
82 size: 12345,
83 },
84 ];
85
86 saveDraft({
87 rkey: "blob-test",
88 title: "With Blob",
89 content: "Content",
90 blobs,
91 });
92
93 const draft = getDraft("blob-test");
94 expect(draft!.blobs).toHaveLength(1);
95 expect(draft!.blobs![0].ref.$link).toBe("bafkreitest123");
96 expect(draft!.blobs![0].mimeType).toBe("image/png");
97 });
98
99 it("stores null blobs when none provided", () => {
100 saveDraft({
101 rkey: "no-blob",
102 title: "No Blob",
103 content: "Content",
104 });
105
106 const draft = getDraft("no-blob");
107 expect(draft!.blobs).toBeUndefined();
108 });
109
110 it("preserves createdAt when provided", () => {
111 const date = "2025-06-01T12:00:00.000Z";
112 saveDraft({
113 rkey: "date-test",
114 title: "Dated",
115 content: "Content",
116 createdAt: date,
117 });
118
119 const draft = getDraft("date-test");
120 expect(draft!.createdAt).toBe(date);
121 });
122
123 it("returns null for non-existent rkey", () => {
124 const draft = getDraft("nonexistent");
125 expect(draft).toBeNull();
126 });
127
128 it("sets empty uri and cid on draft entries", () => {
129 saveDraft({
130 rkey: "fields-test",
131 title: "Fields",
132 content: "Content",
133 });
134
135 const draft = getDraft("fields-test");
136 expect(draft!.uri).toBe("");
137 expect(draft!.cid).toBe("");
138 });
139 });
140
141 describe("listDrafts", () => {
142 it("returns drafts sorted newest first", () => {
143 saveDraft({
144 rkey: "older",
145 title: "Older Draft",
146 content: "C",
147 createdAt: "2025-01-01T00:00:00Z",
148 });
149 saveDraft({
150 rkey: "newer",
151 title: "Newer Draft",
152 content: "C",
153 createdAt: "2025-06-01T00:00:00Z",
154 });
155
156 const drafts = listDrafts();
157 expect(drafts).toHaveLength(2);
158 expect(drafts[0].title).toBe("Newer Draft");
159 expect(drafts[1].title).toBe("Older Draft");
160 });
161
162 it("returns empty array when no drafts exist", () => {
163 const drafts = listDrafts();
164 expect(drafts).toEqual([]);
165 });
166 });
167
168 describe("deleteDraft", () => {
169 it("deletes an existing draft", () => {
170 saveDraft({
171 rkey: "to-delete",
172 title: "Delete Me",
173 content: "Content",
174 });
175
176 expect(getDraft("to-delete")).not.toBeNull();
177 const result = deleteDraft("to-delete");
178 expect(result).toBe(true);
179 expect(getDraft("to-delete")).toBeNull();
180 });
181
182 it("returns false when draft does not exist", () => {
183 const result = deleteDraft("nonexistent");
184 expect(result).toBe(false);
185 });
186
187 it("removes draft from list", () => {
188 saveDraft({ rkey: "keep", title: "Keep", content: "C" });
189 saveDraft({ rkey: "remove", title: "Remove", content: "C" });
190
191 deleteDraft("remove");
192 const drafts = listDrafts();
193 expect(drafts).toHaveLength(1);
194 expect(drafts[0].rkey).toBe("keep");
195 });
196 });
197});