fix(tests): resolve pre-existing test failures on non-standard environments
- TeamProvisioningServiceRelay: add missing stat fields (mode, dev, ino, mtimeMs, ctimeMs, birthtimeMs) to fs mock so new fingerprint-based TeamConfigReader cache can read config in tests - TeamMcpConfigBuilder: export clearResolvedNodePathForTests() to reset module-level node path cache between tests; restore execFileMock implementation in beforeEach after vi.restoreAllMocks() clears it; broaden node binary regex to accept versioned names (node-22, node-20) common on Fedora/RHEL systems - ScheduledTaskExecutor: strip CLAUDECODE at spawn site as last defence so nested-session detection is prevented even when buildProviderAwareCliEnv merges it back in from the outer process environment
This commit is contained in:
parent
9ad32d9978
commit
7609c548c5
4 changed files with 88 additions and 72 deletions
|
|
@ -178,7 +178,9 @@ export class ScheduledTaskExecutor {
|
||||||
cwd: request.config.cwd,
|
cwd: request.config.cwd,
|
||||||
// shellEnv spread after buildEnrichedEnv ensures freshly-resolved values
|
// shellEnv spread after buildEnrichedEnv ensures freshly-resolved values
|
||||||
// take precedence over the cached snapshot inside buildEnrichedEnv.
|
// take precedence over the cached snapshot inside buildEnrichedEnv.
|
||||||
env,
|
// CLAUDECODE stripped last to prevent nested-session detection regardless
|
||||||
|
// of what buildProviderAwareCliEnv merges in.
|
||||||
|
env: { ...env, CLAUDECODE: undefined },
|
||||||
stdio: ['ignore', 'pipe', 'pipe'],
|
stdio: ['ignore', 'pipe', 'pipe'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,10 @@ async function hasValidServerCopy(dir: string): Promise<boolean> {
|
||||||
|
|
||||||
let _resolvedNodePath: string | undefined;
|
let _resolvedNodePath: string | undefined;
|
||||||
|
|
||||||
|
export function clearResolvedNodePathForTests(): void {
|
||||||
|
_resolvedNodePath = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the real `node` binary path. In Electron, process.execPath is the
|
* Find the real `node` binary path. In Electron, process.execPath is the
|
||||||
* Electron binary — NOT node — so we must resolve node separately.
|
* Electron binary — NOT node — so we must resolve node separately.
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,10 @@ vi.mock('@main/utils/pathDecoder', async (importOriginal) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
import { setAppDataBasePath, setClaudeBasePathOverride } from '@main/utils/pathDecoder';
|
import { setAppDataBasePath, setClaudeBasePathOverride } from '@main/utils/pathDecoder';
|
||||||
import { TeamMcpConfigBuilder } from '@main/services/team/TeamMcpConfigBuilder';
|
import {
|
||||||
|
TeamMcpConfigBuilder,
|
||||||
|
clearResolvedNodePathForTests,
|
||||||
|
} from '@main/services/team/TeamMcpConfigBuilder';
|
||||||
|
|
||||||
describe('TeamMcpConfigBuilder', () => {
|
describe('TeamMcpConfigBuilder', () => {
|
||||||
const createdPaths: string[] = [];
|
const createdPaths: string[] = [];
|
||||||
|
|
@ -93,7 +96,7 @@ describe('TeamMcpConfigBuilder', () => {
|
||||||
entry: string
|
entry: string
|
||||||
): void {
|
): void {
|
||||||
expect(server?.args).toEqual([entry]);
|
expect(server?.args).toEqual([entry]);
|
||||||
expect(server?.command).toMatch(/(^node$|[\\/]node(?:\.exe)?$)/);
|
expect(server?.command).toMatch(/(^node(?:-\d+)?$|[\\/]node(?:-\d+)?(?:\.exe)?$)/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function expectNodeTsxSourceEntry(
|
function expectNodeTsxSourceEntry(
|
||||||
|
|
@ -102,7 +105,7 @@ describe('TeamMcpConfigBuilder', () => {
|
||||||
sourceEntry: string
|
sourceEntry: string
|
||||||
): void {
|
): void {
|
||||||
expect(server?.args).toEqual([tsxCli, sourceEntry]);
|
expect(server?.args).toEqual([tsxCli, sourceEntry]);
|
||||||
expect(server?.command).toMatch(/(^node$|[\\/]node(?:\.exe)?$)/);
|
expect(server?.command).toMatch(/(^node(?:-\d+)?$|[\\/]node(?:-\d+)?(?:\.exe)?$)/);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBuiltWorkspaceEntry(): string {
|
function getBuiltWorkspaceEntry(): string {
|
||||||
|
|
@ -165,6 +168,7 @@ describe('TeamMcpConfigBuilder', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
clearResolvedNodePathForTests();
|
||||||
originalResourcesPath = (process as NodeJS.Process & { resourcesPath?: string }).resourcesPath;
|
originalResourcesPath = (process as NodeJS.Process & { resourcesPath?: string }).resourcesPath;
|
||||||
tempAppData = fs.mkdtempSync(path.join(os.tmpdir(), 'team-mcp-appdata-'));
|
tempAppData = fs.mkdtempSync(path.join(os.tmpdir(), 'team-mcp-appdata-'));
|
||||||
createdDirs.push(tempAppData);
|
createdDirs.push(tempAppData);
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,16 @@ const hoisted = vi.hoisted(() => {
|
||||||
error.code = 'ENOENT';
|
error.code = 'ENOENT';
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
const size = Buffer.byteLength(data, 'utf8');
|
||||||
return {
|
return {
|
||||||
isFile: () => true,
|
isFile: () => true,
|
||||||
size: Buffer.byteLength(data, 'utf8'),
|
size,
|
||||||
|
mode: 0o644,
|
||||||
|
dev: 0,
|
||||||
|
ino: 0,
|
||||||
|
mtimeMs: 0,
|
||||||
|
ctimeMs: 0,
|
||||||
|
birthtimeMs: 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -54,22 +61,20 @@ const hoisted = vi.hoisted(() => {
|
||||||
files.set(sentMessagesPath, JSON.stringify(rows));
|
files.set(sentMessagesPath, JSON.stringify(rows));
|
||||||
return message;
|
return message;
|
||||||
}),
|
}),
|
||||||
sendInboxMessage: vi.fn(
|
sendInboxMessage: vi.fn((teamName: string, message: Record<string, unknown>) => {
|
||||||
(teamName: string, message: Record<string, unknown>) => {
|
const member =
|
||||||
const member =
|
typeof message.member === 'string'
|
||||||
typeof message.member === 'string'
|
? message.member
|
||||||
? message.member
|
: typeof message.to === 'string'
|
||||||
: typeof message.to === 'string'
|
? message.to
|
||||||
? message.to
|
: 'unknown';
|
||||||
: 'unknown';
|
const p = `/mock/teams/${teamName}/inboxes/${member}.json`;
|
||||||
const p = `/mock/teams/${teamName}/inboxes/${member}.json`;
|
const current = files.get(p);
|
||||||
const current = files.get(p);
|
const rows = current ? (JSON.parse(current) as unknown[]) : [];
|
||||||
const rows = current ? (JSON.parse(current) as unknown[]) : [];
|
rows.push(message);
|
||||||
rows.push(message);
|
files.set(p, JSON.stringify(rows));
|
||||||
files.set(p, JSON.stringify(rows));
|
return { deliveredToInbox: true, messageId: 'mock-id', message };
|
||||||
return { deliveredToInbox: true, messageId: 'mock-id', message };
|
}),
|
||||||
}
|
|
||||||
),
|
|
||||||
setAtomicWriteShouldFail: (next: boolean) => {
|
setAtomicWriteShouldFail: (next: boolean) => {
|
||||||
atomicWriteShouldFail = next;
|
atomicWriteShouldFail = next;
|
||||||
},
|
},
|
||||||
|
|
@ -371,7 +376,9 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
const payload = String(writeSpy.mock.calls[0]?.[0] ?? '');
|
const payload = String(writeSpy.mock.calls[0]?.[0] ?? '');
|
||||||
expect(payload).toContain('Source: system_notification');
|
expect(payload).toContain('Source: system_notification');
|
||||||
expect(payload).toContain('summary looks like \\"Comment on #...\\"');
|
expect(payload).toContain('summary looks like \\"Comment on #...\\"');
|
||||||
expect(payload).toContain('reply via task_add_comment only when you have a substantive board update');
|
expect(payload).toContain(
|
||||||
|
'reply via task_add_comment only when you have a substantive board update'
|
||||||
|
);
|
||||||
expect(payload).toContain('Do NOT post acknowledgement-only task comments');
|
expect(payload).toContain('Do NOT post acknowledgement-only task comments');
|
||||||
|
|
||||||
(service as any).handleStreamJsonMessage(run, {
|
(service as any).handleStreamJsonMessage(run, {
|
||||||
|
|
@ -492,9 +499,13 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
runId: 'run-old',
|
runId: 'run-old',
|
||||||
});
|
});
|
||||||
const inboxDeferred = createDeferred<typeof inboxMessages>();
|
const inboxDeferred = createDeferred<typeof inboxMessages>();
|
||||||
const inboxReader = (service as unknown as {
|
const inboxReader = (
|
||||||
inboxReader: { getMessagesFor: (team: string, member: string) => Promise<typeof inboxMessages> };
|
service as unknown as {
|
||||||
}).inboxReader;
|
inboxReader: {
|
||||||
|
getMessagesFor: (team: string, member: string) => Promise<typeof inboxMessages>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
).inboxReader;
|
||||||
const inboxSpy = vi
|
const inboxSpy = vi
|
||||||
.spyOn(inboxReader, 'getMessagesFor')
|
.spyOn(inboxReader, 'getMessagesFor')
|
||||||
.mockImplementationOnce(async () => await inboxDeferred.promise)
|
.mockImplementationOnce(async () => await inboxDeferred.promise)
|
||||||
|
|
@ -538,14 +549,13 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
|
|
||||||
const { runId: oldRunId } = attachAliveRun(service, teamName, { runId: 'run-old' });
|
const { runId: oldRunId } = attachAliveRun(service, teamName, { runId: 'run-old' });
|
||||||
const inboxDeferred = createDeferred<[typeof permissionMessage]>();
|
const inboxDeferred = createDeferred<[typeof permissionMessage]>();
|
||||||
const inboxReader = (service as unknown as {
|
const inboxReader = (
|
||||||
inboxReader: {
|
service as unknown as {
|
||||||
getMessagesFor: (
|
inboxReader: {
|
||||||
team: string,
|
getMessagesFor: (team: string, member: string) => Promise<[typeof permissionMessage]>;
|
||||||
member: string
|
};
|
||||||
) => Promise<[typeof permissionMessage]>;
|
}
|
||||||
};
|
).inboxReader;
|
||||||
}).inboxReader;
|
|
||||||
const inboxSpy = vi
|
const inboxSpy = vi
|
||||||
.spyOn(inboxReader, 'getMessagesFor')
|
.spyOn(inboxReader, 'getMessagesFor')
|
||||||
.mockImplementationOnce(async () => await inboxDeferred.promise)
|
.mockImplementationOnce(async () => await inboxDeferred.promise)
|
||||||
|
|
@ -654,7 +664,9 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
const payload = String(writeSpy.mock.calls[0]?.[0] ?? '');
|
const payload = String(writeSpy.mock.calls[0]?.[0] ?? '');
|
||||||
expect(payload).toContain('Source: cross_team');
|
expect(payload).toContain('Source: cross_team');
|
||||||
expect(payload).toContain('Cross-team conversationId: conv-explicit');
|
expect(payload).toContain('Cross-team conversationId: conv-explicit');
|
||||||
expect(payload).toContain('Call the MCP tool named cross_team_send with toTeam=\\"other-team\\"');
|
expect(payload).toContain(
|
||||||
|
'Call the MCP tool named cross_team_send with toTeam=\\"other-team\\"'
|
||||||
|
);
|
||||||
expect(payload).toContain('replyToConversationId=\\"conv-explicit\\"');
|
expect(payload).toContain('replyToConversationId=\\"conv-explicit\\"');
|
||||||
expect(payload).toContain('NEVER set recipient/to to \\"cross_team_send\\"');
|
expect(payload).toContain('NEVER set recipient/to to \\"cross_team_send\\"');
|
||||||
|
|
||||||
|
|
@ -905,7 +917,11 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
attachAliveRun(service, teamName);
|
attachAliveRun(service, teamName);
|
||||||
|
|
||||||
const run = (service as unknown as { runs: Map<string, unknown> }).runs.get('run-1') as {
|
const run = (service as unknown as { runs: Map<string, unknown> }).runs.get('run-1') as {
|
||||||
silentUserDmForward: { target: string; startedAt: string; mode: 'user_dm' | 'member_inbox_relay' } | null;
|
silentUserDmForward: {
|
||||||
|
target: string;
|
||||||
|
startedAt: string;
|
||||||
|
mode: 'user_dm' | 'member_inbox_relay';
|
||||||
|
} | null;
|
||||||
};
|
};
|
||||||
run.silentUserDmForward = {
|
run.silentUserDmForward = {
|
||||||
target: 'alice',
|
target: 'alice',
|
||||||
|
|
@ -1072,9 +1088,13 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
runId: 'run-old',
|
runId: 'run-old',
|
||||||
});
|
});
|
||||||
const inboxDeferred = createDeferred<typeof inboxMessages>();
|
const inboxDeferred = createDeferred<typeof inboxMessages>();
|
||||||
const inboxReader = (service as unknown as {
|
const inboxReader = (
|
||||||
inboxReader: { getMessagesFor: (team: string, member: string) => Promise<typeof inboxMessages> };
|
service as unknown as {
|
||||||
}).inboxReader;
|
inboxReader: {
|
||||||
|
getMessagesFor: (team: string, member: string) => Promise<typeof inboxMessages>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
).inboxReader;
|
||||||
const inboxSpy = vi
|
const inboxSpy = vi
|
||||||
.spyOn(inboxReader, 'getMessagesFor')
|
.spyOn(inboxReader, 'getMessagesFor')
|
||||||
.mockImplementationOnce(async () => await inboxDeferred.promise)
|
.mockImplementationOnce(async () => await inboxDeferred.promise)
|
||||||
|
|
@ -1284,11 +1304,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
|
|
||||||
await (service as any).markInboxMessagesRead(teamName, 'alice', [
|
await (service as any).markInboxMessagesRead(teamName, 'alice', [
|
||||||
{
|
{
|
||||||
messageId: buildLegacyInboxMessageId(
|
messageId: buildLegacyInboxMessageId(legacyRow.from, legacyRow.timestamp, legacyRow.text),
|
||||||
legacyRow.from,
|
|
||||||
legacyRow.timestamp,
|
|
||||||
legacyRow.text
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -1684,9 +1700,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
taskRefs: [{ teamName, taskId: 'task-1', displayId: 'abcd1234' }],
|
taskRefs: [{ teamName, taskId: 'task-1', displayId: 'abcd1234' }],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows[0].read).toBe(true);
|
expect(rows[0].read).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1732,9 +1746,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
failed: 0,
|
failed: 0,
|
||||||
lastDelivery: { delivered: true, responsePending: true },
|
lastDelivery: { delivered: true, responsePending: true },
|
||||||
});
|
});
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows[0].read).toBe(false);
|
expect(rows[0].read).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1866,9 +1878,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
teamName,
|
teamName,
|
||||||
expect.objectContaining({ messageId: 'opencode-terminal-new' })
|
expect.objectContaining({ messageId: 'opencode-terminal-new' })
|
||||||
);
|
);
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows.map((row: { read?: boolean }) => row.read)).toEqual([false, true]);
|
expect(rows.map((row: { read?: boolean }) => row.read)).toEqual([false, true]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1952,9 +1962,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
'opencode_attachments_not_supported_for_secondary_runtime'
|
'opencode_attachments_not_supported_for_secondary_runtime'
|
||||||
);
|
);
|
||||||
vi.mocked(console.warn).mockClear();
|
vi.mocked(console.warn).mockClear();
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows[0].read).toBe(false);
|
expect(rows[0].read).toBe(false);
|
||||||
expect(records[0]).toMatchObject({
|
expect(records[0]).toMatchObject({
|
||||||
inboxMessageId: 'opencode-attachment-1',
|
inboxMessageId: 'opencode-attachment-1',
|
||||||
|
|
@ -1979,7 +1987,10 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const identity = await (service as any).resolveOpenCodeMemberDeliveryIdentity(teamName, 'jack');
|
const identity = await (service as any).resolveOpenCodeMemberDeliveryIdentity(
|
||||||
|
teamName,
|
||||||
|
'jack'
|
||||||
|
);
|
||||||
expect(identity.ok).toBe(true);
|
expect(identity.ok).toBe(true);
|
||||||
const laneId = identity.laneId;
|
const laneId = identity.laneId;
|
||||||
const records: any[] = [];
|
const records: any[] = [];
|
||||||
|
|
@ -2000,7 +2011,12 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
return record;
|
return record;
|
||||||
}),
|
}),
|
||||||
markAcceptanceUnknown: vi.fn(
|
markAcceptanceUnknown: vi.fn(
|
||||||
async (input: { id: string; reason: string; nextAttemptAt: string; markedAt: string }) => {
|
async (input: {
|
||||||
|
id: string;
|
||||||
|
reason: string;
|
||||||
|
nextAttemptAt: string;
|
||||||
|
markedAt: string;
|
||||||
|
}) => {
|
||||||
const record = records.find((candidate) => candidate.id === input.id);
|
const record = records.find((candidate) => candidate.id === input.id);
|
||||||
Object.assign(record, {
|
Object.assign(record, {
|
||||||
status: 'failed_retryable',
|
status: 'failed_retryable',
|
||||||
|
|
@ -2132,9 +2148,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
teamName,
|
teamName,
|
||||||
expect.objectContaining({ messageId: 'opencode-inflight-new' })
|
expect.objectContaining({ messageId: 'opencode-inflight-new' })
|
||||||
);
|
);
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows.map((row: { read?: boolean }) => row.read)).toEqual([true, true]);
|
expect(rows.map((row: { read?: boolean }) => row.read)).toEqual([true, true]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2211,9 +2225,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
const relay = await service.relayInboxFileToLiveRecipient(teamName, 'jack');
|
const relay = await service.relayInboxFileToLiveRecipient(teamName, 'jack');
|
||||||
|
|
||||||
expect(relay).toMatchObject({ kind: 'opencode_member', relayed: 1 });
|
expect(relay).toMatchObject({ kind: 'opencode_member', relayed: 1 });
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows[0].read).toBe(true);
|
expect(rows[0].read).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2303,9 +2315,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
'OpenCode inbox relay failed for jack/opencode-relay-failed-1'
|
'OpenCode inbox relay failed for jack/opencode-relay-failed-1'
|
||||||
);
|
);
|
||||||
vi.mocked(console.warn).mockClear();
|
vi.mocked(console.warn).mockClear();
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows[0].read).toBe(false);
|
expect(rows[0].read).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -2337,9 +2347,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
delivered: true,
|
delivered: true,
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
});
|
});
|
||||||
vi.spyOn(service as any, 'markInboxMessagesRead').mockRejectedValue(
|
vi.spyOn(service as any, 'markInboxMessagesRead').mockRejectedValue(new Error('write failed'));
|
||||||
new Error('write failed')
|
|
||||||
);
|
|
||||||
|
|
||||||
const relay = await service.relayOpenCodeMemberInboxMessages(teamName, 'jack');
|
const relay = await service.relayOpenCodeMemberInboxMessages(teamName, 'jack');
|
||||||
|
|
||||||
|
|
@ -2360,9 +2368,7 @@ describe('TeamProvisioningService relayLeadInboxMessages', () => {
|
||||||
'opencode_inbox_mark_read_failed_after_delivery'
|
'opencode_inbox_mark_read_failed_after_delivery'
|
||||||
);
|
);
|
||||||
vi.mocked(console.warn).mockClear();
|
vi.mocked(console.warn).mockClear();
|
||||||
const rows = JSON.parse(
|
const rows = JSON.parse(hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]');
|
||||||
hoisted.files.get(`/mock/teams/${teamName}/inboxes/jack.json`) ?? '[]'
|
|
||||||
);
|
|
||||||
expect(rows[0].read).toBe(false);
|
expect(rows[0].read).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue