import { describe, expect, it } from 'vitest' import { FILE_STORAGE_CAPABILITY, getCapability, matchCapabilityAttribute, matchCapabilityTrigger, PLATFORM_CAPABILITIES, } from '../capability-catalog.js' describe('capability-catalog integrity', () => { it('keys are unique and kebab-case', () => { const keys = PLATFORM_CAPABILITIES.map(c => c.key) expect(new Set(keys).size).toBe(keys.length) for (const key of keys) expect(key).toMatch(/^[a-z0-9]+(-[a-z0-9]+)*$/) }) it('every entry carries label, non-empty triggers, seam and useInstead', () => { for (const c of PLATFORM_CAPABILITIES) { expect(c.label.length, c.key).toBeGreaterThan(0) expect(c.triggers.length, c.key).toBeGreaterThan(0) expect(c.seam.length, c.key).toBeGreaterThan(0) expect(c.useInstead.length, c.key).toBeGreaterThan(0) } }) it('references, when present, point under templates/skills (repo-relative, forward slashes)', () => { for (const c of PLATFORM_CAPABILITIES) { if (!c.reference) continue expect(c.reference, c.key).not.toMatch(/\\|^\/|^[A-Za-z]:/) expect(c.reference, c.key).toMatch(/\.md$/) } }) it('file-storage is the flagship entry (the incident this catalogue closes)', () => { expect(FILE_STORAGE_CAPABILITY.key).toBe('file-storage') expect(FILE_STORAGE_CAPABILITY.useInstead).toContain('IFileStorageService') expect(FILE_STORAGE_CAPABILITY.attributeTriggers).toContain('varbinary') expect(getCapability('file-storage')).toBe(FILE_STORAGE_CAPABILITY) }) }) describe('matchCapabilityTrigger — entity / section / tab names', () => { it.each([ ['Document', 'file-storage'], ['Documents', 'file-storage'], ['document', 'file-storage'], ['Pièce jointe', 'file-storage'], ['Pièces jointes', 'file-storage'], ['pieces jointes', 'file-storage'], // accent-insensitive ['Justificatifs', 'file-storage'], ['GED', 'file-storage'], ['Téléversement', 'file-storage'], ])('whole-token hit: %s → %s', (candidate, key) => { expect(matchCapabilityTrigger(candidate)?.key).toBe(key) }) it.each([ // The incident shape: "{Parent}Documents" attachment collections. ['InteractionDocument', 'file-storage'], ['InteractionDocuments', 'file-storage'], ['crm_InteractionDocuments', 'file-storage'], ['ProjectAttachments', 'file-storage'], ['ContratPiècesJointes', 'file-storage'], ['ProfilePhoto', 'file-storage'], ])('trailing-word hit: %s → %s', (candidate, key) => { expect(matchCapabilityTrigger(candidate)?.key).toBe(key) }) it.each([ 'Documentation', // single word — never substring 'Documentaliste', 'Filet', 'Scanner', 'UserStory', 'MediaCampaign', // trailing word is not a trigger 'Interaction', 'Contract', ])('non-hit: %s', (candidate) => { expect(matchCapabilityTrigger(candidate)).toBeUndefined() }) it('other capabilities resolve too', () => { expect(matchCapabilityTrigger('Recherche globale')?.key).toBe('global-search') expect(matchCapabilityTrigger('SearchIndex')?.key).toBe('global-search') expect(matchCapabilityTrigger('OutgoingEmail')?.key).toBe('email-sending') expect(matchCapabilityTrigger('Bookable')?.key).toBe('time-entry-refs') }) it('does not double-flag the PLATFORM_HR_ENTITIES names (CODE-005 owns those)', () => { for (const hrName of ['TimeEntry', 'Timesheet', 'Imputation', 'Pointage']) { expect(matchCapabilityTrigger(hrName), hrName).toBeUndefined() } }) }) describe('matchCapabilityAttribute — attribute name or declared type', () => { it.each([ ['binary', 'file-storage'], ['Binary', 'file-storage'], ['varbinary', 'file-storage'], ['blob', 'file-storage'], ['byte[]', 'file-storage'], ['filestream', 'file-storage'], ['fileContent', 'file-storage'], ['ContenuFichier', 'file-storage'], ])('hit: %s → %s', (candidate, key) => { expect(matchCapabilityAttribute(candidate)?.key).toBe(key) }) it.each(['string', 'decimal', 'guid', 'datetime', 'contenuValidé', 'imageUrl'])( 'non-hit: %s', (candidate) => { expect(matchCapabilityAttribute(candidate)).toBeUndefined() }, ) })