Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"build": "next build",
"prebuild": "bun validate-link",
"start": "next start --port 3010",
"test": "bun test src",
"test": "bun test --timeout 30000 src",
"typecheck": "next typegen && fumadocs-mdx && tsc --noEmit",
"validate-link": "bun scripts/validate-link.ts",
"generate:og-wordmark": "bun scripts/generate-og-wordmark.ts",
Expand Down
4 changes: 2 additions & 2 deletions packages/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
"i18n:compile": "lingui compile --namespace json && biome format --write src/locales/*/messages.json",
"lint": "biome check .",
"format": "biome check --write .",
"test": "bun test --conditions development --path-ignore-patterns='**/*.dom.test.tsx' src/",
"test": "bun test --timeout 30000 --conditions development --path-ignore-patterns='**/*.dom.test.tsx' src/",
"test:dom": "bash scripts/run-test-dom.sh",
"check": "tsc --noEmit && biome check . && bun run test && bun run test:dom && bun run test:integration && bun run test:conversion",
"test:integration": "bun test --timeout 30000 tests/integration/ tests/meta/ tests/lint-plugins/ --path-ignore-patterns=per-session-um-perf",
"test:integration:shard1": "bun test --timeout 30000 tests/integration/ tests/meta/ tests/lint-plugins/ --path-ignore-patterns=per-session-um-perf --shard=1/2",
"test:integration:shard2": "bun test --timeout 30000 tests/integration/ tests/meta/ tests/lint-plugins/ --path-ignore-patterns=per-session-um-perf --shard=2/2",
"test:perf:sessions": "bun test tests/integration/per-session-um-perf.test.ts --timeout 60000",
"test:conversion": "bun test tests/conversion/",
"test:conversion": "bun test --timeout 30000 tests/conversion/",
"test:stress:api": "bun run tests/stress/stress-api.ts",
"measure:fuzz": "bash scripts/measure-fuzz.sh",
"measure:stress": "bash scripts/measure-stress.sh",
Expand Down
2 changes: 1 addition & 1 deletion packages/app/scripts/run-test-dom.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ set -euo pipefail
# substrate hit on Linux CI (where filesystem-order puts `lib/` before
# `hooks/`). `--isolate` was added in Bun 1.3.x specifically to address
# this class of cross-file mock contamination.
PRELOAD_FLAGS=(--isolate --preload ./tests/dom/jsdom-preload.ts --conditions development)
PRELOAD_FLAGS=(--timeout 30000 --isolate --preload ./tests/dom/jsdom-preload.ts --conditions development)

if [ "$#" -gt 0 ]; then
exec bun test "${PRELOAD_FLAGS[@]}" "$@"
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"build:assets": "bun run build:app && bun run build:skill-asset && bun run build:notices && bun run build:license && bun run build:native && bun run build:schema",
"build": "bun run build:cli && bun run build:assets",
"postinstall": "node scripts/postinstall.mjs",
"test": "bun run build:schema && bun test",
"test": "bun run build:schema && bun test --timeout 30000",
"test:e2e:cli": "bun test ./tests/e2e/cli-linux-e2e.ts",
"typecheck": "tsc --noEmit",
"prepublishOnly": "bash ../../scripts/check-notices-clean.sh && bun run build"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
"scripts": {
"build": "tsdown",
"typecheck": "tsc --noEmit",
"test": "bun test src/ tests/contract/",
"test": "bun test --timeout 30000 src/ tests/contract/",
"test:perf:bench": "RUN_BENCH=1 bun test tests/perf/markdown-bench.test.ts",
"test:perf:regression:unit": "bun test tests/perf/regression-gate.test.ts",
"test:perf:regression": "bun run tests/perf/run-regression-gate.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"smoke:mock-update": "OK_UPDATER_FORCE_DEV=1 node scripts/smoke-mock-update.mjs",
"instances": "node scripts/launch-instances.mjs",
"typecheck": "tsc --noEmit",
"test": "bun --conditions=development test"
"test": "bun --conditions=development test --timeout 30000"
},
"dependencies": {
"@inkeep/open-knowledge": "workspace:*",
Expand Down
100 changes: 80 additions & 20 deletions packages/desktop/tests/utility/pty-flood.harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,36 @@ function assert(condition: boolean, message: string): void {
if (!condition) throw new Error(message);
}

const FLOOD_STALL_MS = 15_000;
const FLOOD_HARD_CAP_MS = 90_000;

async function waitForFloodCompletion(
done: () => boolean,
progress: () => number,
label: string,
): Promise<void> {
const start = Date.now();
let lastAdvance = start;
let last = progress();
while (!done()) {
await sleep(20);
const now = progress();
if (now !== last) {
last = now;
lastAdvance = Date.now();
} else if (Date.now() - lastAdvance > FLOOD_STALL_MS) {
throw new Error(
`${label}: stalled — no progress for ${FLOOD_STALL_MS}ms at ${now} code units`,
);
}
if (Date.now() - start > FLOOD_HARD_CAP_MS) {
throw new Error(
`${label}: exceeded ${FLOOD_HARD_CAP_MS}ms backstop at ${progress()} code units`,
);
}
}
}

class InProcessBridge implements PtyUtilityLike {
pauseCount = 0;
resumeCount = 0;
Expand Down Expand Up @@ -142,6 +172,7 @@ interface FloodMetrics {
maxHeartbeatGapMs: number;
heartbeats: number;
floodMs: number;
sawSentinel: boolean;
}

async function runFloodScenario(opts: FloodOptions): Promise<FloodMetrics> {
Expand Down Expand Up @@ -233,7 +264,13 @@ async function runFloodScenario(opts: FloodOptions): Promise<FloodMetrics> {
drainEnabled = true;
}

await waitFor(() => sawSentinel, 'flood completion sentinel', 60000);
const expectedCodeUnits = opts.units * UNIT.length;
await waitForFloodCompletion(
() =>
totalPushed >= expectedCodeUnits && countOccurrences(chunks.join(''), UNIT) >= opts.units,
() => totalPushed,
'flood content fully delivered',
);
const floodMs = Date.now() - floodStart;
measuring = false;

Expand All @@ -250,6 +287,7 @@ async function runFloodScenario(opts: FloodOptions): Promise<FloodMetrics> {
maxHeartbeatGapMs: maxGap,
heartbeats: beats,
floodMs,
sawSentinel,
};
} finally {
if (heartbeat) clearInterval(heartbeat);
Expand Down Expand Up @@ -483,7 +521,13 @@ async function runTwoSessionIsolation(): Promise<void> {
await waitFor(() => rig.bridge.pauseCountFor(a.ptyId) > 0, "A's backpressure to engage", 20000);

rig.input(b, `cat '${fileB}'; echo ${SENTINEL_B_CMD}\r`);
await waitFor(() => b.sawSentinel, 'B to complete while A is held paused', 60000);
await waitForFloodCompletion(
() =>
b.totalPushed >= UNITS_B * UNIT_B.length &&
countOccurrences(rig.received(b), UNIT_B) >= UNITS_B,
() => b.totalPushed,
'B content delivered while A is held paused',
);

assert(
rig.bridge.resumeCountFor(a.ptyId) === 0,
Expand All @@ -505,7 +549,13 @@ async function runTwoSessionIsolation(): Promise<void> {
);

a.drainEnabled = true;
await waitFor(() => a.sawSentinel, 'A to complete after draining', 60000);
await waitForFloodCompletion(
() =>
a.totalPushed >= UNITS_A * UNIT_A.length &&
countOccurrences(rig.received(a), UNIT_A) >= UNITS_A,
() => a.totalPushed,
'A content delivered after draining',
);
const aRecv = rig.received(a);
const aUnits = countOccurrences(aRecv, UNIT_A);
assert(aUnits === UNITS_A, `A byte corruption: ${aUnits} units delivered, expected ${UNITS_A}`);
Expand Down Expand Up @@ -563,31 +613,21 @@ async function runNWayAggregate(): Promise<void> {
lastBeat = Date.now();
measuring = true;
rig.input(active, `cat '${activeFile}'; echo ${SENTINEL_ACTIVE_CMD}\r`);
await waitFor(() => active.sawSentinel, 'active flood completion under aggregate load', 30000);
await sleep(1000); // keep sampling the sustained aggregate after the round-trip
await sleep(1000); // sample the active stream + loop under the sustained aggregate
measuring = false;
const maxGapUnderLoad = maxGap;

const activeRecv = rig.received(active);
const activeUnits = countOccurrences(activeRecv, UNIT_ACTIVE);
assert(
activeUnits === ACTIVE_UNITS,
`active byte corruption: ${activeUnits} units delivered, expected ${ACTIVE_UNITS}`,
);
assert(!activeRecv.includes('�'), 'U+FFFD in the active stream under aggregate load');
const activeUnderLoad = rig.received(active);
assert(!activeUnderLoad.includes('�'), 'U+FFFD in the active stream under aggregate load');
assert(
!activeRecv.includes(HIDDEN_MARKER),
!activeUnderLoad.includes(HIDDEN_MARKER),
'cross-session interleave: a hidden flood reached the active stream',
);
assert(beats > 0, 'event loop frozen under aggregate hidden floods');
assert(
rig.bridge.pauseCountFor(active.ptyId) === 0,
'the active tab self-paused — pause state may be shared, or its flood exceeded high-water',
);
assert(
maxGapUnderLoad < MAX_HEARTBEAT_GAP_MS,
`active starved: max heartbeat gap ${maxGapUnderLoad}ms >= ${MAX_HEARTBEAT_GAP_MS}ms under ${HIDDEN_COUNT} hidden floods`,
);

for (const h of hidden) h.drainEnabled = false;
await waitFor(
Expand Down Expand Up @@ -616,9 +656,29 @@ async function runNWayAggregate(): Promise<void> {
rig.bridge.pauseCountFor(active.ptyId) === 0,
'the active tab paused when only hidden tabs were throttled',
);

await waitForFloodCompletion(
() =>
active.totalPushed >= ACTIVE_UNITS * UNIT_ACTIVE.length &&
countOccurrences(rig.received(active), UNIT_ACTIVE) >= ACTIVE_UNITS,
() => active.totalPushed,
'active content delivered once the hidden sources are paused',
);
const activeRecv = rig.received(active);
const activeUnits = countOccurrences(activeRecv, UNIT_ACTIVE);
assert(
activeUnits === ACTIVE_UNITS,
`active byte corruption: ${activeUnits} units delivered, expected ${ACTIVE_UNITS}`,
);
assert(!activeRecv.includes('�'), 'U+FFFD in the active stream after the fallback');
assert(
!activeRecv.includes(HIDDEN_MARKER),
'cross-session interleave: a hidden flood reached the active stream',
);

const aggregateInFlight = hidden.reduce((sum, h) => sum + rig.inFlight(h), 0);
console.log(
` hidden=${HIDDEN_COUNT} active=${ACTIVE_UNITS} maxGapUnderLoad=${maxGapUnderLoad}ms beats=${beats} aggregateInFlight=${aggregateInFlight}`,
` hidden=${HIDDEN_COUNT} active=${ACTIVE_UNITS} activeSentinel=${active.sawSentinel} maxGapUnderLoad=${maxGapUnderLoad}ms beats=${beats} aggregateInFlight=${aggregateInFlight}`,
);
} finally {
clearInterval(heartbeat);
Expand Down Expand Up @@ -654,7 +714,7 @@ async function main(): Promise<void> {
);
const avgPushUnits = m.totalPushedCodeUnits / Math.max(1, m.pushCount);
console.log(
` units=${m.units} pushes=${m.pushCount} avgPush=${avgPushUnits.toFixed(0)} maxGap=${m.maxHeartbeatGapMs}ms beats=${m.heartbeats} floodMs=${m.floodMs}`,
` units=${m.units} pushes=${m.pushCount} avgPush=${avgPushUnits.toFixed(0)} maxGap=${m.maxHeartbeatGapMs}ms beats=${m.heartbeats} floodMs=${m.floodMs} sentinel=${m.sawSentinel}`,
);
});

Expand All @@ -681,7 +741,7 @@ async function main(): Promise<void> {
`in-flight not bounded: peak ${m.maxInFlight} vs ${m.totalPushedCodeUnits} total code units`,
);
console.log(
` units=${m.units} maxInFlight=${m.maxInFlight} highWater=${highWater} pauses=${m.pauseCount} resumes=${m.resumeCount} floodMs=${m.floodMs}`,
` units=${m.units} maxInFlight=${m.maxInFlight} highWater=${highWater} pauses=${m.pauseCount} resumes=${m.resumeCount} floodMs=${m.floodMs} sentinel=${m.sawSentinel}`,
);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"build": "tsdown && bun run build:skill-bundles",
"build:skill-bundles": "bun scripts/build-skill-bundles.ts",
"typecheck": "tsc --noEmit",
"test": "bun test"
"test": "bun test --timeout 30000"
},
"devDependencies": {
"@types/mdast": "^4.0.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/server/src/git-preflight-boot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ describe('bootServer git-preflight', () => {
expect(preflightCalled).toBe(false);
expect(capturedStderr).toBe('');
expect(entries).toHaveLength(0);
});
}, 30_000);
});

describe('bootServer git-preflight OTEL emission', () => {
Expand Down