agent-ecosystem/test/main/services/identity/AgentTeamsIdentityStore.test.ts

130 lines
4.3 KiB
TypeScript

import * as fs from 'fs/promises';
import * as os from 'os';
import * as path from 'path';
import {
AGENT_TEAMS_IDENTITY_STORE_PATH_ENV,
applyAgentTeamsIdentityEnv,
ensureAgentTeamsClientIdentity,
getAgentTeamsIdentityStorePath,
getSentryAnonymousUserId,
readAgentTeamsIdentityStore,
} from '@main/services/identity/AgentTeamsIdentityStore';
import { setAppDataBasePath, setClaudeBasePathOverride } from '@main/utils/pathDecoder';
const LEGACY_CLIENT_ID = '22222222-2222-4222-8222-222222222222';
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
describe('AgentTeamsIdentityStore', () => {
let tempRoot: string;
let tempHome: string;
let tempAppDataBase: string;
let previousHome: string | undefined;
beforeEach(async () => {
tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'agent-teams-identity-'));
tempHome = path.join(tempRoot, 'home');
tempAppDataBase = path.join(tempRoot, 'app-user-data');
await fs.mkdir(tempHome, { recursive: true });
previousHome = process.env.HOME;
process.env.HOME = tempHome;
setClaudeBasePathOverride(null);
setAppDataBasePath(tempAppDataBase);
});
afterEach(async () => {
setClaudeBasePathOverride(null);
setAppDataBasePath(null);
if (previousHome === undefined) {
delete process.env.HOME;
} else {
process.env.HOME = previousHome;
}
await fs.rm(tempRoot, { recursive: true, force: true });
});
it('creates and reuses a stable app-data UUID', async () => {
const first = await ensureAgentTeamsClientIdentity();
const second = await ensureAgentTeamsClientIdentity();
const persisted = await readAgentTeamsIdentityStore();
expect(first.clientId).toMatch(UUID_PATTERN);
expect(second.clientId).toBe(first.clientId);
expect(first.source).toBe('created');
expect(second.source).toBe('app-data');
expect(persisted?.schemaVersion).toBe(1);
expect(persisted?.clientId).toBe(first.clientId);
});
it('falls back safely when app-data JSON or UUID is invalid', async () => {
const storePath = getAgentTeamsIdentityStorePath();
await fs.mkdir(path.dirname(storePath), { recursive: true });
await fs.writeFile(storePath, '{not-json', 'utf8');
const fromInvalidJson = await ensureAgentTeamsClientIdentity();
expect(fromInvalidJson.clientId).toMatch(UUID_PATTERN);
await fs.writeFile(
storePath,
JSON.stringify({
schemaVersion: 1,
clientId: 'not-a-uuid',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}),
'utf8'
);
const fromInvalidUuid = await ensureAgentTeamsClientIdentity();
expect(fromInvalidUuid.clientId).toMatch(UUID_PATTERN);
expect(fromInvalidUuid.clientId).not.toBe('not-a-uuid');
});
it('soft-migrates legacy ~/.claude.json agentTeams into app data', async () => {
await fs.writeFile(
path.join(tempHome, '.claude.json'),
JSON.stringify({
agentTeams: {
clientId: LEGACY_CLIENT_ID,
session: {
accessToken: 'legacy-access',
refreshToken: 'legacy-refresh',
},
capabilities: {
token: 'legacy-capabilities',
},
},
}),
'utf8'
);
const identity = await ensureAgentTeamsClientIdentity();
const persisted = await readAgentTeamsIdentityStore();
const legacy = JSON.parse(await fs.readFile(path.join(tempHome, '.claude.json'), 'utf8')) as {
agentTeams?: { clientId?: string };
};
expect(identity).toMatchObject({
clientId: LEGACY_CLIENT_ID,
source: 'legacy-global-config',
});
expect(persisted?.clientId).toBe(LEGACY_CLIENT_ID);
expect(legacy.agentTeams?.clientId).toBe(LEGACY_CLIENT_ID);
});
it('builds deterministic Sentry-safe anonymous user ids', () => {
const hashed = getSentryAnonymousUserId(LEGACY_CLIENT_ID);
expect(hashed).toBe(getSentryAnonymousUserId(LEGACY_CLIENT_ID));
expect(hashed).not.toBe(LEGACY_CLIENT_ID);
expect(hashed).toMatch(/^[a-f0-9]{64}$/);
});
it('sets the orchestrator identity store env path', () => {
const env: NodeJS.ProcessEnv = {};
applyAgentTeamsIdentityEnv(env);
expect(env[AGENT_TEAMS_IDENTITY_STORE_PATH_ENV]).toBe(getAgentTeamsIdentityStorePath());
});
});