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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function makeCaptureFixtureStore<S>(resolveSessionDir: (address: string)
},
lookup: (address: string): FixtureSessionRef<S> => {
const entry = entries.get(address);
if (!entry) throw new AppError('COMMAND_FAILED', 'Test session retired');
if (!entry) throw new AppError('COMMAND_FAILED', 'Test session not found');
return Object.freeze({ address, session: entry.current, lifetime: entry });
},
resolveCurrent,
Expand Down
4 changes: 0 additions & 4 deletions packages/platform-android/src/recording/failed-finish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,6 @@ async function adoptAndroidRecording(params: {
const sessionsDir = mkdtempForTestSync('agent-device-android-failed-finish-session-');
let session: AndroidRecordingSession = {};
const sessionStore = {
get: () => session,
set: (_name: string, next: AndroidRecordingSession) => {
session = next;
},
resolveSessionDir: (name: string) => path.join(sessionsDir, name),
};
const binding = {
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/test-utils/store-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function makeStoredSessionRef(session: SessionState, address = session.na
return makeSessionStore().publish(address, session);
}

/** Publishes a fixture or reuses its current record. After an update, pass the rebuilt record. */
export function storeSessionForTest(
store: SessionStore,
session: SessionState,
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/app-log-session-resource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ function makeContext(
createdAt: Date.now(),
actions: [],
};
sessionStore.set(sessionName, session);
sessionStore.publish(sessionName, session);
const resourcePath = appLogResourceStore.resolvePath(sessionStore.resolveSessionDir(sessionName));
return {
admissionLedger: createAppLogAdmissionLedger(),
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/back-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ test('request router joins back admission to execution, recording, and ref inval
const sessionStore = makeSessionStore('agent-device-back-generic-');
const session = makeSession('back-runtime', { device: appleDevice });
activateCompleteRefFrame(session);
sessionStore.set(session.name, session);
sessionStore.publish(session.name, session);
const handler = createRequestHandler({
logPath: '/tmp/daemon.log',
token: 't',
Expand Down
12 changes: 6 additions & 6 deletions src/daemon/__tests__/capture-disclosure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ beforeEach(() => {
test('mutating find on a system-surface capture discloses the occlusion on the found outcome', async () => {
const sessionStore = makeSessionStore();
const session = makeAndroidSession('default');
sessionStore.set('default', session);
sessionStore.publish('default', session);

const response = await handleFindCommands({
req: {
Expand All @@ -102,7 +102,7 @@ test('mutating find on a system-surface capture discloses the occlusion on the f
test('read-only find exists on a system-surface capture discloses the occlusion', async () => {
const sessionStore = makeSessionStore();
const session = makeAndroidSession('default');
sessionStore.set('default', session);
sessionStore.publish('default', session);

const response = await dispatchFindReadOnlyViaRuntime({
req: {
Expand All @@ -128,7 +128,7 @@ test('read-only find exists on a system-surface capture discloses the occlusion'
test('wait timeout for app text hidden behind a system surface discloses the occlusion', async () => {
const sessionStore = makeSessionStore();
const session = makeAndroidSession('default');
sessionStore.set('default', session);
sessionStore.publish('default', session);

const response = await dispatchWaitViaRuntime({
req: {
Expand Down Expand Up @@ -304,7 +304,7 @@ function serveSheetCapture(): void {
test('mutating find on an in-place system surface discloses it on the found outcome', async () => {
serveSheetCapture();
const sessionStore = makeSessionStore();
sessionStore.set('default', makeIosSession('default', { appBundleId: 'com.example.app' }));
sessionStore.publish('default', makeIosSession('default', { appBundleId: 'com.example.app' }));

const response = await handleFindCommands({
req: {
Expand All @@ -331,7 +331,7 @@ test('mutating find on an in-place system surface discloses it on the found outc
test('mutating find that misses on an in-place system surface still discloses it', async () => {
serveSheetCapture();
const sessionStore = makeSessionStore();
sessionStore.set('default', makeIosSession('default', { appBundleId: 'com.example.app' }));
sessionStore.publish('default', makeIosSession('default', { appBundleId: 'com.example.app' }));

const response = await handleFindCommands({
req: {
Expand Down Expand Up @@ -378,7 +378,7 @@ test('the shared disclosure helper reports an iOS system surface on both outcome
*/
test('wait timeout whose polls required a foreground repair discloses the repair', async () => {
const sessionStore = makeSessionStore();
sessionStore.set('default', makeIosSession('default', { appBundleId: 'com.example.app' }));
sessionStore.publish('default', makeIosSession('default', { appBundleId: 'com.example.app' }));
legacyDispatchCapture.mockResolvedValue({
backend: 'xctest',
truncated: false,
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/daemon-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function makeHandler(
options: { inventory?: readonly DeviceInfo[]; providerInventory?: readonly DeviceInfo[] } = {},
) {
const sessionStore = makeSessionStore('agent-device-daemon-policy-');
sessionStore.set('default', makeIosSession('default', { appBundleId: 'com.example.app' }));
sessionStore.publish('default', makeIosSession('default', { appBundleId: 'com.example.app' }));
const bind = vi.fn(lifecycleDeviceRuntimeGateway.bind);
const inspectFacts = vi.fn(lifecycleDeviceRuntimeGateway.inspectFacts);
const handler = createRequestHandler({
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/daemon-runtime-app-log.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ test('daemon shutdown settles fenced app-log cleanup before finalization can rel
forceCleanup,
});
session.appLog = { handle, envelope };
sessionStore.set(session.name, session);
sessionStore.publish(session.name, session);
const resourcePath = appLogResourceStore.resolvePath(
sessionStore.resolveSessionDir(session.name),
);
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/focus-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ test('request router joins focus admission to execution, recording, and ref inva
const sessionStore = makeSessionStore('agent-device-focus-generic-');
const session = makeSession('focus-runtime', { device: appleDevice });
activateCompleteRefFrame(session);
sessionStore.set(session.name, session);
sessionStore.publish(session.name, session);
const handler = createRequestHandler({
logPath: '/tmp/daemon.log',
token: 't',
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/generic-settle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ function seedSession(
const session = makeIosSession(sessionName);
setSessionSnapshot(session, buildSnapshotState(snapshotPayload(baseline), {}));
activateCompleteRefFrame(session);
sessionStore.set(sessionName, session);
sessionStore.publish(sessionName, session);
return session;
}

Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/human-control-router-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function createHumanControlHarness() {
const lease = registry.allocateLease(HUMAN_CONTROL_LEASE_REQUEST);
const sessionStore = makeSessionStore('agent-device-human-control-');
const sessionName = tenantScopedSessionName(lease.tenantId, 'takeover-test');
sessionStore.set(
sessionStore.publish(
sessionName,
makeIosAppSession(sessionName, {
lease: buildSessionLeaseFromRequest(humanControlRequest(lease), lease),
Expand Down
29 changes: 16 additions & 13 deletions src/daemon/__tests__/is-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ function isRequest(
test('an admitted is inspects once, binds once, and answers through the bound capture', async () => {
const fixture = selectorCaptureFixture({ snapshot: () => buttonSnapshot() });
const sessionStore = makeSessionStore();
sessionStore.set('is-bound', makeAndroidSession('is-bound', { appBundleId: 'com.example.app' }));
sessionStore.publish(
'is-bound',
makeAndroidSession('is-bound', { appBundleId: 'com.example.app' }),
);

const response = await dispatchIsViaRuntime({
req: isRequest('is-bound', ['visible', 'id=auth_continue']),
Expand All @@ -95,7 +98,7 @@ test('is absent uses the bound readAny capture for selector-first input without
snapshot: () => ({ nodes: [], backend: 'xctest', producer: 'apple-runner' }),
});
const sessionStore = makeSessionStore();
sessionStore.set('is-absent', makeIosAppSession('is-absent'));
sessionStore.publish('is-absent', makeIosAppSession('is-absent'));

const response = await dispatchIsViaRuntime({
req: isRequest('is-absent', ['label="Removed row"', 'absent']),
Expand Down Expand Up @@ -133,7 +136,7 @@ test('is absent bypasses a cached no-match snapshot before evaluating the bound
}),
});
const sessionStore = makeSessionStore();
sessionStore.set(
sessionStore.publish(
'is-absent-fresh',
makeIosAppSession('is-absent-fresh', { snapshot: cachedAbsent }),
);
Expand Down Expand Up @@ -168,7 +171,7 @@ test('is absent fails closed for a quality-less legacy iOS root-only capture', a
}),
});
const sessionStore = makeSessionStore();
sessionStore.set('is-legacy-sparse', makeIosAppSession('is-legacy-sparse'));
sessionStore.publish('is-legacy-sparse', makeIosAppSession('is-legacy-sparse'));

const response = await dispatchIsViaRuntime({
req: isRequest('is-legacy-sparse', ['absent', 'label="Removed row"']),
Expand Down Expand Up @@ -200,7 +203,7 @@ test('is absent fails closed for a quality-less legacy iOS root-only capture', a
test('is absent rejects depth and scope before binding with typed invalid arguments', async () => {
const fixture = selectorCaptureFixture();
const sessionStore = makeSessionStore();
sessionStore.set('is-absent-flags', makeIosAppSession('is-absent-flags'));
sessionStore.publish('is-absent-flags', makeIosAppSession('is-absent-flags'));

for (const [flag, value] of [
['snapshotScope', 'Login'],
Expand Down Expand Up @@ -233,7 +236,7 @@ test('an unavailable capture fact refuses before any bind', async () => {
// The watchOS sentinel shape: capability-supported today, no snapshot backend at the owner.
const fixture = selectorCaptureFixture({ capture: unavailableCapture });
const sessionStore = makeSessionStore();
sessionStore.set('is-refused', makeAndroidSession('is-refused', { appBundleId: 'com.a' }));
sessionStore.publish('is-refused', makeAndroidSession('is-refused', { appBundleId: 'com.a' }));

const response = await dispatchIsViaRuntime({
req: isRequest('is-refused', ['visible', 'id=auth_continue']),
Expand Down Expand Up @@ -262,7 +265,7 @@ test('an iOS session with no tracked app is refused with the open hint, not answ
snapshot: () => buttonSnapshot(),
});
const sessionStore = makeSessionStore();
sessionStore.set('is-no-app', makeIosSession('is-no-app'));
sessionStore.publish('is-no-app', makeIosSession('is-no-app'));

const response = await withTestDeviceInventory(
{},
Expand Down Expand Up @@ -291,7 +294,7 @@ test('an iOS session WITH a tracked app still answers, so the refusal is the pla
snapshot: () => buttonSnapshot(),
});
const sessionStore = makeSessionStore();
sessionStore.set('is-with-app', makeIosAppSession('is-with-app'));
sessionStore.publish('is-with-app', makeIosAppSession('is-with-app'));

const response = await dispatchIsViaRuntime({
req: isRequest('is-with-app', ['visible', 'label=Continue']),
Expand All @@ -308,7 +311,7 @@ test('an iOS session WITH a tracked app still answers, so the refusal is the pla
test('an Android session with no tracked app proceeds, because the owner advertises the without-active-app capture', async () => {
const fixture = selectorCaptureFixture({ snapshot: () => buttonSnapshot() });
const sessionStore = makeSessionStore();
sessionStore.set('is-android-no-app', makeAndroidSession('is-android-no-app'));
sessionStore.publish('is-android-no-app', makeAndroidSession('is-android-no-app'));

const response = await dispatchIsViaRuntime({
req: isRequest('is-android-no-app', ['visible', 'id=auth_continue']),
Expand All @@ -329,7 +332,7 @@ test('an Android session with no tracked app proceeds, because the owner adverti
test('a refused request reaches the device by no route at all', async () => {
const fixture = selectorCaptureFixture({ capture: unavailableCapture });
const sessionStore = makeSessionStore();
sessionStore.set('is-direct-refused', makeIosAppSession('is-direct-refused'));
sessionStore.publish('is-direct-refused', makeIosAppSession('is-direct-refused'));
mockRunAppleRunnerCommand.mockResolvedValue({
found: true,
nodes: [
Expand Down Expand Up @@ -389,7 +392,7 @@ test('a failing predicate answers COMMAND_FAILED from the bound capture', async
}),
});
const sessionStore = makeSessionStore();
sessionStore.set('is-direct-false', makeIosAppSession('is-direct-false'));
sessionStore.publish('is-direct-false', makeIosAppSession('is-direct-false'));
mockRunAppleRunnerCommand.mockResolvedValue({
found: true,
text: 'Apple Account',
Expand Down Expand Up @@ -444,7 +447,7 @@ test('a miss on a surface that never settled carries the unsettled fact, and the
const sessionStore = makeSessionStore();
const session = makeIosAppSession('is-unsettled');
markDeferredInteractionOutcome({ session, command: 'scroll', positionals: [], flags: {} });
sessionStore.set('is-unsettled', session);
sessionStore.publish('is-unsettled', session);
const isVisible = () =>
dispatchIsViaRuntime({
req: isRequest('is-unsettled', ['visible', 'id=target']),
Expand Down Expand Up @@ -486,7 +489,7 @@ test('a read after a scroll that moved nothing carries the no-effect outcome, an
const sessionStore = makeSessionStore();
const session = makeIosAppSession('is-no-effect', { snapshot: makeSnapshotState([row]) });
markDeferredInteractionOutcome({ session, command: 'scroll', positionals: ['down'], flags: {} });
sessionStore.set('is-no-effect', session);
sessionStore.publish('is-no-effect', session);
const isVisible = () =>
dispatchIsViaRuntime({
req: isRequest('is-no-effect', ['visible', 'id=row']),
Expand Down
4 changes: 2 additions & 2 deletions src/daemon/__tests__/lease-lifecycle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test('admitRequestLeaseForLockedScope heartbeats and stores admitted lease on th
deviceKey: 'ios:SIM-001',
clientId: 'client-a',
});
sessionStore.set(
sessionStore.publish(
'default',
makeIosSession('default', {
lease: {
Expand Down Expand Up @@ -71,7 +71,7 @@ test('cleanupExpiredLeasedSession consumes expired lease and deletes the session
expiresAt: lease.expiresAt,
},
});
sessionStore.set('default', session);
sessionStore.publish('default', session);
now = 1_011;
const teardownSession = vi.fn(async () => {});

Expand Down
13 changes: 8 additions & 5 deletions src/daemon/__tests__/open-device-contention-wait.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function session(address: string): SessionState {

function storeWithHolder(): SessionStore {
const store = makeSessionStore('agent-device-open-wait-');
store.set(HOLDER_ADDRESS, session(HOLDER_ADDRESS));
store.publish(HOLDER_ADDRESS, session(HOLDER_ADDRESS));
return store;
}

Expand Down Expand Up @@ -161,7 +161,7 @@ test('only a fresh open with a budget and a resolved device gets a wait', () =>
).toBeUndefined();

// An open onto a session that already exists is bound to a device nobody else is refused for.
sessionStore.set(OPENER_ADDRESS, session(OPENER_ADDRESS));
sessionStore.publish(OPENER_ADDRESS, session(OPENER_ADDRESS));
expect(
beginWait({
req: budget,
Expand Down Expand Up @@ -193,7 +193,8 @@ test('a device that frees up ends the wait without claiming a spent budget', asy
vi.useFakeTimers();
const req = openRequest({ waitMs: 30_000 });
const store = storeWithHolder();
const release = setTimeout(() => store.delete(HOLDER_ADDRESS), 600);
const holder = store.lookup(HOLDER_ADDRESS)!;
const release = setTimeout(() => store.retire(holder), 600);

await waitUntil(
() =>
Expand Down Expand Up @@ -223,10 +224,12 @@ test('an open yields the device lock to a session that took the device after the
})!;
// A competing open puts its session on the device in the window between this open's look at the
// free store and its first pass under the locks, and hands it back 300ms later.
setTimeout(() => store.delete(HOLDER_ADDRESS), 300);
const trace = lockTrace({
onAcquire: (pass) => {
if (pass === 1) store.set(HOLDER_ADDRESS, session(HOLDER_ADDRESS));
if (pass === 1) {
const holder = store.publish(HOLDER_ADDRESS, session(HOLDER_ADDRESS));
setTimeout(() => store.retire(holder), 300);
}
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/orientation-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ test('request router joins orientation admission to execution and ref invalidati
const sessionStore = makeSessionStore('agent-device-orientation-generic-');
const session = makeSession('orientation-runtime', { device: testDevice });
activateCompleteRefFrame(session);
sessionStore.set(session.name, session);
sessionStore.publish(session.name, session);
const handler = createRequestHandler({
logPath: '/tmp/daemon.log',
token: 't',
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/__tests__/perf-capture-session-resource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ test('a perf stop whose pull failed re-collects the device-side trace the first
createdAt: 1,
actions: [],
};
sessionStore.set(sessionName, session);
sessionStore.publish(sessionName, session);
const capture = {
action: 'start' as const,
kind: 'perfetto' as const,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ test.each([
const root = mkdtempForTestSync('agent-device-replay-divergence-qsshade-');
const sessionStore = new SessionStore(path.join(root, 'sessions'));
const sessionName = 'default';
sessionStore.set(
sessionStore.publish(
sessionName,
makeAndroidSession(sessionName, { appBundleId: 'com.google.android.deskclock' }),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ test('buildReplayFailureDivergence: routes through the freshness-retry wrapper a
baselineCount: 20,
routeComparable: false,
};
sessionStore.set(sessionName, session);
sessionStore.publish(sessionName, session);

// Capture 1: stale, near-empty dump (a single bare view — no hittable/label/
// id) → `sharp-drop` vs the 20-node baseline → triggers a retry.
Expand Down Expand Up @@ -149,7 +149,7 @@ test('buildReplayFailureDivergence: divergence capture drops the action snapshot
const sessionStore = new SessionStore(path.join(root, 'sessions'));
const sessionName = 'default';
const appBundleId = 'com.callstack.agentdevicelab';
sessionStore.set(sessionName, makeAndroidSession(sessionName, { appBundleId }));
sessionStore.publish(sessionName, makeAndroidSession(sessionName, { appBundleId }));

mockDispatchCommand.mockReset();
mockDispatchCommand.mockResolvedValue({
Expand Down
Loading
Loading