mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
2d1d70613f
* feat(bench): renderer-pinned samples, topic-coverage gate, error quizzes; trim skillgym to agentic checks The help conformance bench's quoted CLI output is now sourced from scripts/help-conformance-sample-outputs.mjs, and every sample is rebuilt through the real production renderers (settle output formatters, printHumanError, formatSnapshotText, refMutationAdmissionResponse) by scripts/__tests__/help-conformance-sample-outputs.test.ts — a rendering or message change fails deterministically instead of leaving the bench grading against output the CLI no longer prints. This retires the fabricated recoverable-failure envelope (production never throws a textual settle timeout; that case is replaced by a real DEVICE_IN_USE recovery quiz). Bench cases move to scripts/help-conformance-cases.mjs and are enumerated against the help-topic registry: helpTopicIds() is exported from cli-help, and scripts/__tests__/help-conformance-topic-coverage.test.ts fails when a help topic has neither a bench case nor an explicit waiver. New case families: error-envelope recovery quizzes (device-in-use, stale pinned ref, ambiguous find match, app-not-installed) pinned to real error text, topic coverage for tv/web/react-native/debugging/workflow, and a metamorphic twin of the settled-diff quiz. The skillgym smoke suite shrinks from 119 cases to the 5 that measure what only an agentic runner can show: skill routing plus output interpretation with a proven local CLI help probe (local-cli-help-policy). Its embedded samples now import the same pinned constants, replacing hand-transcribed output that had already drifted from the renderer. Knowledge checks belong to the bench; live fixture behavior belongs to the iOS simulator e2e suite. * review: drive error samples through the real producers; enforce local-help on the routing smoke The DEVICE_IN_USE, AMBIGUOUS_MATCH, and APP_NOT_INSTALLED parity tests no longer hand-author the producer message before rendering: each drives the actual producer — buildDeviceInUseBySessionError (extracted in session-open.ts and called by the handler), buildAmbiguousMatchError (now exported from find.ts), and buildAppNotInstalledError (extracted in app-resolution.ts and thrown by the resolver). Because each factory is exported from its producer file and called by the production path, dropping the production call would make it test-only and fail check:production-exports — the wiring is gate-enforced, not conventional. open-and-snapshot now sets requireLocalCliHelp and allowOnlyLocalCliHelpCommands, so the 'skill plus local help' claim is observed rather than assumed; without them the case can pass on model prior alone.
125 lines
4.8 KiB
TypeScript
125 lines
4.8 KiB
TypeScript
import { defineConfig } from 'vitest/config';
|
|
import slowTestGateReporter from './scripts/vitest-slow-test-reporter.ts';
|
|
|
|
// Tests that stub a real binary (adb/xcrun/npx) by mutating process.env.PATH and
|
|
// then spawn it, so each case waits real subprocess/retry/poll time. Run at the
|
|
// unit suite's default ~7x file parallelism they contend for CPU and their stub
|
|
// spawns get starved past an internal budget, so production takes a generic
|
|
// failure path and returns a different error than the assertion expects — a
|
|
// contention flake whose failing subset shifts between runs (see
|
|
// docs/agents/testing.md "tests must not wait real time"). Serialized below with
|
|
// per-file isolation so only one such file spawns stubs at a time, the same
|
|
// execution contract the pre-split android index.test.ts aggregation provided.
|
|
const SUBPROCESS_STUB_TESTS = [
|
|
'src/platforms/android/__tests__/{app-lifecycle-install,app-lifecycle-open,device-input-state,input-actions,notifications,settings}.test.ts',
|
|
'src/daemon/__tests__/runtime-hints.test.ts',
|
|
'src/platforms/apple/core/__tests__/index.test.ts',
|
|
// Stubs npx + the package managers on PATH and spawns a real Metro dev server per case.
|
|
'src/__tests__/client-metro.test.ts',
|
|
];
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
// Wall-clock discipline: unit tests must not wait real time. Measured
|
|
// 2026-07-04: the suite's duration was bounded by files sleeping through
|
|
// production timeout budgets. slowTestThreshold surfaces creep in local
|
|
// output; the slow-test reporter enforces the ratchet (pinned offenders
|
|
// only shrink). Isolation stays ON and pool stays forks: measured
|
|
// --no-isolate = 205s wall vs 48s (module state thrashes across files),
|
|
// threads = no change.
|
|
slowTestThreshold: 500,
|
|
reporters: ['default', slowTestGateReporter()],
|
|
projects: [
|
|
{
|
|
test: {
|
|
name: 'unit-core',
|
|
// Explicit script entries keep maintained conformance guards in the
|
|
// unit suite without waking every ad-hoc *.test.ts under scripts/.
|
|
include: [
|
|
'src/**/*.test.ts',
|
|
'scripts/__tests__/help-conformance-bench.test.ts',
|
|
'scripts/__tests__/help-conformance-sample-outputs.test.ts',
|
|
'scripts/__tests__/help-conformance-topic-coverage.test.ts',
|
|
'test/skillgym/suites/local-cli-help-policy.test.ts',
|
|
// The Maestro conformance oracle runs via `node --test` in its own CI
|
|
// job (scripts/maestro-conformance), like the layering guard.
|
|
],
|
|
exclude: SUBPROCESS_STUB_TESTS,
|
|
setupFiles: [
|
|
'src/__tests__/hermetic-env-setup.ts',
|
|
'src/__tests__/process-memo-setup.ts',
|
|
],
|
|
},
|
|
},
|
|
{
|
|
// The subprocess-stub tests stub adb/xcrun/npx by mutating process.env
|
|
// (PATH, AGENT_DEVICE_TEST_ARGS_FILE) and wait real subprocess/retry/poll
|
|
// time, so the group runs serialized with per-file isolation — the same
|
|
// execution contract the pre-split android index.test.ts aggregation
|
|
// provided without leaking module caches between split files.
|
|
test: {
|
|
name: 'subprocess-stub',
|
|
include: SUBPROCESS_STUB_TESTS,
|
|
setupFiles: [
|
|
'src/__tests__/hermetic-env-setup.ts',
|
|
'src/__tests__/process-memo-setup.ts',
|
|
],
|
|
fileParallelism: false,
|
|
isolate: true,
|
|
maxWorkers: 1,
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: 'provider-integration',
|
|
include: ['test/integration/provider-scenarios/**/*.test.ts'],
|
|
setupFiles: [
|
|
'src/__tests__/hermetic-env-setup.ts',
|
|
'src/__tests__/process-memo-setup.ts',
|
|
],
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: 'interaction-contract',
|
|
include: ['test/integration/interaction-contract/**/*.test.ts'],
|
|
setupFiles: [
|
|
'src/__tests__/hermetic-env-setup.ts',
|
|
'src/__tests__/process-memo-setup.ts',
|
|
],
|
|
},
|
|
},
|
|
{
|
|
test: {
|
|
name: 'output-economy',
|
|
include: ['test/output-economy/**/*.test.ts'],
|
|
setupFiles: [
|
|
'src/__tests__/hermetic-env-setup.ts',
|
|
'src/__tests__/process-memo-setup.ts',
|
|
],
|
|
},
|
|
},
|
|
],
|
|
coverage: {
|
|
provider: 'v8',
|
|
reporter: ['text', 'html', 'lcov', 'json-summary'],
|
|
thresholds: {
|
|
statements: 78,
|
|
lines: 80,
|
|
},
|
|
include: ['src/**/*.ts'],
|
|
exclude: [
|
|
'src/**/*.test.ts',
|
|
'src/**/__tests__/**',
|
|
'src/**/*-types.ts',
|
|
'src/**/types.ts',
|
|
'src/sdk/**',
|
|
'src/bin.ts',
|
|
'src/client/client-types.ts',
|
|
'src/core/interactor-types.ts',
|
|
'src/remote/remote-config.ts',
|
|
],
|
|
},
|
|
},
|
|
});
|