|
1 | 1 | import { afterEach, beforeEach, expect, test } from "bun:test"; |
2 | | -import { mkdir, rm, writeFile } from "node:fs/promises"; |
| 2 | +import { mkdir, rm, utimes, writeFile } from "node:fs/promises"; |
3 | 3 | import { join } from "node:path"; |
4 | 4 | import { tmpdir } from "node:os"; |
5 | 5 |
|
@@ -203,3 +203,60 @@ test("listSessions stays silent when many sibling runs failed with an error", as |
203 | 203 | expect(logged).not.toContain("unreadable session state"); |
204 | 204 | expect(logged).not.toContain(home); |
205 | 205 | }); |
| 206 | + |
| 207 | +async function writeRun( |
| 208 | + sessionId: string, |
| 209 | + body: { status: string; task: string; startedAt: number; turnsUsed?: number }, |
| 210 | +): Promise<string> { |
| 211 | + await initSessionDir(cwd, sessionId, home); |
| 212 | + const dir = sessionDir(cwd, sessionId, home); |
| 213 | + await writeFile(join(dir, "run.json"), JSON.stringify({ turnsUsed: 1, ...body })); |
| 214 | + return join(dir, "run.json"); |
| 215 | +} |
| 216 | + |
| 217 | +test("listSessions includes completed and failed sessions", async () => { |
| 218 | + const doneId = generateSessionId(); |
| 219 | + const failedId = generateSessionId(); |
| 220 | + await writeRun(doneId, { status: "done", task: "finished work", startedAt: 1 }); |
| 221 | + await writeRun(failedId, { status: "failed", task: "broke", startedAt: 2 }); |
| 222 | + const listed = await listSessions(cwd, home); |
| 223 | + expect(listed.find((s) => s.sessionId === doneId)?.status).toBe("done"); |
| 224 | + expect(listed.find((s) => s.sessionId === failedId)?.status).toBe("failed"); |
| 225 | +}); |
| 226 | + |
| 227 | +test("listSessions sorts by run.json mtime, not startedAt", async () => { |
| 228 | + const olderStart = generateSessionId(); |
| 229 | + const newerStart = generateSessionId(); |
| 230 | + const olderPath = await writeRun(olderStart, { |
| 231 | + status: "done", |
| 232 | + task: "started first, touched last", |
| 233 | + startedAt: 1_000, |
| 234 | + }); |
| 235 | + const newerPath = await writeRun(newerStart, { |
| 236 | + status: "running", |
| 237 | + task: "started later, stale", |
| 238 | + startedAt: 9_000, |
| 239 | + }); |
| 240 | + const now = Date.now(); |
| 241 | + await utimes(newerPath, now / 1000 - 60, now / 1000 - 60); |
| 242 | + await utimes(olderPath, now / 1000, now / 1000); |
| 243 | + const listed = await listSessions(cwd, home); |
| 244 | + expect(listed[0]?.sessionId).toBe(olderStart); |
| 245 | + expect(listed[1]?.sessionId).toBe(newerStart); |
| 246 | + expect(listed[0]?.updatedAt).toBeGreaterThan(listed[1]?.updatedAt ?? 0); |
| 247 | +}); |
| 248 | + |
| 249 | +test("listSessions reports updatedAt from run.json mtime", async () => { |
| 250 | + const sessionId = generateSessionId(); |
| 251 | + const path = await writeRun(sessionId, { |
| 252 | + status: "done", |
| 253 | + task: "mtime title", |
| 254 | + startedAt: 1, |
| 255 | + }); |
| 256 | + const stamp = Date.now() - 120_000; |
| 257 | + await utimes(path, stamp / 1000, stamp / 1000); |
| 258 | + const listed = await listSessions(cwd, home); |
| 259 | + const row = listed.find((s) => s.sessionId === sessionId); |
| 260 | + expect(row?.updatedAt).toBeGreaterThanOrEqual(stamp - 2000); |
| 261 | + expect(row?.updatedAt).toBeLessThanOrEqual(stamp + 2000); |
| 262 | +}); |
0 commit comments