mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-22 21:00:51 +02:00
v1.87.5.0 perf: remove idle waits from tests and CI planning (#2897)
* v1.87.5.0 perf: remove idle waits from tests and CI planning * fix: settle split PTY redraws before routing input * docs: record final burst-safe test benchmarks * fix: keep cold-setup snapshot metadata dependency-free * fix: avoid early-reader pipe races in artifact URL parsing * fix: preserve safety matches for multiline command payloads * fix: recognize concurrent CSO publication removal * test: preload the UI design-review target before invocation * docs: record validation blocker fixes * fix: bind plan observer rejection to the invoked command * fix: count only native design decisions in the UI gate * docs: clarify UI-positive eval evidence requirements * test: recognize native UI decisions without weakening finding counts * test: decouple native UI evidence from question punctuation * test: recognize concrete native UI decisions independently of prose format * fix: retain failed eval logs under the hidden CI cache * test: await telemetry completion instead of racing disk writes
This commit is contained in:
@@ -114,10 +114,11 @@ describe('bun-polyfill', () => {
|
||||
const p = Bun.spawn(['this-binary-does-not-exist-zzz-' + Date.now()], {
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
});
|
||||
let deadline;
|
||||
const code = await Promise.race([
|
||||
p.exited,
|
||||
new Promise((_, r) => setTimeout(() => r(new Error('timeout')), 3000))
|
||||
]).catch(() => 'TIMEOUT');
|
||||
new Promise((_, r) => { deadline = setTimeout(() => r(new Error('timeout')), 3000); })
|
||||
]).catch(() => 'TIMEOUT').finally(() => clearTimeout(deadline));
|
||||
console.log('exit:' + code);
|
||||
})();
|
||||
`], { stdout: 'pipe', stderr: 'pipe', timeout: 30_000 });
|
||||
@@ -159,10 +160,11 @@ describe('bun-polyfill', () => {
|
||||
['node', '-e', 'process.stdout.write("y".repeat(10 * 1024)); process.exit(0)'],
|
||||
{ stdio: ['ignore', 'pipe', 'ignore'] }
|
||||
);
|
||||
let deadline;
|
||||
const code = await Promise.race([
|
||||
p.exited,
|
||||
new Promise((_, r) => setTimeout(() => r(new Error('timeout')), 3000))
|
||||
]).catch(() => 'TIMEOUT');
|
||||
new Promise((_, r) => { deadline = setTimeout(() => r(new Error('timeout')), 3000); })
|
||||
]).catch(() => 'TIMEOUT').finally(() => clearTimeout(deadline));
|
||||
const out = await new Response(p.stdout).text();
|
||||
console.log(out.length + ':' + code);
|
||||
})();
|
||||
@@ -190,10 +192,11 @@ describe('bun-polyfill', () => {
|
||||
['node', '-e', 'process.stdout.write("x".repeat(' + ONE_MB + '), () => process.exit(0))'],
|
||||
{ stdio: ['ignore', 'pipe', 'ignore'] }
|
||||
);
|
||||
let deadline;
|
||||
const code = await Promise.race([
|
||||
p.exited,
|
||||
new Promise((_, r) => setTimeout(() => r(new Error('timeout')), 10000))
|
||||
]).catch(e => 'TIMEOUT');
|
||||
new Promise((_, r) => { deadline = setTimeout(() => r(new Error('timeout')), 10000); })
|
||||
]).catch(e => 'TIMEOUT').finally(() => clearTimeout(deadline));
|
||||
const out = await new Response(p.stdout).text();
|
||||
console.log(out.length + ':' + code);
|
||||
})().catch((e) => { console.log('THREW:' + e.message); });
|
||||
|
||||
@@ -6,9 +6,19 @@
|
||||
* CORS headers, and JSON response formats.
|
||||
*/
|
||||
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import { afterAll, describe, test, expect } from 'bun:test';
|
||||
import { handleCookiePickerRoute, generatePickerCode, hasActivePicker } from '../src/cookie-picker-routes';
|
||||
|
||||
afterAll(() => {
|
||||
const realNow = Date.now;
|
||||
Date.now = () => realNow() + 3_700_000;
|
||||
try {
|
||||
expect(hasActivePicker()).toBe(false);
|
||||
} finally {
|
||||
Date.now = realNow;
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Mock BrowserManager ──────────────────────────────────────
|
||||
|
||||
function mockBrowserManager() {
|
||||
|
||||
@@ -151,23 +151,19 @@ describe('telemetry env tier + cache semantics', () => {
|
||||
describe('enforcement: logTelemetry writes only with granted consent', () => {
|
||||
test('config-tier opt-out suppresses the JSONL append', async () => {
|
||||
const dir = tmpHomeWith('telemetry: off\n');
|
||||
logTelemetry({ event: 'domain_skill_fired', host: 'example.com' });
|
||||
// Fire-and-forget path: give any (incorrect) async append time to land.
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await logTelemetry({ event: 'domain_skill_fired', host: 'example.com' });
|
||||
expect(fs.existsSync(path.join(dir, 'analytics', 'browse-telemetry.jsonl'))).toBe(false);
|
||||
});
|
||||
|
||||
test('no consent ever recorded (absent key) suppresses the JSONL append', async () => {
|
||||
const dir = tmpHomeWith('pair_agent: on\n');
|
||||
logTelemetry({ event: 'domain_skill_fired', host: 'example.com' });
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await logTelemetry({ event: 'domain_skill_fired', host: 'example.com' });
|
||||
expect(fs.existsSync(path.join(dir, 'analytics', 'browse-telemetry.jsonl'))).toBe(false);
|
||||
});
|
||||
|
||||
test('granted `community` tier appends the event', async () => {
|
||||
const dir = tmpHomeWith('telemetry: community\n');
|
||||
logTelemetry({ event: 'domain_skill_fired', host: 'example.com' });
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await logTelemetry({ event: 'domain_skill_fired', host: 'example.com' });
|
||||
const file = path.join(dir, 'analytics', 'browse-telemetry.jsonl');
|
||||
expect(fs.existsSync(file)).toBe(true);
|
||||
expect(fs.readFileSync(file, 'utf-8')).toContain('domain_skill_fired');
|
||||
|
||||
@@ -33,8 +33,6 @@ afterAll(async () => {
|
||||
});
|
||||
|
||||
async function readEvents(): Promise<any[]> {
|
||||
// Wait briefly for fire-and-forget appends to flush.
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
try {
|
||||
const raw = await fs.readFile(TELEMETRY_FILE, 'utf8');
|
||||
return raw.trim().split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
||||
@@ -47,7 +45,7 @@ describe('telemetry: signals fire to ~/.gstack/analytics/browse-telemetry.jsonl'
|
||||
it('logTelemetry writes a JSONL line with ts injected', async () => {
|
||||
const { logTelemetry, _resetTelemetryCache } = await import('../src/telemetry');
|
||||
_resetTelemetryCache();
|
||||
logTelemetry({ event: 'domain_skill_saved', host: 'test.com', scope: 'project', state: 'quarantined', bytes: 42 });
|
||||
await logTelemetry({ event: 'domain_skill_saved', host: 'test.com', scope: 'project', state: 'quarantined', bytes: 42 });
|
||||
const events = await readEvents();
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0].event).toBe('domain_skill_saved');
|
||||
@@ -60,7 +58,7 @@ describe('telemetry: signals fire to ~/.gstack/analytics/browse-telemetry.jsonl'
|
||||
process.env.GSTACK_TELEMETRY_OFF = '1';
|
||||
const { logTelemetry, _resetTelemetryCache } = await import('../src/telemetry');
|
||||
_resetTelemetryCache();
|
||||
logTelemetry({ event: 'cdp_method_called', domain: 'X', method: 'y' });
|
||||
await logTelemetry({ event: 'cdp_method_called', domain: 'X', method: 'y' });
|
||||
const events = await readEvents();
|
||||
expect(events).toHaveLength(0);
|
||||
process.env.GSTACK_TELEMETRY_OFF = '0';
|
||||
@@ -72,6 +70,8 @@ describe('telemetry: signals fire to ~/.gstack/analytics/browse-telemetry.jsonl'
|
||||
// logTelemetry on a missing directory doesn't throw.
|
||||
const { logTelemetry, _resetTelemetryCache } = await import('../src/telemetry');
|
||||
_resetTelemetryCache();
|
||||
expect(() => logTelemetry({ event: 'noop_test' })).not.toThrow();
|
||||
let completed: Promise<void> | undefined;
|
||||
expect(() => { completed = logTelemetry({ event: 'noop_test' }); }).not.toThrow();
|
||||
await completed;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,8 +43,7 @@ afterEach(async () => {
|
||||
// Kill any survivors so subsequent tests get a clean slate.
|
||||
try { parentProc?.kill('SIGKILL'); } catch {}
|
||||
try { serverProc?.kill('SIGKILL'); } catch {}
|
||||
// Give processes a moment to exit before tmpDir cleanup.
|
||||
await Bun.sleep(100);
|
||||
await Promise.all([parentProc?.exited, serverProc?.exited]);
|
||||
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
||||
parentProc = null;
|
||||
serverProc = null;
|
||||
@@ -316,7 +315,13 @@ describe('suppressed watchdog still reaps tunnel orphans (behavioral)', () => {
|
||||
});
|
||||
|
||||
test('CRITICAL: suppression active + tunnel live — parent death still shuts down', async () => {
|
||||
const exitMock = mock((_code?: number) => {});
|
||||
let resolveExit!: () => void;
|
||||
let exitDeadline!: ReturnType<typeof setTimeout>;
|
||||
const exited = new Promise<void>((resolve, reject) => {
|
||||
resolveExit = resolve;
|
||||
exitDeadline = setTimeout(() => reject(new Error('Watchdog shutdown did not exit within 3s')), 3_000);
|
||||
});
|
||||
const exitMock = mock((_code?: number) => { resolveExit(); });
|
||||
const originalExit = process.exit;
|
||||
(process as any).exit = exitMock;
|
||||
try {
|
||||
@@ -324,12 +329,13 @@ describe('suppressed watchdog still reaps tunnel orphans (behavioral)', () => {
|
||||
__testInternals__.suppressHeadedParentShutdown();
|
||||
__testInternals__.setTunnelActive(true); // handoff → resume → /pair-agent tunnel
|
||||
__testInternals__.parentWatchdogTick(DEAD_PID);
|
||||
await drainShutdown();
|
||||
await exited;
|
||||
// The tick is the ONLY reaper for tunnel orphans (idle timeout is
|
||||
// disabled in tunnel mode). If this fails, an internet-exposed daemon
|
||||
// outlives its parent forever.
|
||||
expect(exitMock).toHaveBeenCalled();
|
||||
} finally {
|
||||
clearTimeout(exitDeadline);
|
||||
(process as any).exit = originalExit;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user