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

A test pins an origin's labels: `importLabels` and `applyLabelNames` make each name once and attach exactly the remote set #6668

Merged opened this

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

Merged

scout/forge:scout/6656-a-test-pins-an-origin-s-labels-importlab → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/6656-a-test-pins-an-origin-s-labels-importlab was merged into main as 2b542d0.

Review

Sign in to review this change.
1 file changed +121 −0
test/origins-import-labels.test.ts added +121
@@ -0,0 +1,121 @@
1 import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2 import { mkdtemp, rm } from "node:fs/promises";
3 import { tmpdir } from "node:os";
4 import { join } from "node:path";
5 import { createApp, type App } from "../src/app.ts";
6 import { createRepo } from "../src/actions/repos.ts";
7 import { createIssue } from "../src/core/issues.ts";
8 import { applyLabelNames, importLabels } from "../src/origins/import.ts";
9 import { seedAdmin } from "./helpers/admin.ts";
10
11 /**
12 * Labels from an origin are matched by name (case-insensitively, as the
13 * module's queries do): a re-sync neither duplicates a label nor keeps one
14 * the origin took off an issue.
15 */
16
17 let dir: string;
18 let app: App;
19 let repoId: string;
20 let authorId: string;
21
22 const rows = () =>
23 app.db
24 .query<{ name: string }, [string]>(
25 "SELECT name FROM label WHERE repo_id = ?1 ORDER BY name",
26 )
27 .all(repoId)
28 .map((r) => r.name);
29
30 const attached = (issueId: string) =>
31 app.db
32 .query<{ name: string }, [string]>(
33 `SELECT l.name FROM issue_label il JOIN label l ON l.id = il.label_id
34 WHERE il.issue_id = ?1 ORDER BY l.name`,
35 )
36 .all(issueId)
37 .map((r) => r.name);
38
39 const newIssue = (title: string) => {
40 const made = createIssue(app.db, { repoId, authorId, title });
41 if (!made.ok) throw new Error(made.error);
42 return made.value.id;
43 };
44
45 beforeAll(async () => {
46 dir = await mkdtemp(join(tmpdir(), "forge-origin-labels-"));
47 app = createApp({ dataDir: dir, port: 0 });
48 const admin = await seedAdmin(app, {
49 username: "alice",
50 email: "[email protected]",
51 password: "alice-login-secret",
52 });
53 if (!admin.ok) throw new Error(admin.error);
54 authorId = admin.value.id;
55 const repo = await createRepo(app, admin.value, { name: "yard", visibility: "private" });
56 if (!repo.ok) throw new Error(String(repo.error));
57 repoId = repo.value.id;
58 });
59
60 afterAll(async () => {
61 await rm(dir, { recursive: true, force: true });
62 });
63
64 describe("importLabels", () => {
65 test("importing the same labels twice leaves one row per name", () => {
66 const remote = [
67 { remoteId: "1", name: "bug", color: "d73a4a" },
68 { remoteId: "2", name: "docs", color: "0075ca" },
69 ];
70 importLabels(app.db, repoId, remote);
71 importLabels(app.db, repoId, remote);
72 expect(rows()).toEqual(["bug", "docs"]);
73 });
74
75 test("a re-sync adopts the row by name, case-insensitively, and takes the new colour", () => {

Nit: this case relies on the bug/docs rows the previous test made. Seeding them here, or in a beforeEach, would let this case run alone, for example with bun test -t.

76 importLabels(app.db, repoId, [{ remoteId: "9", name: "BUG", color: "000000" }]);
77 expect(rows()).toEqual(["bug", "docs"]);
78 const bug = app.db
79 .query<{ color: string; origin_remote_id: string }, []>(
80 "SELECT color, origin_remote_id FROM label WHERE name = 'bug'",
81 )
82 .get();
83 expect(bug).toEqual({ color: "000000", origin_remote_id: "9" });
84 });
85
86 test("a name blank after trimming is skipped", () => {
87 const before = rows();
88 const out = importLabels(app.db, repoId, [
89 { remoteId: "7", name: "", color: "ffffff" },
90 { remoteId: "8", name: " \t ", color: "ffffff" },
91 ]);
92 expect(out.size).toBe(0);
93 expect(rows()).toEqual(before);
94 });
95 });
96
97 describe("applyLabelNames", () => {
98 test("[a, b] then [b] leaves the issue with only b", () => {
99 const issueId = newIssue("labelled");
100 applyLabelNames(app.db, repoId, issueId, ["a", "b"]);
101 expect(attached(issueId)).toEqual(["a", "b"]);
102 applyLabelNames(app.db, repoId, issueId, ["b"]);
103 expect(attached(issueId)).toEqual(["b"]);
104 });
105
106 test("each name is made once, however often it is applied", () => {
107 const issueId = newIssue("again");
108 applyLabelNames(app.db, repoId, issueId, ["fresh", "fresh"]);
109 applyLabelNames(app.db, repoId, issueId, ["fresh"]);
110 expect(rows().filter((n) => n === "fresh")).toHaveLength(1);
111 expect(attached(issueId)).toEqual(["fresh"]);
112 });
113
114 test("a name blank after trimming is skipped, and the rest are trimmed", () => {
115 const issueId = newIssue("blanks");
116 const before = rows().length;
117 applyLabelNames(app.db, repoId, issueId, ["", " ", " padded "]);
118 expect(attached(issueId)).toEqual(["padded"]);
119 expect(rows().length).toBe(before + 1);
120 });
121 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs