Skip to content

Commit 37c681c

Browse files
committed
Exercise fresh provider claim guard and existing reopen bypass
1 parent 29975c2 commit 37c681c

3 files changed

Lines changed: 64 additions & 43 deletions

File tree

‎packages/daemon/src/core/composite-resource-admission-lifecycle.test.ts‎

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import crypto from 'node:crypto';
44
import { expect, test } from 'vitest';
55
import { canonicalLocalDeviceKey } from '@agent-device/daemon-contracts/device-claims';
66
import { deviceIdentity } from '@agent-device/kernel/device';
7+
import { publishDaemonRegistration } from '@agent-device/test-kit';
78
import { resourceAdmissionWorld, assertRpcOk } from './composite-resource-admission.fixtures.ts';
89
import { seedExistingCapture } from './composite-resource-existing.fixtures.ts';
910

@@ -106,44 +107,59 @@ test('bounded close may release a local lease when the daemon has no provider re
106107
}
107108
});
108109

109-
test('active provider device skips local foreign-claim preview', async () => {
110-
const world = await resourceAdmissionWorld('capture', { providerOwned: true });
111-
try {
112-
const device = world.sessionStore.requireCurrent(world.ref).device;
113-
const deviceKey = canonicalLocalDeviceKey(deviceIdentity(device));
114-
const claimsDir = path.join(world.daemon.stateDir, 'claims');
115-
fs.mkdirSync(claimsDir, { recursive: true });
116-
const claimPath = path.join(
117-
claimsDir,
118-
`${crypto.createHash('sha256').update(deviceKey).digest('hex')}.json`,
119-
);
120-
fs.writeFileSync(
121-
claimPath,
122-
JSON.stringify({
123-
schemaVersion: 3,
124-
deviceKey,
125-
device: { ...deviceIdentity(device), name: device.name },
126-
session: 'foreign',
127-
workspace: '/foreign',
128-
stateDir: '/foreign/state',
129-
ownerPid: process.ppid,
130-
ownerStartTime: null,
131-
ownerToken: 'foreign-token',
132-
createdAtMs: 1,
133-
updatedAtMs: 1,
134-
claimOwnerKind: 'open-session',
135-
}),
136-
);
137-
const before = fs.readFileSync(claimPath);
138-
assertRpcOk(
139-
await world.call('batch', [], {
140-
batchSteps: [
141-
{ command: 'open', positionals: ['com.android.settings'], flags: { relaunch: true } },
142-
],
143-
}),
144-
);
145-
expect(fs.readFileSync(claimPath)).toEqual(before);
146-
} finally {
147-
await world.daemon.close();
148-
}
149-
});
110+
test.each([true, false])(
111+
'fresh bounded open with providerOwned=%s handles a foreign local claim',
112+
async (providerOwned) => {
113+
const world = await resourceAdmissionWorld('capture', { providerOwned });
114+
try {
115+
const device = world.sessionStore.requireCurrent(world.ref).device;
116+
world.sessionStore.retire(world.ref);
117+
const deviceKey = canonicalLocalDeviceKey(deviceIdentity(device));
118+
const claimsDir = path.join(world.daemon.stateDir, 'claims');
119+
const foreignStateDir = path.join(world.daemon.stateDir, 'foreign-owner');
120+
fs.mkdirSync(foreignStateDir, { recursive: true });
121+
publishDaemonRegistration(foreignStateDir, { pid: process.ppid, startTime: null });
122+
fs.mkdirSync(claimsDir, { recursive: true });
123+
const claimPath = path.join(
124+
claimsDir,
125+
`${crypto.createHash('sha256').update(deviceKey).digest('hex')}.json`,
126+
);
127+
fs.writeFileSync(
128+
claimPath,
129+
JSON.stringify({
130+
schemaVersion: 3,
131+
deviceKey,
132+
device: { ...deviceIdentity(device), name: device.name },
133+
session: 'foreign',
134+
workspace: '/foreign',
135+
stateDir: foreignStateDir,
136+
ownerPid: process.ppid,
137+
ownerStartTime: null,
138+
ownerToken: 'foreign-token',
139+
createdAtMs: 1,
140+
updatedAtMs: 1,
141+
claimOwnerKind: 'open-session',
142+
}),
143+
);
144+
const before = fs.readFileSync(claimPath);
145+
const response = await world.call('batch', [], {
146+
batchSteps: [{ command: 'open', positionals: ['com.android.settings'] }],
147+
});
148+
if (providerOwned) {
149+
assertRpcOk(response);
150+
expect(world.sessionStore.lookup('resource-session')).toBeDefined();
151+
} else {
152+
expect(response.json.error?.data).toMatchObject({
153+
code: 'DEVICE_IN_USE',
154+
details: { reason: 'DEVICE_CLAIM_LIVE_OWNER' },
155+
});
156+
expect(world.sessionStore.lookup('resource-session')).toBeUndefined();
157+
expect(world.inspectFacts).not.toHaveBeenCalled();
158+
expect(world.bind).not.toHaveBeenCalled();
159+
}
160+
expect(fs.readFileSync(claimPath)).toEqual(before);
161+
} finally {
162+
await world.daemon.close();
163+
}
164+
},
165+
);

‎packages/daemon/src/core/composite-resource-admission.fixtures.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '@agent-device/test-kit';
2020
import {
2121
localRuntimeOwner,
22+
providerRuntimeOwner,
2223
type DeviceRuntimeGateway,
2324
} from '@agent-device/contracts/platform-runtime';
2425
import type { PlatformRuntimeOperations } from '@agent-device/contracts/platform-runtime-operations';
@@ -44,7 +45,9 @@ export async function resourceAdmissionWorld(
4445
minLeaseTtlMs: 1,
4546
now: () => now,
4647
});
47-
const owner = localRuntimeOwner(device.platform);
48+
const owner = options.providerOwned
49+
? providerRuntimeOwner('fixture', 'lease-a')
50+
: localRuntimeOwner(device.platform);
4851
const sessionStore = new SessionStore(
4952
path.join(mkdtempForTestSync('composite-resources-'), 'sessions'),
5053
createLiveTestHostEnvironment(),

‎packages/daemon/src/server/__tests__/request-router-bounded-claim-recovery.test.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ test('claim preview uses the request workspace to recognize its own exact claim'
269269
test('bounded reopen renews its own claim after the caller changes cwd', async () => {
270270
const device = { ...makeIosDevice('SIM-REOPEN-CWD'), appleOs: 'ios' as const };
271271
process.env.AGENT_DEVICE_CLAIMS_DIR = root('bounded-reopen-cwd-');
272-
const { handler } = route(device);
272+
let inventory = [device];
273+
const { handler } = route(device, { inventory: () => inventory });
273274
expect(
274275
(
275276
await handler(
@@ -279,6 +280,7 @@ test('bounded reopen renews its own claim after the caller changes cwd', async (
279280
)
280281
).ok,
281282
).toBe(true);
283+
inventory = [device, { ...makeIosDevice('SIM-OTHER-DEVICE'), appleOs: 'ios' as const }];
282284
const request = batchStep('open', {}, 'reopen-other-cwd');
283285
const response = await handler({
284286
...request,

0 commit comments

Comments
 (0)