Files
callstack__agent-device/test/integration/provider-scenarios/transcript.ts
Michał Pierzchała 59d28e8446 refactor: add provider-first device lab tests (#542)
* refactor: add provider-first device lab tests

* refactor: tighten device lab provider seams

* test: cover provider lab contracts

* docs: record device lab harness direction

* ci: run device lab integration tests

* test: move device lab under integration

* test: extract device lab helpers

* refactor: centralize apps filter defaults

* test: drop lab-covered unit tests

* test: fold platform happy paths into device lab

* test: reuse device lab helpers

* test: move device lab to in-process harness

* test: replace session handler cases with device lab

* test: harden device lab scenario contracts

* docs: define unit test retention policy

* test: expand provider device lab coverage

* test: harden provider device lab coverage

* test: cover manifest install and runner session contracts

* chore: remove unused provider cleanup code

* test: split android find device lab scenario

* test: track provider lab architecture progress

* test: clarify provider lab roadmap progress

* test: advance provider lab session coverage

* test: move menubar click routing to device lab

* test: move menubar snapshots to device lab

* refactor: centralize screenshot flag plumbing

* refactor: colocate screenshot flag metadata

* test: cover all public commands in device lab

* test: move macos wait success to device lab

* test: drop redundant perf and diff units

* test: move push payload paths to device lab

* test: move network parsing to device lab

* test: move log cleanup to device lab

* test: move log restart and boot to device lab

* test: move ios physical boot to device lab

* test: cover perf startup in device lab

* test: extract android and ios device lab worlds

* test: trim device lab world surface

* test: split snapshot capture unit coverage

* test: deepen device lab coverage and trim handler units

* test: clean up device lab migration scaffolding

* test: report device lab public command coverage

* refactor: make Apple provider seams semantic

* refactor: tighten device inventory and Linux provider seams

* refactor: tighten request provider scoping

* refactor: add semantic macos host provider

* test: broaden device lab find coverage

* test: cover workflow flags in device lab

* refactor: promote linux input provider seam

* test: clarify device lab flag coverage

* test: classify snapshot force-full progress

* test: enforce device lab progress in ci

* test: stabilize device lab ci

* test: move packaged metro smoke to integration

* test: drop stale provider seam coverage

* test: harden provider scope regression coverage

* refactor: remove stale platform barrels

* refactor: keep linux clipboard and screenshots semantic

* refactor: move macos host tools behind provider

* fix: honor remote artifact output paths

* test: deepen runtime coverage for daemon and runner paths

* test: share loopback test helpers

* refactor: make daemon runtime importable

* fix: honor replay target metadata

* chore: tighten final device lab quality gates

* test: share device lab setup helpers

* test: remove generic apple lab fallback

* test: deduplicate device lab helpers

* chore: tighten fallow duplication signal

* refactor: share apple diagnostic helpers

* fix: detect active android ime during fill verification

* test: consolidate provider-backed integration suite

* ci: fix fallow and iOS smoke setup

* chore: consolidate cleanup after ci fixes

* test: split vitest unit and integration projects

* docs: mention MCP discovery metadata

* docs: add agent skills context pointers

* fix: close provider recording coverage gaps

* fix: restore mcp compatibility smoke

* test: cover provider edge regressions

* test: consolidate loopback helpers

* docs: remove stale provider routing reference

* fix: harden final provider review issues

* chore: defer mcp cleanup from provider refactor
2026-05-18 14:50:52 +02:00

133 lines
3.9 KiB
TypeScript

import assert from 'node:assert/strict';
import { isDeepStrictEqual } from 'node:util';
import type { Platform } from '../../../src/utils/device.ts';
export interface ProviderScenarioProviderScope {
deviceId?: string;
platform?: Platform;
}
export interface ProviderScenarioProviderEntry<
TResult = unknown,
> extends ProviderScenarioProviderScope {
command: string;
request?: unknown;
result?: TResult;
error?: Error | string;
}
export interface ProviderScenarioProviderCall<
TResult = unknown,
> extends ProviderScenarioProviderScope {
command: string;
request?: unknown;
result?: TResult;
}
export interface ProviderScenarioTranscript {
readonly calls: readonly ProviderScenarioProviderCall[];
readonly remaining: readonly ProviderScenarioProviderEntry[];
next<TResult = unknown>(
command: string,
request?: unknown,
scope?: ProviderScenarioProviderScope,
): TResult;
assertComplete(): void;
}
export function createProviderTranscript(
entries: readonly ProviderScenarioProviderEntry[],
options: { ordered?: boolean } = {},
): ProviderScenarioTranscript {
const pending = [...entries];
const calls: ProviderScenarioProviderCall[] = [];
return {
get calls() {
return [...calls];
},
get remaining() {
return [...pending];
},
next<TResult = unknown>(
command: string,
request?: unknown,
scope: ProviderScenarioProviderScope = {},
): TResult {
const entryIndex = options.ordered
? 0
: pending.findIndex((candidate) =>
providerEntryMatches(candidate, command, request, scope),
);
const entry = entryIndex >= 0 ? pending.splice(entryIndex, 1)[0] : undefined;
assert.ok(entry, `Unexpected provider call: ${formatCall(command, scope)}`);
assert.equal(command, entry.command, 'Provider command mismatch');
assertScope(scope, entry);
if (Object.hasOwn(entry, 'request')) {
assert.deepEqual(request, entry.request, 'Provider request mismatch');
}
const call = {
command,
request,
deviceId: scope.deviceId,
platform: scope.platform,
result: entry.result as TResult,
};
calls.push(call);
if (entry.error) {
throw entry.error instanceof Error ? entry.error : new Error(entry.error);
}
return entry.result as TResult;
},
assertComplete() {
assert.equal(
pending.length,
0,
`Unconsumed provider transcript entries: ${pending.map(formatEntry).join(', ')}`,
);
},
};
}
export function createOrderedProviderTranscript(
entries: readonly ProviderScenarioProviderEntry[],
): ProviderScenarioTranscript {
return createProviderTranscript(entries, { ordered: true });
}
function providerEntryMatches(
entry: ProviderScenarioProviderEntry,
command: string,
request: unknown,
scope: ProviderScenarioProviderScope,
): boolean {
if (entry.command !== command) return false;
if (entry.deviceId && entry.deviceId !== scope.deviceId) return false;
if (entry.platform && entry.platform !== scope.platform) return false;
return !Object.hasOwn(entry, 'request') || isDeepStrictEqual(request, entry.request);
}
function assertScope(
actual: ProviderScenarioProviderScope,
expected: ProviderScenarioProviderEntry,
): void {
if (expected.deviceId) {
assert.equal(actual.deviceId, expected.deviceId, 'Provider device id mismatch');
}
if (expected.platform) {
assert.equal(actual.platform, expected.platform, 'Provider platform mismatch');
}
}
function formatCall(command: string, scope: ProviderScenarioProviderScope): string {
return formatEntry({ command, ...scope });
}
function formatEntry(entry: { command: string; deviceId?: string; platform?: Platform }): string {
const scope = [entry.platform, entry.deviceId].filter(Boolean).join(':');
return scope ? `${scope}.${entry.command}` : entry.command;
}