245 lines
6.4 KiB
TypeScript
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' }]);
|
|
});
|
|
});
|