feat(desktop,server,web): app icons, completion notifications, single-user mode, bundled penguin CLI (#226)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createCompletionTracker } from "../src/lib/completion-notify";
|
||||
import type { ObservedSession } from "../src/lib/completion-notify";
|
||||
import type { SessionStatus } from "@prismshadow/penguin-server/api";
|
||||
|
||||
const row = (sessionId: string, status: SessionStatus): ObservedSession => ({
|
||||
sessionId,
|
||||
status,
|
||||
});
|
||||
|
||||
describe("createCompletionTracker", () => {
|
||||
it("fires when a known running session goes idle", () => {
|
||||
const t = createCompletionTracker();
|
||||
expect(t.observe([row("a", "running")], 0)).toEqual([]);
|
||||
expect(t.observe([row("a", "idle")], 1000)).toEqual(["a"]);
|
||||
});
|
||||
|
||||
it("treats compacting as still active and fires only on the flip to idle", () => {
|
||||
const t = createCompletionTracker();
|
||||
t.observe([row("a", "running")], 0);
|
||||
expect(t.observe([row("a", "compacting")], 100)).toEqual([]);
|
||||
expect(t.observe([row("a", "idle")], 200)).toEqual(["a"]);
|
||||
});
|
||||
|
||||
it("never fires on first observation, even an idle one after a running list load", () => {
|
||||
const t = createCompletionTracker();
|
||||
// A list that loads with idle sessions has witnessed nothing.
|
||||
expect(t.observe([row("a", "idle"), row("b", "idle")], 0)).toEqual([]);
|
||||
expect(t.observe([row("a", "idle"), row("b", "idle")], 100)).toEqual([]);
|
||||
});
|
||||
|
||||
it("dedupes per run: repeated idle observations fire once", () => {
|
||||
const t = createCompletionTracker();
|
||||
t.observe([row("a", "running")], 0);
|
||||
expect(t.observe([row("a", "idle")], 1000)).toEqual(["a"]);
|
||||
expect(t.observe([row("a", "idle")], 2000)).toEqual([]);
|
||||
expect(t.observe([row("a", "idle")], 60_000)).toEqual([]);
|
||||
});
|
||||
|
||||
it("fires again for a genuinely new run after the cooldown", () => {
|
||||
const t = createCompletionTracker(10_000);
|
||||
t.observe([row("a", "running")], 0);
|
||||
expect(t.observe([row("a", "idle")], 1000)).toEqual(["a"]);
|
||||
t.observe([row("a", "running")], 20_000);
|
||||
expect(t.observe([row("a", "idle")], 30_000)).toEqual(["a"]);
|
||||
});
|
||||
|
||||
it("suppresses a stale-snapshot flap within the cooldown window", () => {
|
||||
const t = createCompletionTracker(10_000);
|
||||
t.observe([row("a", "running")], 0);
|
||||
expect(t.observe([row("a", "idle")], 1000)).toEqual(["a"]);
|
||||
// A stale reload snapshot resurrects "running", then the fresh state lands again:
|
||||
// the same finish must not notify twice.
|
||||
t.observe([row("a", "running")], 1100);
|
||||
expect(t.observe([row("a", "idle")], 1200)).toEqual([]);
|
||||
});
|
||||
|
||||
it("tracks sessions independently and reports multiple completions", () => {
|
||||
const t = createCompletionTracker();
|
||||
t.observe([row("a", "running"), row("b", "running"), row("c", "idle")], 0);
|
||||
expect(t.observe([row("a", "idle"), row("b", "idle"), row("c", "idle")], 1000)).toEqual([
|
||||
"a",
|
||||
"b",
|
||||
]);
|
||||
});
|
||||
|
||||
it("prunes sessions that leave the list: reappearing is a fresh first observation", () => {
|
||||
const t = createCompletionTracker();
|
||||
t.observe([row("a", "running")], 0);
|
||||
// Project switch empties the list; the session's state is dropped.
|
||||
t.observe([], 100);
|
||||
expect(t.observe([row("a", "idle")], 200)).toEqual([]);
|
||||
});
|
||||
|
||||
it("ignores duplicate ids within one observation (first occurrence wins)", () => {
|
||||
const t = createCompletionTracker();
|
||||
t.observe([row("a", "running")], 0);
|
||||
expect(t.observe([row("a", "idle"), row("a", "running")], 1000)).toEqual(["a"]);
|
||||
// The duplicate "running" row did not restart the run.
|
||||
expect(t.observe([row("a", "idle")], 60_000)).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user