pike/glyph Public Public: anyone can read this; nobody can change it.

forked from apps/glyph

workbenchthis machine
Files 20b002cea95af30a05955adee40ddec8a6b7120a
102 lines · 4.4 KB · ~1.3k tok Raw History
1
import { expect, test } from "bun:test";
2
import { mkdtempSync } from "node:fs";
3
import { tmpdir } from "node:os";
4
import { join } from "node:path";
5
import {
6
  AT_ONCE,
7
  canDelete,
8
  deleteDrawing,
9
  getDrawing,
10
  listDrawings,
11
  startDrawing,
12
  svgsOf,
13
} from "./actions.ts";
14
import { openDb } from "./db.ts";
15
import { recover, setDrawer, settled } from "./draw.ts";
16
import type { Who } from "./who.ts";
17
18
const db = openDb(mkdtempSync(join(tmpdir(), "app-actions-")));
19
const anon: Who = { role: "anon" };
20
const alice: Who = { role: "user", id: "u1", name: "alice" };
21
const bob: Who = { role: "user", id: "u2", name: "bob" };
22
const root: Who = { role: "admin", id: "u0", name: "root" };
23
const SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><circle cx="32" cy="32" r="9"/></svg>`;
24
setDrawer(async () => [SVG]);
25
26
test("only a signed-in person draws, as themselves; the drawing fills in after", async () => {
27
  const refused = startDrawing(db, anon, { prompt: "hello" });
28
  expect(refused.ok).toBe(false);
29
  if (!refused.ok) expect(refused.error).toBe("signin_required");
30
  const started = startDrawing(db, alice, { prompt: "  a   fox  " });
31
  expect(started.ok).toBe(true);
32
  if (!started.ok) return;
33
  expect(started.value).toMatchObject({ prompt: "a fox", kind: "mark", status: "drawing", author_id: "u1" });
34
  await settled();
35
  const done = getDrawing(db, alice, { id: started.value.id });
36
  expect(done.ok && done.value.status).toBe("done");
37
  expect(done.ok && svgsOf(done.value)).toEqual([SVG]);
38
});
39
40
test("a prompt is 1 to 400 characters and a kind is one of four", () => {
41
  for (const input of [{ prompt: "" }, { prompt: "   " }, { prompt: "x".repeat(401) }, { prompt: "ok", kind: "poster" }]) {
42
    const r = startDrawing(db, alice, input);
43
    expect(r.ok).toBe(false);
44
    if (!r.ok) expect(r.error).toBe("invalid");
45
  }
46
  const icon = startDrawing(db, bob, { prompt: "ok", kind: "icon" });
47
  expect(icon.ok && icon.value.kind).toBe("icon");
48
});
49
50
test("drawings are their asker's: listed, read and removed by them or an admin", async () => {
51
  await settled();
52
  const mine = startDrawing(db, alice, { prompt: "mine" });
53
  if (!mine.ok) throw new Error(mine.message);
54
  await settled();
55
  const bobs = listDrawings(db, bob);
56
  expect(bobs.ok && bobs.value.every((d) => d.author_id === "u2")).toBe(true);
57
  expect(listDrawings(db, anon).ok).toBe(false);
58
  const all = listDrawings(db, root);
59
  expect(all.ok && all.value.some((d) => d.author_id === "u1") && all.value.some((d) => d.author_id === "u2")).toBe(true);
60
  const peek = getDrawing(db, bob, { id: mine.value.id });
61
  expect(!peek.ok && peek.error).toBe("forbidden");
62
  expect(canDelete(bob, mine.value)).toBe(false);
63
  expect(canDelete(alice, mine.value)).toBe(true);
64
  const byBob = deleteDrawing(db, bob, { id: mine.value.id });
65
  expect(!byBob.ok && byBob.error).toBe("forbidden");
66
  expect(deleteDrawing(db, root, { id: mine.value.id }).ok).toBe(true);
67
  const gone = deleteDrawing(db, alice, { id: mine.value.id });
68
  expect(!gone.ok && gone.error).toBe("not_found");
69
  const odd = deleteDrawing(db, root, { id: 1.5 });
70
  expect(!odd.ok && odd.error).toBe("invalid");
71
});
72
73
test("one drawing at a time per person, and a few at once for everyone", async () => {
74
  await settled();
75
  const waits: (() => void)[] = [];
76
  setDrawer(() => new Promise((resolve) => waits.push(() => resolve([SVG]))));
77
  try {
78
    expect(startDrawing(db, alice, { prompt: "one" }).ok).toBe(true);
79
    const again = startDrawing(db, alice, { prompt: "two" });
80
    expect(!again.ok && again.error).toBe("busy");
81
    for (let i = 1; i < AT_ONCE; i++)
82
      expect(startDrawing(db, { role: "user", id: `x${i}`, name: `x${i}` }, { prompt: "p" }).ok).toBe(true);
83
    const full = startDrawing(db, bob, { prompt: "three" });
84
    expect(!full.ok && full.message).toContain("others");
85
  } finally {
86
    for (const go of waits) go();
87
    await settled();
88
    setDrawer(async () => [SVG]);
89
  }
90
  expect(startDrawing(db, alice, { prompt: "after" }).ok).toBe(true);
91
  await settled();
92
});
93
94
test("a drawing a stopped process left half done is failed, not waited on forever", () => {
95
  const row = db
96
    .query<{ id: number }, []>("INSERT INTO drawing (prompt, kind, author_id, author_name) VALUES ('p', 'mark', 'u1', 'alice') RETURNING id")
97
    .get()!;
98
  expect(recover(db)).toBe(1);
99
  const after = getDrawing(db, alice, { id: row.id });
100
  expect(after.ok && after.value.status).toBe("failed");
101
  expect(after.ok && after.value.error).toContain("restarted");
102
});
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts