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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@

- Only write high quality tests that verify where there could be potential bugs. Avoid testing trivial getters/setters, framework wiring, or other code that is unlikely to break.
- Pipe slow test output to a file, then read the file. Example: `pnpm exec turbo run test --filter=@bb/integration-tests --force > /tmp/test-out.txt 2>&1`.
- Package `vitest.config.ts` files build their `projects` with `sharedWorkerProjects` from `vitest.shared.ts`. It runs node-environment test files in shared workers (`isolate: false`) and gives a file its own worker when it runs in a DOM environment (`jsdom`) or when the file, or a test helper it imports, mutates worker-global state (`vi.mock`, `vi.stubGlobal`, `process.env`, `globalThis.*` assignments, `Object.defineProperty` on a global). Re-importing the module graph per file was 80–90% of the big suites' CPU. Restore what a test changes anyway; the scan is a safety net, not a license.

## GitHub Issues And Pull Requests

Expand Down
15 changes: 13 additions & 2 deletions apps/app/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import path from "path";
import { defineWorkspaceTestConfig } from "../../vitest.shared.js";
import {
defineWorkspaceTestConfig,
sharedWorkerProjects,
} from "../../vitest.shared.js";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { sharedUiEnvSeam } from "./vite-shared-ui-seam.js";
Expand All @@ -14,8 +17,16 @@ export default defineWorkspaceTestConfig({
test: {
silent: "passed-only",
environment: "node",
include: ["src/**/*.test.ts", "src/**/*.test.tsx"],
setupFiles: ["src/test/setup.ts"],
testTimeout: 15_000,
// Per-file module-graph import and setup were ~85% of this suite's CPU.
// Node-environment files that do not mock share a worker context; jsdom
// files keep their own worker (see vitest.shared.ts).
projects: sharedWorkerProjects({
pkgDir: __dirname,
aliases: { "@": path.resolve(__dirname, "./src") },
name: "@bb/app",
include: ["src/**/*.test.ts", "src/**/*.test.tsx"],
}),
},
});
12 changes: 9 additions & 3 deletions apps/cli/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { defineWorkspaceTestConfig } from "../../vitest.shared.js";
import {
defineWorkspaceTestConfig,
sharedWorkerProjects,
} from "../../vitest.shared.js";

export default defineWorkspaceTestConfig({
test: {
silent: "passed-only",
name: "@bb/cli",
exclude: ["dist/**", "node_modules/**"],
env: {
BB_SERVER_URL: "http://127.0.0.1:49161",
BB_HOST_DAEMON_PORT: "49162",
},
projects: sharedWorkerProjects({
pkgDir: __dirname,
name: "@bb/cli",
include: ["src/**/*.test.ts"],
}),
},
});
12 changes: 9 additions & 3 deletions apps/desktop/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { defineWorkspaceTestConfig } from "../../vitest.shared.js";
import {
defineWorkspaceTestConfig,
sharedWorkerProjects,
} from "../../vitest.shared.js";

export default defineWorkspaceTestConfig({
test: {
environment: "node",
include: ["test/**/*.test.ts"],
name: "@bb/desktop",
projects: sharedWorkerProjects({
pkgDir: __dirname,
name: "@bb/desktop",
include: ["test/**/*.test.ts"],
}),
},
});
13 changes: 9 additions & 4 deletions apps/host-daemon/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { defineWorkspaceTestConfig } from "../../vitest.shared.js";
import {
defineWorkspaceTestConfig,
sharedWorkerProjects,
} from "../../vitest.shared.js";

export default defineWorkspaceTestConfig({
test: {
silent: "passed-only",
name: "@bb/host-daemon",
include: ["src/**/*.test.ts", "test/**/*.test.ts"],
exclude: ["dist/**", "node_modules/**"],
env: {
BB_DATA_DIR: "/tmp/bb-host-daemon-test",
BB_SERVER_URL: "http://127.0.0.1:49161",
BB_HOST_DAEMON_PORT: "49162",
},
testTimeout: 15_000,
projects: sharedWorkerProjects({
pkgDir: __dirname,
name: "@bb/host-daemon",
include: ["src/**/*.test.ts", "test/**/*.test.ts"],
}),
},
});
12 changes: 10 additions & 2 deletions apps/mobile/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import path from "node:path";
import { defineWorkspaceTestConfig } from "../../vitest.shared.js";
import {
defineWorkspaceTestConfig,
sharedWorkerProjects,
} from "../../vitest.shared.js";

// Pure-logic tests only (node environment). Screen behavior is covered by
// Maestro flows under e2e/flows. Modules under test must not import
Expand All @@ -13,8 +16,13 @@ export default defineWorkspaceTestConfig({
test: {
silent: "passed-only",
environment: "node",
include: ["src/**/*.test.ts"],
passWithNoTests: true,
testTimeout: 15_000,
projects: sharedWorkerProjects({
pkgDir: __dirname,
aliases: { "@": path.resolve(__dirname, "./src") },
name: "@bb/mobile",
include: ["src/**/*.test.ts"],
}),
},
});
53 changes: 37 additions & 16 deletions apps/server/test/helpers/provider-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,34 +177,55 @@ export function stubHostArtifact(pluginId: string): PluginHostArtifactSnapshot {
};
}

const firstPartyBridgeArtifactBuilds = new Map<
string,
Promise<PluginHostArtifactSnapshot | null>
>();

async function buildFirstPartyBridgeArtifact(
pluginId: string,
): Promise<PluginHostArtifactSnapshot | null> {
const rootDir = pluginRootDir(pluginId);
// Pi has no `bb.host`: its bridge stays in the daemon bundle.
if (!(await hasHostEntry(rootDir))) {
return null;
}
const toolchain = await resolvePluginBuildToolchain(
join(tmpdir(), "bb-plugin-build-toolchain"),
);
const build = await buildPluginHost(rootDir, "0.0.0-test", toolchain);
const bytes = await readFile(build.jsPath);
return {
digest: build.artifactDigest,
byteLength: bytes.byteLength,
path: build.jsPath,
generation: `test-${pluginId}`,
};
}

/**
* Builds and records the first-party provider bridge artifacts, exactly as the
* plugin runtime does on load. Without this a graduated provider has no
* `bridgeLaunch`, so the daemon has no bridge for it at all — which is the
* whole point of the artifact route and therefore worth exercising rather
* than stubbing. Bridges are rebuilt from source so a stale `dist/` cannot
* make a test pass against yesterday's bridge.
* make a test pass against yesterday's bridge — once per worker process:
* the sources do not change during a run, and the ~0.6s esbuild pass was
* paid by every integration harness, one per test.
*/
export async function recordFirstPartyProviderBridgeArtifacts(
artifacts: PluginHostArtifactRegistry,
): Promise<void> {
const toolchain = await resolvePluginBuildToolchain(
join(tmpdir(), "bb-plugin-build-toolchain"),
);
for (const pluginId of FIRST_PARTY_PROVIDER_PLUGIN_IDS) {
const rootDir = pluginRootDir(pluginId);
// Pi has no `bb.host`: its bridge stays in the daemon bundle.
if (!(await hasHostEntry(rootDir))) {
continue;
let build = firstPartyBridgeArtifactBuilds.get(pluginId);
if (!build) {
build = buildFirstPartyBridgeArtifact(pluginId);
firstPartyBridgeArtifactBuilds.set(pluginId, build);
}
const snapshot = await build;
if (snapshot !== null) {
artifacts.set(pluginId, snapshot);
}
const build = await buildPluginHost(rootDir, "0.0.0-test", toolchain);
const bytes = await readFile(build.jsPath);
artifacts.set(pluginId, {
digest: build.artifactDigest,
byteLength: bytes.byteLength,
path: build.jsPath,
generation: `test-${pluginId}`,
});
}
}

Expand Down
20 changes: 18 additions & 2 deletions apps/server/test/helpers/test-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { serve } from "@hono/node-server";
import type { AddressInfo } from "node:net";
import type { DbConnection } from "@bb/db";
import { createConnection, type DbConnection } from "@bb/db";
import { defaultFeatureFlags, type HostType } from "@bb/domain";
import { initDb } from "../../src/db.js";
import { createApp } from "../../src/server.js";
Expand Down Expand Up @@ -119,6 +119,22 @@ export function createTestDaemonHostKey(
});
}

let migratedTemplate: Buffer | null = null;

/**
* A fresh in-memory database with every migration applied and the personal
* project seeded, exactly as `initDb` leaves it. The first call migrates for
* real and keeps the serialized image; every later call opens an independent
* copy of that image. Replaying the 100+ migrations was ~57ms of the ~61ms a
* harness cost, paid by nearly two thousand tests.
*/
export function createTestDb(): DbConnection {
if (migratedTemplate === null) {
migratedTemplate = initDb(":memory:").$client.serialize();
}
return createConnection(migratedTemplate);
}

export async function createTestAppHarness(
overrides: TestAppHarnessConfigOverrides = {},
): Promise<TestAppHarness> {
Expand All @@ -129,7 +145,7 @@ export async function createTestAppHarness(
...configOverrides
} = overrides;
const dataDir = await mkdtemp(join(tmpdir(), "bb-server-test-"));
const db = initDb(":memory:");
const db = createTestDb();
const hub = new NotificationHubImpl();
const watchInterests = new WatchInterestCoordinator({ db, hub });
const sharedPorts = new HostSharedPortCoordinator({ db, hub });
Expand Down
5 changes: 4 additions & 1 deletion apps/server/test/internal/internal-skill-trees.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mkdir, writeFile } from "node:fs/promises";
import { chmod, mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { readSkillTreeManifest } from "../../src/services/skills/injected-skills.js";
Expand All @@ -13,6 +13,9 @@ describe("internal skill tree routes", () => {
const rootPath = path.join(harness.config.dataDir, "tree-route-skill");
await mkdir(rootPath, { recursive: true });
await writeFile(path.join(rootPath, "SKILL.md"), "tree route bytes\n");
// The manifest reports the on-disk mode; pin it so the process umask
// cannot change the expected entry.
await chmod(path.join(rootPath, "SKILL.md"), 0o644);
const manifest = readSkillTreeManifest(rootPath);
harness.deps.skillTreeRegistry.register(manifest.treeHash, rootPath);

Expand Down
27 changes: 6 additions & 21 deletions apps/server/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import {
defineWorkspaceTestConfig,
findIsolationRequiringTests,
sharedWorkerProjects,
} from "../../vitest.shared.js";

const isolationTests = findIsolationRequiringTests(__dirname, ["src", "test"]);

export default defineWorkspaceTestConfig({
test: {
silent: "passed-only",
Expand All @@ -13,23 +11,10 @@ export default defineWorkspaceTestConfig({
BB_SERVER_PORT: "49161",
BB_HOST_DAEMON_PORT: "49162",
},
projects: [
{
extends: true,
test: {
name: "@bb/server",
include: ["src/**/*.test.ts", "test/**/*.test.ts"],
exclude: ["dist/**", "node_modules/**", ...isolationTests],
isolate: false,
},
},
{
extends: true,
test: {
name: "@bb/server:isolated",
include: isolationTests,
},
},
],
projects: sharedWorkerProjects({
pkgDir: __dirname,
name: "@bb/server",
include: ["src/**/*.test.ts", "test/**/*.test.ts"],
}),
},
});
15 changes: 9 additions & 6 deletions apps/web/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { defineWorkspaceTestConfig } from "../../vitest.shared.js";
import {
defineWorkspaceTestConfig,
sharedWorkerProjects,
} from "../../vitest.shared.js";

export default defineWorkspaceTestConfig({
// vite.config.ts injects this from the target deployment's APP_URL; tests
// load modules without that config, so they get an obviously-not-real origin.
define: { __SITE_ORIGIN__: JSON.stringify("https://web.test") },
test: {
silent: "passed-only",
name: "@bb/web",
include: ["src/**/*.test.ts"],
exclude: ["dist/**", "node_modules/**"],
projects: sharedWorkerProjects({
pkgDir: __dirname,
name: "@bb/web",
include: ["src/**/*.test.ts"],
}),
},
});
Loading
Loading