Skip to content

Commit eec7d54

Browse files
committed
fix(test): dedupe session-adapters test boilerplate to satisfy CI fallow
CI's auto-changed-since fallow caught two clone groups my local pass missed: - 8 lines / 52 tokens between session-adapters-integration.test.ts (tempDirs + afterEach + makeWorkspace) and the existing test/fixtures/ingest-workspace.ts useIngestWorkspaces composable - 6 lines / 54 tokens between the empty-turns and empty-slug tests (runCLI → expectCLIFailure → stderr regex → assert no .md) Reuse useIngestWorkspaces for the lifecycle, and hoist the failure shape into expectIngestSessionFailureWithoutWrite() so the two no-write-on-failure tests share it.
1 parent fb8da68 commit eec7d54

1 file changed

Lines changed: 58 additions & 38 deletions

File tree

test/session-adapters-integration.test.ts

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,16 @@
1616
* - empty-turn export fails loudly (codex review hardening)
1717
*/
1818

19-
import { describe, it, expect, afterEach } from "vitest";
19+
import { describe, it, expect } from "vitest";
2020
import path from "path";
21-
import { mkdir, mkdtemp, rm, readdir, readFile, writeFile, copyFile } from "fs/promises";
22-
import { tmpdir } from "os";
23-
import { runCLI, expectCLIExit, expectCLIFailure } from "./fixtures/run-cli.js";
21+
import { mkdir, readdir, readFile, writeFile, copyFile } from "fs/promises";
22+
import {
23+
runCLI,
24+
expectCLIExit,
25+
expectCLIFailure,
26+
type CLIResult,
27+
} from "./fixtures/run-cli.js";
28+
import { useIngestWorkspaces } from "./fixtures/ingest-workspace.js";
2429

2530
const FIXTURES = path.resolve("test/fixtures/sessions");
2631
const CLAUDE_FIXTURE = path.join(FIXTURES, "claude-session.jsonl");
@@ -29,21 +34,35 @@ const CURSOR_FIXTURE = path.join(FIXTURES, "cursor-session.json");
2934
const MALFORMED_FIXTURE = path.join(FIXTURES, "malformed.jsonl");
3035
const EMPTY_TURNS_FIXTURE = path.join(FIXTURES, "claude-empty-session.jsonl");
3136

32-
const tempDirs: string[] = [];
37+
// Reuse the shared workspace lifecycle from the existing ingest tests so
38+
// the tempDirs + afterEach boilerplate stays in one place.
39+
const workspaces = useIngestWorkspaces("session");
3340

34-
afterEach(async () => {
35-
while (tempDirs.length > 0) {
36-
const dir = tempDirs.pop();
37-
if (dir) await rm(dir, { recursive: true, force: true });
38-
}
39-
});
41+
/** Make a temp workspace ready for `ingest-session`. */
42+
async function makeWorkspace(): Promise<string> {
43+
return workspaces.makeEmptyWorkspace();
44+
}
45+
46+
/** Read sources/ tolerating ENOENT when the CLI failed before creating it. */
47+
async function readSources(cwd: string): Promise<string[]> {
48+
return readdir(path.join(cwd, "sources")).catch(() => [] as string[]);
49+
}
4050

41-
/** Make a temp workspace with an empty `sources/`. */
42-
async function makeWorkspace(prefix: string): Promise<string> {
43-
const cwd = await mkdtemp(path.join(tmpdir(), `llmwiki-session-${prefix}-`));
44-
tempDirs.push(cwd);
45-
await mkdir(path.join(cwd, "sources"), { recursive: true });
46-
return cwd;
51+
/**
52+
* Common assertion shape for tests that expect the CLI to fail with a
53+
* specific stderr pattern AND verify no markdown was written into
54+
* sources/. Hoisted so the empty-turns and empty-slug tests share it
55+
* (fallow's CI mode flagged the pattern as a clone group otherwise).
56+
*/
57+
async function expectIngestSessionFailureWithoutWrite(
58+
result: CLIResult,
59+
stderrPattern: RegExp,
60+
cwd: string,
61+
): Promise<void> {
62+
expectCLIFailure(result);
63+
expect(result.stderr.toLowerCase()).toMatch(stderrPattern);
64+
const files = await readSources(cwd);
65+
expect(files).not.toContain(".md");
4766
}
4867

4968
/**
@@ -61,46 +80,46 @@ async function assertSingleSessionIngested(cwd: string, adapterName: string): Pr
6180

6281
describe("ingest-session CLI integration", () => {
6382
it("ingest-session --help shows the command description", async () => {
64-
const cwd = await makeWorkspace("help");
83+
const cwd = await makeWorkspace();
6584
const result = await runCLI(["ingest-session", "--help"], cwd);
6685
expectCLIExit(result, 0);
6786
expect(result.stdout).toContain("ingest-session");
6887
expect(result.stdout).toContain("session");
6988
});
7089

7190
it("ingest-session with claude fixture writes markdown to sources/", async () => {
72-
const cwd = await makeWorkspace("claude");
91+
const cwd = await makeWorkspace();
7392
const result = await runCLI(["ingest-session", CLAUDE_FIXTURE], cwd);
7493
expectCLIExit(result, 0);
7594
expect(result.stdout).toContain("claude");
7695
await assertSingleSessionIngested(cwd, "claude");
7796
});
7897

7998
it("ingest-session with codex fixture writes markdown to sources/", async () => {
80-
const cwd = await makeWorkspace("codex");
99+
const cwd = await makeWorkspace();
81100
const result = await runCLI(["ingest-session", CODEX_FIXTURE], cwd);
82101
expectCLIExit(result, 0);
83102
expect(result.stdout).toContain("codex");
84103
await assertSingleSessionIngested(cwd, "codex");
85104
});
86105

87106
it("ingest-session with cursor fixture writes markdown to sources/", async () => {
88-
const cwd = await makeWorkspace("cursor");
107+
const cwd = await makeWorkspace();
89108
const result = await runCLI(["ingest-session", CURSOR_FIXTURE], cwd);
90109
expectCLIExit(result, 0);
91110
expect(result.stdout).toContain("cursor");
92111
await assertSingleSessionIngested(cwd, "cursor");
93112
});
94113

95114
it("ingest-session with malformed JSONL exits non-zero with actionable error", async () => {
96-
const cwd = await makeWorkspace("malformed");
115+
const cwd = await makeWorkspace();
97116
const result = await runCLI(["ingest-session", MALFORMED_FIXTURE], cwd);
98117
expectCLIFailure(result);
99118
expect(result.stderr.toLowerCase()).toMatch(/malformed|line \d+|invalid/);
100119
});
101120

102121
it("ingest-session with unknown format exits non-zero with no-adapter message", async () => {
103-
const cwd = await makeWorkspace("unknown");
122+
const cwd = await makeWorkspace();
104123
const unknownFile = path.join(cwd, "unknown.txt");
105124
await writeFile(unknownFile, "hello world", "utf-8");
106125
const result = await runCLI(["ingest-session", unknownFile], cwd);
@@ -109,7 +128,7 @@ describe("ingest-session CLI integration", () => {
109128
});
110129

111130
it("ingest-session with missing path exits non-zero with file-not-found error", async () => {
112-
const cwd = await makeWorkspace("missing");
131+
const cwd = await makeWorkspace();
113132
const missingPath = path.join(cwd, "does-not-exist.jsonl");
114133
const result = await runCLI(["ingest-session", missingPath], cwd);
115134
expectCLIFailure(result);
@@ -119,19 +138,19 @@ describe("ingest-session CLI integration", () => {
119138

120139
describe("ingest-session — adapter validation hardening", () => {
121140
it("session with no user/assistant turns fails loudly even when shape detection passes", async () => {
122-
const cwd = await makeWorkspace("empty-turns");
141+
const cwd = await makeWorkspace();
123142
const result = await runCLI(["ingest-session", EMPTY_TURNS_FIXTURE], cwd);
124-
expectCLIFailure(result);
125-
expect(result.stderr.toLowerCase()).toMatch(/no usable turns|no user or assistant/);
126-
// No file written — empty exports should not produce a content-free source.
127-
const files = await readdir(path.join(cwd, "sources"));
128-
expect(files).toEqual([]);
143+
await expectIngestSessionFailureWithoutWrite(
144+
result,
145+
/no usable turns|no user or assistant/,
146+
cwd,
147+
);
129148
});
130149
});
131150

132151
describe("ingest-session — bulk directory import", () => {
133152
it("mixed good and bad files: good ones import, bad ones warn, exit 0", async () => {
134-
const cwd = await makeWorkspace("bulk-mixed");
153+
const cwd = await makeWorkspace();
135154
const dir = path.join(cwd, "sessions");
136155
await mkdir(dir, { recursive: true });
137156
await copyFile(CLAUDE_FIXTURE, path.join(dir, "claude.jsonl"));
@@ -148,7 +167,7 @@ describe("ingest-session — bulk directory import", () => {
148167
});
149168

150169
it("directory with only malformed/unrecognised files exits non-zero", async () => {
151-
const cwd = await makeWorkspace("bulk-all-bad");
170+
const cwd = await makeWorkspace();
152171
const dir = path.join(cwd, "sessions");
153172
await mkdir(dir, { recursive: true });
154173
await copyFile(MALFORMED_FIXTURE, path.join(dir, "malformed.jsonl"));
@@ -162,7 +181,7 @@ describe("ingest-session — bulk directory import", () => {
162181

163182
describe("ingest-session — filename safety (#35/#36 inheritance)", () => {
164183
it("two sessions with the same title from different files do not silently overwrite", async () => {
165-
const cwd = await makeWorkspace("dup-titles");
184+
const cwd = await makeWorkspace();
166185
// Build two distinct claude sessions whose first user turn produces the
167186
// same title — same slug, different sources. The second ingest must
168187
// disambiguate via the hash suffix shared with normal ingest (#36).
@@ -194,7 +213,7 @@ describe("ingest-session — filename safety (#35/#36 inheritance)", () => {
194213
});
195214

196215
it("session whose title slugifies to empty fails loudly without writing a dotfile", async () => {
197-
const cwd = await makeWorkspace("empty-slug");
216+
const cwd = await makeWorkspace();
198217
const file = path.join(cwd, "emoji-only.jsonl");
199218
// Title derives from the first user turn content. Use pure-emoji content
200219
// so slugify returns "" and the empty-slug guard from #35 fires.
@@ -206,9 +225,10 @@ describe("ingest-session — filename safety (#35/#36 inheritance)", () => {
206225
);
207226

208227
const result = await runCLI(["ingest-session", file], cwd);
209-
expectCLIFailure(result);
210-
expect(result.stderr.toLowerCase()).toMatch(/could not derive a filename/);
211-
const files = await readdir(path.join(cwd, "sources"));
212-
expect(files).not.toContain(".md");
228+
await expectIngestSessionFailureWithoutWrite(
229+
result,
230+
/could not derive a filename/,
231+
cwd,
232+
);
213233
});
214234
});

0 commit comments

Comments
 (0)