feat(search): recherche intelligente — parsing backend + pg_trgm fuzzy
- parseSearchTokens() côté backend : extrait type (async/openapi), version (v1, 1.2.3) et latestOnly depuis la chaîne brute - Recherche fuzzy via word_similarity (pg_trgm, seuil 0.5) + ILIKE - latestOnly automatique quand champ non vide sans version explicite - Suppression de parseSearch() dans BrowseComponent et CatalogComponent - Suppression de version/latestOnly de ApiListQuery (géré par le backend) - Activation de pg_trgm via CREATE EXTENSION dans SeederService - Tests unitaires Vitest pour parseSearchTokens (24 cas) Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
This commit is contained in:
156
back/src/modules/apis/apis.service.spec.ts
Normal file
156
back/src/modules/apis/apis.service.spec.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { ApisService } from './apis.service';
|
||||
import { ApiType } from '@datacat/shared';
|
||||
|
||||
/** Accès à la méthode privée pour les tests unitaires */
|
||||
function parse(raw: string) {
|
||||
return (ApisService.prototype as any).parseSearchTokens.call({}, raw) as {
|
||||
text: string;
|
||||
type?: ApiType;
|
||||
version?: string;
|
||||
latestOnly: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
describe('ApisService.parseSearchTokens()', () => {
|
||||
describe('chaîne vide', () => {
|
||||
it('retourne text vide, pas de type, pas de version, latestOnly=false', () => {
|
||||
expect(parse('')).toEqual({ text: '', type: undefined, version: undefined, latestOnly: false });
|
||||
});
|
||||
|
||||
it('gère les espaces seuls', () => {
|
||||
expect(parse(' ')).toEqual({ text: '', type: undefined, version: undefined, latestOnly: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('texte libre seul (sans version ni type)', () => {
|
||||
it('"auth" → latestOnly=true automatique', () => {
|
||||
expect(parse('auth')).toEqual({ text: 'auth', type: undefined, version: undefined, latestOnly: true });
|
||||
});
|
||||
|
||||
it('"paiement commande" → texte libre multi-mots, latestOnly=true', () => {
|
||||
expect(parse('paiement commande')).toEqual({
|
||||
text: 'paiement commande',
|
||||
type: undefined,
|
||||
version: undefined,
|
||||
latestOnly: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('détection du type', () => {
|
||||
it('"async" → ASYNCAPI', () => {
|
||||
const r = parse('async');
|
||||
expect(r.type).toBe('ASYNCAPI');
|
||||
});
|
||||
|
||||
it('"asyncapi" → ASYNCAPI', () => {
|
||||
expect(parse('asyncapi').type).toBe('ASYNCAPI');
|
||||
});
|
||||
|
||||
it('"openapi" → OPENAPI', () => {
|
||||
expect(parse('openapi').type).toBe('OPENAPI');
|
||||
});
|
||||
|
||||
it('"open" → OPENAPI', () => {
|
||||
expect(parse('open').type).toBe('OPENAPI');
|
||||
});
|
||||
|
||||
it('type seul → latestOnly=true (champ non vide, pas de version)', () => {
|
||||
expect(parse('async').latestOnly).toBe(true);
|
||||
});
|
||||
|
||||
it('mots de type ne font pas partie du texte libre', () => {
|
||||
expect(parse('auth async').text).toBe('auth');
|
||||
});
|
||||
});
|
||||
|
||||
describe('détection de version', () => {
|
||||
it('"v1" → version="1", latestOnly=false', () => {
|
||||
expect(parse('v1')).toEqual({ text: '', type: undefined, version: '1', latestOnly: false });
|
||||
});
|
||||
|
||||
it('"1.2" → version="1.2"', () => {
|
||||
expect(parse('1.2').version).toBe('1.2');
|
||||
});
|
||||
|
||||
it('"v1.2.3" → version="1.2.3" (préfixe v supprimé)', () => {
|
||||
expect(parse('v1.2.3').version).toBe('1.2.3');
|
||||
});
|
||||
|
||||
it('"1.0.0" → version exacte', () => {
|
||||
expect(parse('1.0.0').version).toBe('1.0.0');
|
||||
});
|
||||
|
||||
it('version présente → latestOnly=false', () => {
|
||||
expect(parse('auth v1').latestOnly).toBe(false);
|
||||
});
|
||||
|
||||
it('numéro de version ne fait pas partie du texte libre', () => {
|
||||
expect(parse('auth v1').text).toBe('auth');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mot-clé "latest"', () => {
|
||||
it('"latest" seul → latestOnly=true, pas de texte', () => {
|
||||
expect(parse('latest')).toEqual({ text: '', type: undefined, version: undefined, latestOnly: true });
|
||||
});
|
||||
|
||||
it('"latest" + type → latestOnly=true + type détecté', () => {
|
||||
const r = parse('latest async');
|
||||
expect(r.latestOnly).toBe(true);
|
||||
expect(r.type).toBe('ASYNCAPI');
|
||||
});
|
||||
|
||||
it('"latest" + version → les deux flags actifs', () => {
|
||||
const r = parse('latest v1');
|
||||
expect(r.latestOnly).toBe(true);
|
||||
expect(r.version).toBe('1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('combinaisons', () => {
|
||||
it('"auth async" → texte=auth, type=ASYNCAPI, latestOnly=true', () => {
|
||||
expect(parse('auth async')).toEqual({
|
||||
text: 'auth',
|
||||
type: 'ASYNCAPI',
|
||||
version: undefined,
|
||||
latestOnly: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('"auth async v1" → texte=auth, type=ASYNCAPI, version=1, latestOnly=false', () => {
|
||||
expect(parse('auth async v1')).toEqual({
|
||||
text: 'auth',
|
||||
type: 'ASYNCAPI',
|
||||
version: '1',
|
||||
latestOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('"auth 1.0.0" → texte=auth, version=1.0.0, latestOnly=false', () => {
|
||||
expect(parse('auth 1.0.0')).toEqual({
|
||||
text: 'auth',
|
||||
type: undefined,
|
||||
version: '1.0.0',
|
||||
latestOnly: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('"openapi latest" → type=OPENAPI, latestOnly=true', () => {
|
||||
expect(parse('openapi latest')).toEqual({
|
||||
text: '',
|
||||
type: 'OPENAPI',
|
||||
version: undefined,
|
||||
latestOnly: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('insensible à la casse pour les mots-clés', () => {
|
||||
const r = parse('ASYNC Auth LATEST');
|
||||
expect(r.type).toBe('ASYNCAPI');
|
||||
expect(r.latestOnly).toBe(true);
|
||||
expect(r.text).toBe('Auth');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user