agent-ecosystem/test/features/member-work-sync/main/adapters/output/mergeTeamMembers.test.ts

245 lines
6.4 KiB
TypeScript

import { mergeTeamMembers } from '@features/member-work-sync/main/adapters/output/mergeTeamMembers';
import { describe, expect, it } from 'vitest';
describe('mergeTeamMembers', () => {
it('preserves config provider fields when member meta only carries runtime fields', () => {
expect(
mergeTeamMembers(
[
{
name: 'NickName',
providerId: 'codex',
providerBackendId: 'codex-native',
model: 'gpt-5.5',
effort: 'medium',
},
],
[
{
name: 'NickName',
role: 'developer',
agentType: 'general-purpose',
color: 'blue',
},
]
)
).toEqual([
{
name: 'NickName',
providerId: 'codex',
providerBackendId: 'codex-native',
model: 'gpt-5.5',
effort: 'medium',
role: 'developer',
agentType: 'general-purpose',
color: 'blue',
},
]);
});
it('allows explicit member meta values to override config values', () => {
expect(
mergeTeamMembers(
[{ name: 'Alice', providerId: 'codex', model: 'gpt-5.5', removedAt: undefined }],
[
{
name: 'Alice',
providerId: 'opencode',
model: 'minimax-m2.5-free',
removedAt: 1780567089118,
},
]
)
).toEqual([
{
name: 'Alice',
providerId: 'opencode',
model: 'minimax-m2.5-free',
removedAt: 1780567089118,
},
]);
});
it('clears stale config provider fields when explicit member meta changes provider', () => {
const [member] = mergeTeamMembers(
[
{
name: 'Alice',
providerId: 'codex',
providerBackendId: 'codex-native',
model: 'gpt-5.5',
effort: 'medium',
fastMode: 'off',
},
],
[{ name: 'Alice', providerId: 'opencode', role: 'developer' }]
);
expect(member).toEqual({
name: 'Alice',
providerId: 'opencode',
role: 'developer',
});
});
it('clears stale inferred config provider fields when explicit member meta changes provider', () => {
const [member] = mergeTeamMembers(
[
{
name: 'Alice',
model: 'gpt-5.5',
effort: 'medium',
fastMode: 'off',
},
],
[{ name: 'Alice', providerId: 'opencode', role: 'developer' }]
);
expect(member).toEqual({
name: 'Alice',
providerId: 'opencode',
role: 'developer',
});
});
it('clears stale backend-inferred config provider fields when explicit member meta changes provider', () => {
const [member] = mergeTeamMembers(
[
{
name: 'Alice',
providerBackendId: 'codex-native',
fastMode: 'off',
},
],
[{ name: 'Alice', providerId: 'opencode', role: 'developer' }]
);
expect(member).toEqual({
name: 'Alice',
providerId: 'opencode',
role: 'developer',
});
});
it('does not let providerless runtime meta model override config provider metadata', () => {
expect(
mergeTeamMembers(
[{ name: 'Alice', providerId: 'codex', model: 'gpt-5.5', effort: 'medium' }],
[
{
name: 'Alice',
role: 'developer',
model: 'opencode/openai/gpt-oss',
effort: 'high',
},
]
)
).toEqual([
{
name: 'Alice',
providerId: 'codex',
model: 'gpt-5.5',
effort: 'medium',
role: 'developer',
},
]);
});
it('preserves config fastMode off without dropping runtime provider identity fields', () => {
expect(
mergeTeamMembers(
[{ name: 'Alice', fastMode: 'off' }],
[{ name: 'Alice', model: 'opencode/openai/gpt-oss', fastMode: 'on' }]
)
).toEqual([{ name: 'Alice', fastMode: 'off', model: 'opencode/openai/gpt-oss' }]);
});
it('preserves backend-only config provider metadata over providerless runtime meta model', () => {
expect(
mergeTeamMembers(
[{ name: 'Alice', providerBackendId: 'codex-native' }],
[{ name: 'Alice', model: 'opencode/openai/gpt-oss', role: 'developer' }]
)
).toEqual([
{
name: 'Alice',
providerBackendId: 'codex-native',
role: 'developer',
},
]);
});
it('treats provider backend as stronger provider identity than a stale model', () => {
const [member] = mergeTeamMembers(
[
{
name: 'Alice',
providerBackendId: 'opencode-cli',
model: 'gpt-5.5',
fastMode: 'off',
},
],
[{ name: 'Alice', providerId: 'codex', role: 'developer' }]
);
expect(member).toEqual({
name: 'Alice',
providerId: 'codex',
role: 'developer',
});
});
it('does not treat empty or null config provider metadata as authoritative', () => {
const [member] = mergeTeamMembers(
[{ name: 'Alice', model: '', providerBackendId: null as never }],
[{ name: 'Alice', model: 'gpt-5.5', role: 'developer' }]
);
expect(member).toMatchObject({
name: 'Alice',
model: 'gpt-5.5',
role: 'developer',
});
});
it('does not treat an uninferable config model as authoritative provider identity', () => {
const [member] = mergeTeamMembers(
[{ name: 'Alice', model: 'custom-local-model', fastMode: 'off' }],
[{ name: 'Alice', model: 'gpt-5.5', role: 'developer' }]
);
expect(member).toEqual({
name: 'Alice',
model: 'gpt-5.5',
fastMode: 'off',
role: 'developer',
});
});
it('allows runtime member meta to clear stale config removal state without clearing provider fields', () => {
const [member] = mergeTeamMembers(
[{ name: 'Alice', providerId: 'codex', model: 'gpt-5.5', removedAt: 1780567089118 }],
[
{
name: 'Alice',
role: 'developer',
removedAt: undefined,
},
]
);
expect(member).toMatchObject({
name: 'Alice',
providerId: 'codex',
model: 'gpt-5.5',
role: 'developer',
});
expect(member).not.toHaveProperty('removedAt');
});
it('keeps meta-only members', () => {
expect(
mergeTeamMembers([], [{ name: 'Bob', role: 'reviewer', color: 'green' }])
).toEqual([{ name: 'Bob', role: 'reviewer', color: 'green' }]);
});
});