GitNexus/gitnexus/test/unit/group-service-not-found.test.ts
Gergő Magyar 7e993ab897
fix(group): fail ambiguous sync names and honor analyze --name (#3094)
* fix(group): fail sync when a member name is ambiguous

Silent first-match bound the wrong clone when --allow-duplicate-name
registered two paths under one alias. Refs #3028.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(analyze): apply --name on the already-up-to-date path

A rename should not require --force when the index is already current.
Register before the same-commit branch restamp. Refs #3028.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(group): hint member path when impact --repo is an alias

$localRepo stays the yaml key; joining on the registry alias is a
non-join. List matching keys so operators can retry. Refs #3028.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(group): keep injected sync and alias hints consistent

Workspace-deps path maps reuse the resolved handle so duplicate names
cannot throw after an injected resolver. Alias hints match case-insensitively.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 21:48:46 +00:00

96 lines
3.7 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
const loadGroupConfigMock = vi.fn();
const getGroupDirMock = vi.fn(() => '/fake/.gitnexus/groups/missing');
const getDefaultGitnexusDirMock = vi.fn(() => '/fake/.gitnexus');
const readContractRegistryMock = vi.fn(() => null);
const listGroupsMock = vi.fn(() => []);
const syncGroupMock = vi.fn();
vi.mock('../../src/core/group/config-parser.js', async () => {
const { GroupNotFoundError } = await vi.importActual<
typeof import('../../src/core/group/config-parser.js')
>('../../src/core/group/config-parser.js');
return { loadGroupConfig: loadGroupConfigMock, GroupNotFoundError };
});
vi.mock('../../src/core/group/storage.js', () => ({
getDefaultGitnexusDir: getDefaultGitnexusDirMock,
getGroupDir: getGroupDirMock,
readContractRegistry: readContractRegistryMock,
listGroups: listGroupsMock,
}));
vi.mock('../../src/core/group/sync.js', () => ({
syncGroup: syncGroupMock,
formatGroupSyncAmbiguousError: (err: Error) => err.message,
}));
vi.mock('../../src/core/git-staleness.js', () => ({ checkStaleness: vi.fn() }));
describe('GroupService — missing group error handling', () => {
let GroupService: typeof import('../../src/core/group/service.js').GroupService;
let GroupNotFoundError: typeof import('../../src/core/group/config-parser.js').GroupNotFoundError;
let service: InstanceType<typeof import('../../src/core/group/service.js').GroupService>;
const stubPort = {
resolveRepo: vi.fn(),
impact: vi.fn(),
query: vi.fn(),
impactByUid: vi.fn(),
contextByUid: vi.fn(),
};
beforeEach(async () => {
vi.resetModules();
loadGroupConfigMock.mockReset();
({ GroupService } = await import('../../src/core/group/service.js'));
({ GroupNotFoundError } = await import('../../src/core/group/config-parser.js'));
service = new GroupService(stubPort as never);
loadGroupConfigMock.mockRejectedValue(new GroupNotFoundError('missing'));
});
it('groupSync returns friendly error for missing group', async () => {
const result = await service.groupSync({ name: 'missing' });
expect(result).toEqual({
error: 'Group "missing" not found. Run group_list to see configured groups.',
});
});
it('groupQuery returns friendly error for missing group', async () => {
const result = await service.groupQuery({ name: 'missing', query: 'auth' });
expect(result).toEqual({
error: 'Group "missing" not found. Run group_list to see configured groups.',
});
});
it('groupStatus returns friendly error for missing group', async () => {
const result = await service.groupStatus({ name: 'missing' });
expect(result).toEqual({
error: 'Group "missing" not found. Run group_list to see configured groups.',
});
});
it('groupSync re-throws non-ENOENT errors', async () => {
loadGroupConfigMock.mockRejectedValue(new Error('YAML parse error'));
await expect(service.groupSync({ name: 'bad-yaml' })).rejects.toThrow('YAML parse error');
});
it('groupQuery re-throws non-ENOENT errors', async () => {
loadGroupConfigMock.mockRejectedValue(new Error('YAML parse error'));
await expect(service.groupQuery({ name: 'bad-yaml', query: 'auth' })).rejects.toThrow(
'YAML parse error',
);
});
it('groupStatus re-throws non-ENOENT errors', async () => {
loadGroupConfigMock.mockRejectedValue(new Error('YAML parse error'));
await expect(service.groupStatus({ name: 'bad-yaml' })).rejects.toThrow('YAML parse error');
});
it('groupList returns friendly error for missing group', async () => {
const result = await service.groupList({ name: 'missing' });
expect(result).toEqual({
error: 'Group "missing" not found. Run group_list to see configured groups.',
});
});
});