Skip to content

Commit 24d8ad9

Browse files
mschmickingclaude
andcommitted
test: stop the watch debounce test asserting CI disk speed
CI failed on 'collapses rapid saves into a single push' with two pushes instead of one. Not a bug in the debounce: three awaited writes with a 120 ms window can legitimately straddle it on a loaded runner, so the test was asserting something about the machine rather than the code. It now uses synchronous writes and a 1000 ms window. The shared debounce also rises from 20 ms to 150 ms. chokidar sometimes emits add and change for a single write, and at 20 ms that pair can span the window and produce a second push — a rare failure that only appears under load. Costs about a second across the suite. One assertion changes from waiting for the count to equal 1 to waiting for at least 1 and checking the total after settling, so a double push fails with '2 !== 1' rather than an unexplained timeout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent dee20c8 commit 24d8ad9

1 file changed

Lines changed: 19 additions & 8 deletions

File tree

test/commands-watch.test.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
import { after, before, beforeEach, describe, it } from 'node:test';
1515
import assert from 'node:assert/strict';
16+
import { writeFileSync } from 'node:fs';
17+
import * as path from 'node:path';
1618
import { setTimeout as delay } from 'node:timers/promises';
1719

1820
import { FakeAdminServer } from './fake-server';
@@ -35,8 +37,12 @@ const SOURCE_A = "log('a');\n";
3537
const SOURCE_B = "log('b');\n";
3638
const SOURCE_C = "log('c');\n";
3739

38-
/** Short enough that tests do not spend 300 ms per edit. */
39-
const DEBOUNCE = 20;
40+
/**
41+
* Short enough to keep the suite quick, long enough to absorb chokidar emitting
42+
* more than one event for a single write. At 20 ms an add+change pair can straddle
43+
* the window and produce two pushes, which shows up as a rare CI-only failure.
44+
*/
45+
const DEBOUNCE = 150;
4046

4147
function script(source: string, overrides: Partial<ScriptObject['common']> = {}): ScriptObject {
4248
return {
@@ -133,7 +139,7 @@ describe('watch', () => {
133139
const handle = await watch(t.ctx, { pull: true, debounceMs: DEBOUNCE });
134140
try {
135141
await writeLocal(project, REL, SOURCE_B);
136-
await waitFor(() => pushLines(t.captured.result).length === 1, 'the push to complete');
142+
await waitFor(() => pushLines(t.captured.result).length >= 1, 'the push to complete');
137143

138144
// What the javascript adapter does after a push: same source, plus the
139145
// fields it manages itself.
@@ -166,14 +172,19 @@ describe('watch', () => {
166172
await writeLocal(project, REL, SOURCE_A);
167173
await writeManifest(project.root, [entryFor(ID, REL, 'TypeScript/ts', SOURCE_A)]);
168174

175+
// A generous window plus synchronous writes, so this asserts that the debounce
176+
// coalesces — not that the runner's disk is fast. With an async helper and a
177+
// 120 ms window, three awaited writes can straddle the window on a loaded CI
178+
// machine and legitimately produce two pushes.
169179
const t = await makeContext(port, project);
170-
const handle = await watch(t.ctx, { debounceMs: 120 });
180+
const handle = await watch(t.ctx, { debounceMs: 1000 });
171181
try {
172-
await writeLocal(project, REL, "log('1');\n");
173-
await writeLocal(project, REL, "log('2');\n");
174-
await writeLocal(project, REL, SOURCE_B);
182+
const target = path.join(project.scriptRoot, REL);
183+
writeFileSync(target, "log('1');\n", 'utf8');
184+
writeFileSync(target, "log('2');\n", 'utf8');
185+
writeFileSync(target, SOURCE_B, 'utf8');
175186

176-
await waitFor(() => pushLines(t.captured.result).length >= 1, 'the push to complete');
187+
await waitFor(() => pushLines(t.captured.result).length >= 1, 'the push to complete', 8000);
177188
await settle();
178189

179190
assert.equal(

0 commit comments

Comments
 (0)