qwen_2.5_model / src /modules /search /search.controller.spec.ts
Muhammad Noman
Deploy OpenWA to Hugging Face Spaces
46252cd
Raw
History Blame Contribute Delete
3.04 kB
import { BadRequestException, NotImplementedException } from '@nestjs/common';
import { SearchController } from './search.controller';
import { SearchService } from './search.service';
import { SearchQueryDto } from './dto/search-query.dto';
import type { ApiKey } from '../auth/entities/api-key.entity';
import type { SearchResults } from './search.types';
describe('SearchController', () => {
const search = jest.fn();
const ctrl = new SearchController({ search } as unknown as SearchService);
const ok = { hits: [], total: 0, tookMs: 1, provider: 'builtin-fts' } satisfies SearchResults;
beforeEach(() => {
search.mockReset();
});
it('throws 400 when q is empty', async () => {
const dto: SearchQueryDto = { q: '' };
await expect(ctrl.search(dto, undefined)).rejects.toBeInstanceOf(BadRequestException);
});
it('throws 400 when q is only whitespace', async () => {
// @IsNotEmpty() on the DTO passes for ' ' (non-empty), so the controller's own trim guard is
// what rejects whitespace-only q β€” this test pins that guard in place.
const dto: SearchQueryDto = { q: ' ' };
await expect(ctrl.search(dto, undefined)).rejects.toBeInstanceOf(BadRequestException);
expect(search).not.toHaveBeenCalled();
});
it('returns results and forwards the key allowedSessions as callerSessionIds', async () => {
search.mockResolvedValue(ok);
const apiKey = { allowedSessions: ['s1', 's2'] } as unknown as ApiKey;
const dto: SearchQueryDto = { q: 'hello', limit: 5 };
const res = await ctrl.search(dto, apiKey);
expect(search).toHaveBeenCalledWith(dto, ['s1', 's2']);
expect(res).toBe(ok);
});
it('passes undefined (no scope) for an unrestricted key (null allowedSessions)', async () => {
search.mockResolvedValue(ok);
// A null/empty allowlist (e.g. ADMIN) sees all sessions β€” mirrors GET /webhooks behavior.
const apiKey = { allowedSessions: null } as unknown as ApiKey;
const dto: SearchQueryDto = { q: 'hello' };
await ctrl.search(dto, apiKey);
expect(search).toHaveBeenCalledWith(dto, undefined);
});
it('derives callerSessionIds only from the key, never from the query (anti-smuggling)', async () => {
search.mockResolvedValue(ok);
// `sessionIds` is not a field on SearchQueryDto, so it cannot be expressed in the query at all;
// the global ValidationPipe (forbidNonWhitelisted) would reject it, and SearchService clobbers
// any sessionIds at the provider boundary. The controller itself derives scope solely from the
// key β€” here there is no key β†’ undefined.
const dto: SearchQueryDto = { q: 'hello' };
await ctrl.search(dto, undefined);
expect(search).toHaveBeenCalledWith(dto, undefined);
});
it('propagates 501 (NotImplementedException) from the service when no provider is active', async () => {
search.mockRejectedValue(new NotImplementedException('none'));
await expect(ctrl.search({ q: 'x' }, undefined)).rejects.toBeInstanceOf(NotImplementedException);
});
});