test(ai-roles): cover update() happy-path return shape (#88)

The concurrent-soft-delete guard was already covered; add the missing assertion
that update() returns toView(updated) from the post-update re-fetch (full
AgentRoleView shape, distinct second findById row), so a regression returning the
stale pre-update view or leaking columns is caught.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude code agent 227
2026-06-21 03:28:58 +03:00
parent 317fdb9424
commit b597841cf0

View File

@@ -121,6 +121,49 @@ describe('AiAgentRolesService guards', () => {
expect(repo.findById).toHaveBeenCalledTimes(2);
});
it('happy path returns toView(updated) reflecting the POST-update re-fetch (full AgentRoleView shape)', async () => {
// The pre-update guard sees the OLD row; the post-update re-fetch returns a
// DISTINCT row (the freshly-persisted state). The service must return the
// view built from the SECOND findById, not the first — proving update()
// returns toView(updated) rather than toView(existing).
const { service, repo } = makeService();
const oldRow = makeRow({ id: 'r1', name: 'Old name' });
const createdAt = new Date('2024-01-01T00:00:00.000Z');
const updatedAt = new Date('2024-06-20T00:00:00.000Z');
const updatedRow = makeRow({
id: 'r1',
name: 'New name',
emoji: '🤖',
description: 'updated description',
instructions: 'updated instructions',
modelConfig: { driver: 'gemini', chatModel: 'gemini-2.0-flash' } as never,
enabled: false,
createdAt,
updatedAt,
});
repo.findById
.mockResolvedValueOnce(oldRow)
.mockResolvedValueOnce(updatedRow);
const result = await service.update('ws-1', 'r1', {
name: 'New name',
} as UpdateAgentRoleDto);
// The returned value is the full admin view of the RE-FETCHED row, with
// exactly the fields toView produces (no extra/leaked columns).
expect(result).toEqual({
id: 'r1',
name: 'New name',
emoji: '🤖',
description: 'updated description',
instructions: 'updated instructions',
modelConfig: { driver: 'gemini', chatModel: 'gemini-2.0-flash' },
enabled: false,
createdAt,
updatedAt,
});
});
it('emoji/description tri-state: emoji:"" => null (clear), emoji omitted => undefined (unchanged), description:" " => null', async () => {
const { service, repo } = makeService({ existing: makeRow() });