Files
ApiCore/ApiCore-Backend/src/search/search.service.spec.ts
T
Nick SzaunigandClaude Opus 5.5 eafe925d60 fix(search): only search record kinds the caller may read
search:read alone returned apiary names, colony and queen codes, task
titles and batch numbers to roles without the matching read permission.
Each kind of result is now searched only when apiaries:read,
colonies:read, queens:read, tasks:read or honey-batches:read is held.
The search term is validated (string, at most 100 characters).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-01 07:18:33 +02:00

67 lines
2.1 KiB
TypeScript

import { SearchService } from './search.service';
import { PrismaService } from '../prisma/prisma.service';
const ALL_READ: ReadonlySet<string> = new Set([
'apiaries:read',
'colonies:read',
'queens:read',
'tasks:read',
'honey-batches:read',
]);
describe('SearchService', () => {
let service: SearchService;
let prisma: {
apiary: { findMany: jest.Mock };
colony: { findMany: jest.Mock };
queen: { findMany: jest.Mock };
task: { findMany: jest.Mock };
honeyBatch: { findMany: jest.Mock };
};
beforeEach(() => {
prisma = {
apiary: { findMany: jest.fn().mockResolvedValue([]) },
colony: { findMany: jest.fn().mockResolvedValue([]) },
queen: { findMany: jest.fn().mockResolvedValue([]) },
task: { findMany: jest.fn().mockResolvedValue([]) },
honeyBatch: { findMany: jest.fn().mockResolvedValue([]) },
};
service = new SearchService(prisma as unknown as PrismaService);
});
it('returns an empty array for a blank query', async () => {
const results = await service.search(' ', ALL_READ);
expect(results).toEqual([]);
expect(prisma.apiary.findMany).not.toHaveBeenCalled();
});
it('combines matches from every entity type', async () => {
prisma.colony.findMany.mockResolvedValue([
{ id: 'colony-1', code: 'V-001' },
]);
prisma.apiary.findMany.mockResolvedValue([
{ id: 'apiary-1', name: 'Heimstand' },
]);
const results = await service.search('V-001', ALL_READ);
expect(results).toEqual(
expect.arrayContaining([
{ type: 'Colony', id: 'colony-1', label: 'V-001' },
{ type: 'Apiary', id: 'apiary-1', label: 'Heimstand' },
]),
);
});
it('searches only the kinds of records the caller may read', async () => {
await service.search('V-001', new Set(['colonies:read']));
expect(prisma.colony.findMany).toHaveBeenCalled();
expect(prisma.apiary.findMany).not.toHaveBeenCalled();
expect(prisma.queen.findMany).not.toHaveBeenCalled();
expect(prisma.task.findMany).not.toHaveBeenCalled();
expect(prisma.honeyBatch.findMany).not.toHaveBeenCalled();
});
});