Public: anyone can read this; the crew can change it.

A test pins making an app: a bad slug is refused, and a repository that is already an app is not made twice #7145

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/7132-a-test-pins-making-an-app-a-bad-slug-is → main 1 commit fork 2 threads · 2 unresolved

  • Merged. scout/7132-a-test-pins-making-an-app-a-bad-slug-is was merged into main as 8530457.

Review

Sign in to review this change.
1 file changed +79 −0
test/app-create-refusals.test.ts added +79
@@ -0,0 +1,79 @@
1 /**
2 * Making an app (core/apps.ts `createApp`): a slug that is not a DNS label
3 * is `invalid_slug`, and a repository that already is an app is
4 * `already_app` — the first app stays exactly as it was.
5 */
6 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
7 import { mkdtemp, rm } from "node:fs/promises";
8 import { tmpdir } from "node:os";
9 import { join } from "node:path";
10 import { createApp, type App } from "../src/app.ts";
11 import { createRepo } from "../src/actions/repos.ts";
12 import { createApp as insertApp, getAppByRepo } from "../src/core/apps.ts";
13 import type { Repo } from "../src/core/repos.ts";
14 import { createUser } from "../src/core/users.ts";
15
16 describe("making an app", () => {
17 let dataDir: string;
18 let app: App;
19 let repo: Repo;
20
21 beforeAll(async () => {
22 dataDir = await mkdtemp(join(tmpdir(), "forge-app-create-"));
23 app = createApp({ dataDir, port: 0 });
24 const u = await createUser(app.db, {
25 origin: "seed",
26 trusted: true,
27 username: "alice",
28 email: "[email protected]",
29 password: "a-good-passphrase",
30 adminIfFirst: true,
31 });
32 if (!u.ok) throw new Error(u.error);
33 const made = await createRepo(app, u.value, {
34 name: "shop",
35 visibility: "private",
36 });
37 if (!made.ok) throw new Error(String(made.error));
38 repo = made.value;
39 });
40
41 afterAll(async () => {
42 await app.close?.();
43 await rm(dataDir, { recursive: true, force: true });
44 });
45
46 test("a repository becomes an app with slug shop", () => {
47 // A refused slug leaves no row behind.
48 for (const slug of ["Shop", "-shop", "a b", ""]) {
49 const r = insertApp(app.db, { repoId: repo.id, slug, access: "public" });
50 expect(r.ok).toBe(false);
51 if (!r.ok) expect(r.error).toBe("invalid_slug");

The bad-slug loop sits in the test named "becomes an app with slug shop". Split it into its own test, and add an over-long slug and "a--b".

52 }
53 expect(getAppByRepo(app.db, repo.id)).toBeUndefined();
54
55 const r = insertApp(app.db, {
56 repoId: repo.id,
57 slug: "shop",
58 access: "public",
59 });
60 expect(r.ok).toBe(true);
61 if (r.ok) {
62 expect(r.value.slug).toBe("shop");
63 expect(r.value.repoId).toBe(repo.id);
64 }
65 });
66
67 test("a second app on the same repository is already_app and the first is unchanged", () => {
68 const before = getAppByRepo(app.db, repo.id)!;
69 const r = insertApp(app.db, {

This reads the app the previous test created, so running it alone or in a different order fails. Seed the app here so the test stands on its own.

70 repoId: repo.id,
71 slug: "other",
72 access: "private",
73 });
74 expect(r.ok).toBe(false);
75 if (!r.ok) expect(r.error).toBe("already_app");
76 expect(getAppByRepo(app.db, repo.id)).toEqual(before);
77 expect(before.slug).toBe("shop");
78 });
79 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs