Skip to content
File

Blob: tests/client/api/session-storage.test.ts

typescript188 lines
1import { describe, expect, it } from "vitest";
2import { setStoredBookmark, getStoredBookmark } from "@/client/lib/api/bookmarks";
3import {
4 clearAdminBookmark,
5 getInboxSession,
6 loadInboxSessions,
7 removeInboxSession,
8 storeInboxSession,
9 updateInboxSession,
10} from "@/client/lib/api/session-storage";
11import { ADMIN_BOOKMARK_SCOPE, INBOX_SESSIONS_KEY, INBOX_TOKEN_KEY_PREFIX } from "@/client/lib/api/shared";
12 
13function makeSession(
14 index: number,
15 overrides: Partial<{ address: string; expiresAt: string; token: string; ttlHours: 24 | 48 | 72 }> = {},
16) {
17 return {
18 address: overrides.address ?? `user${index}@mail.test`,
19 expiresAt: overrides.expiresAt ?? `2099-01-${String((index % 9) + 1).padStart(2, "0")}T00:00:00.000Z`,
20 token: overrides.token ?? `tok_${index}`,
21 ttlHours: overrides.ttlHours ?? 24,
22 };
23}
24 
25describe("client inbox session storage", () => {
26 it("stores a new inbox session and returns it on lookup", () => {
27 const session = makeSession(1);
28 
29 const stored = storeInboxSession(session);
30 
31 expect(stored).toEqual([
32 {
33 address: session.address,
34 expiresAt: session.expiresAt,
35 ttlHours: session.ttlHours,
36 },
37 ]);
38 expect(getInboxSession(session.address)).toEqual(session);
39 });
40 
41 it("keeps only the most recent inbox sessions", () => {
42 for (let index = 0; index < 10; index += 1) {
43 storeInboxSession(makeSession(index));
44 }
45 
46 const sessions = loadInboxSessions();
47 
48 expect(sessions).toHaveLength(8);
49 expect(sessions[0]?.address).toBe("user9@mail.test");
50 expect(sessions[7]?.address).toBe("user2@mail.test");
51 expect(sessions.some((session) => session.address === "user1@mail.test")).toBe(false);
52 expect(getInboxSession("user1@mail.test")).toBeNull();
53 });
54 
55 it("prunes expired sessions and removes their tokens and bookmarks on read", () => {
56 const expired = makeSession(1, {
57 address: "expired@mail.test",
58 expiresAt: "2020-01-01T00:00:00.000Z",
59 });
60 const active = makeSession(2, {
61 address: "active@mail.test",
62 });
63 
64 storeInboxSession(expired);
65 setStoredBookmark("inbox:expired@mail.test", "bookmark-expired");
66 storeInboxSession(active);
67 setStoredBookmark("inbox:active@mail.test", "bookmark-active");
68 
69 const sessions = loadInboxSessions();
70 
71 expect(sessions).toEqual([
72 {
73 address: active.address,
74 expiresAt: active.expiresAt,
75 ttlHours: active.ttlHours,
76 },
77 ]);
78 expect(localStorage.getItem(`${INBOX_TOKEN_KEY_PREFIX}:expired@mail.test`)).toBeNull();
79 expect(getStoredBookmark("inbox:expired@mail.test")).toBeNull();
80 expect(getStoredBookmark("inbox:active@mail.test")).toBe("bookmark-active");
81 });
82 
83 it("migrates legacy sessionStorage inbox tokens into localStorage", () => {
84 const session = makeSession(1, {
85 address: "legacy@mail.test",
86 });
87 localStorage.setItem(
88 INBOX_SESSIONS_KEY,
89 JSON.stringify([
90 {
91 address: session.address,
92 expiresAt: session.expiresAt,
93 ttlHours: session.ttlHours,
94 },
95 ]),
96 );
97 sessionStorage.setItem(`${INBOX_TOKEN_KEY_PREFIX}:${session.address}`, session.token);
98 
99 const restored = getInboxSession(session.address);
100 
101 expect(restored).toEqual(session);
102 expect(localStorage.getItem(`${INBOX_TOKEN_KEY_PREFIX}:${session.address}`)).toBe(session.token);
103 expect(sessionStorage.getItem(`${INBOX_TOKEN_KEY_PREFIX}:${session.address}`)).toBeNull();
104 });
105 
106 it("returns null when a summary exists but the token is missing", () => {
107 const session = makeSession(1, {
108 address: "missing-token@mail.test",
109 });
110 localStorage.setItem(
111 INBOX_SESSIONS_KEY,
112 JSON.stringify([
113 {
114 address: session.address,
115 expiresAt: session.expiresAt,
116 ttlHours: session.ttlHours,
117 },
118 ]),
119 );
120 
121 expect(getInboxSession(session.address)).toBeNull();
122 });
123 
124 it("updates ttl, expiry, and token on session update", () => {
125 const session = makeSession(1, {
126 address: "reader@mail.test",
127 ttlHours: 24,
128 });
129 storeInboxSession(session);
130 
131 const updated = updateInboxSession(session.address, {
132 expiresAt: "2099-02-01T00:00:00.000Z",
133 ttlHours: 72,
134 token: "tok_updated",
135 });
136 
137 expect(updated).toEqual([
138 {
139 address: session.address,
140 expiresAt: "2099-02-01T00:00:00.000Z",
141 ttlHours: 72,
142 },
143 ]);
144 expect(getInboxSession(session.address)).toEqual({
145 address: session.address,
146 expiresAt: "2099-02-01T00:00:00.000Z",
147 token: "tok_updated",
148 ttlHours: 72,
149 });
150 });
151 
152 it("removes inbox sessions and clears related token and bookmark state", () => {
153 const session = makeSession(1, {
154 address: "reader@mail.test",
155 });
156 storeInboxSession(session);
157 setStoredBookmark("inbox:reader@mail.test", "bookmark-reader");
158 
159 const remaining = removeInboxSession(session.address);
160 
161 expect(remaining).toEqual([]);
162 expect(localStorage.getItem(`${INBOX_TOKEN_KEY_PREFIX}:${session.address}`)).toBeNull();
163 expect(getStoredBookmark("inbox:reader@mail.test")).toBeNull();
164 });
165 
166 it("removes expired sessions when an update makes them expire immediately", () => {
167 const session = makeSession(1, {
168 address: "reader@mail.test",
169 });
170 storeInboxSession(session);
171 
172 const remaining = updateInboxSession(session.address, {
173 expiresAt: "2020-01-01T00:00:00.000Z",
174 });
175 
176 expect(remaining).toEqual([]);
177 expect(getInboxSession(session.address)).toBeNull();
178 });
179 
180 it("clears the admin bookmark scope on logout", () => {
181 setStoredBookmark(ADMIN_BOOKMARK_SCOPE, "bookmark-admin");
182 
183 clearAdminBookmark();
184 
185 expect(getStoredBookmark(ADMIN_BOOKMARK_SCOPE)).toBeNull();
186 });
187});