Skip to content

Tests ​

Tous les tests tournent avec Vitest. Les tests unitaires n’ont besoin que de Node.js ; les tests d’intégration du serveur exigent un vrai PostgreSQL, et quelques-uns un stockage objet compatible S3. Aucun test n’appelle une vraie API externe : ni fournisseur de mail, ni service tiers, ni modèle de langage.

Projets Vitest ​

Le vitest.config.ts racine déclare un projet Vitest par paquet : une seule commande lance tout, et une option en sélectionne une partie.

ProjetFichiersEnvironnement
workflow, api-types, credentials, connectors, mirror, nodes, engine, tools, serverpackages/<nom>/src/**/*.test.tsNode.js. Délai des hooks de 60 secondes, pour les suites d’intégration qui préparent une base.
uipackages/ui/src/**/*.test.tsjsdom, avec le plugin Vue.
docsapps/docs/scripts/**/*.test.tsNode.js. Tests du générateur de documentation.

Les fichiers de test sont placés à côté du code qu’ils testent, avec le suffixe .test.ts. Les tests du serveur qui exigent PostgreSQL s’appellent *.integration.test.ts ; ils suivent le même motif et appartiennent au projet server.

Lancer les tests ​

Depuis la racine du dépôt :

sh
pnpm test                                   # tous les projets
pnpm test --project nodes                   # un projet
pnpm test --project server --project engine # plusieurs projets
pnpm test mail-flag                         # les fichiers dont le chemin contient « mail-flag »
pnpm test --project nodes -t "mail.flag"    # les tests dont le nom correspond
pnpm test:watch --project workflow          # mode surveillance
pnpm test --coverage                        # rapport de couverture (texte et HTML)

pnpm test lance vitest run : toutes les options de Vitest sont disponibles à sa suite. Certains paquets dépendent de la sortie compilée d’autres paquets : lancez pnpm build une fois après le clonage, et après avoir modifié un paquet que d’autres importent.

La base PostgreSQL de test ​

Les tests d’intégration ne tournent que si TEST_DATABASE_URL est renseignée. Sans elle, ils sont sautés avec un message explicite, jamais verts en silence. Démarrez un PostgreSQL 16 jetable sur un port qui ne heurte pas la pile de développement, puis faites pointer la variable dessus :

sh
docker run -d --rm --name test-pg \
  -e POSTGRES_PASSWORD=test -p 55432:5432 postgres:16-alpine

TEST_DATABASE_URL=postgres://postgres:test@localhost:55432/postgres pnpm test --project server

TEST_DATABASE_URL désigne un serveur, pas la base qu’utilisent les tests. Chaque fichier de test d’intégration reçoit sa propre base sur ce serveur, nommée d’après le fichier (isolatedDatabaseUrl dans packages/server/src/db/testing.ts) :

  • la base est créée si elle manque, et jamais supprimée ;
  • au début du fichier, toutes ses tables sont vidées, sauf celle qui enregistre les migrations appliquées ;
  • createTestDatabase applique ensuite les vraies migrations du produit avec l’exécuteur du produit : les tests vérifient donc le schéma livré.

Les bases survivent donc d’un passage à l’autre, et le second passage saute les migrations déjà appliquées. Supprimer le conteneur supprime tout. Ne faites jamais pointer TEST_DATABASE_URL vers la base de votre pile de développement.

La variable n’est qu’un interrupteur du harnais de test : aucun binaire livré ne la lit. Le code de test peut la lire directement, alors que le code du produit ne lit l’environnement que par le module de configuration.

Stockage objet dans les tests ​

La suite de l’adaptateur S3 ne tourne que si TEST_S3_ENDPOINT est renseignée. La suite des pièces jointes d’exécution utilise MinIO quand la variable est renseignée, et un stockage en mémoire sinon. Pour les lancer contre MinIO :

sh
docker run -d --rm --name test-minio \
  -e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin \
  -p 59000:9000 minio/minio:latest server /data

TEST_S3_ENDPOINT=http://localhost:59000 pnpm test --project server

Les suites créent elles-mêmes leur bucket. Renseignez les deux variables pour lancer le projet server comme le fait la CI.

Parallélisme et --maxWorkers ​

Vitest exécute les fichiers de test dans des workers parallèles, et tous les fichiers d’intégration partagent le même serveur PostgreSQL. Quand plusieurs personnes ou plusieurs passages utilisent le même serveur de test, ou sur une petite machine, limitez le nombre de workers :

sh
pnpm test --project server --maxWorkers=4

Un hook qui expire (Hook timed out) dans un fichier d’intégration traduit presque toujours une contention sur la base partagée, pas un défaut du test. Regardez le harnais de packages/server/src/db/testing.ts avant de regarder le test.

Tests de nœuds ​

Chaque nœud du catalogue a un fichier de test à côté de lui dans packages/nodes/src/catalog/. Les fonctions de packages/nodes/src/testing/context.ts construisent un contexte complet sans aucun serveur :

FonctionRôle
runNode(definition, input)Applique les défauts du nœud comme le moteur, puis appelle execute.
createContext(input)Construit seulement le contexte, pour des tests plus fins.
bind(JETON, doublure)Branche un service dans le contexte.
createMailService, createHttpService, createAttachmentService, createLlmService, createProfileServiceDes doublures qui enregistrent les appels et rendent des résultats programmés ou un échec programmé (failWith).
emailFixture(patch)Le mail déclencheur, fourni par défaut ; passez email: undefined pour une exécution sans mail.
createSignal()Un signal que vous pouvez annuler, pour tester l’annulation.

Le contexte par défaut utilise la clé d’idempotence step-42, l’exécution { id: 'exec-42', workflowId: 'wf-42' }, simulated: false et la langue française ; chacun se surcharge.

En plus des tests propres à chaque nœud, packages/nodes/src/catalog/catalog.test.ts vérifie tout le catalogue : la liste exacte des clés de nœuds et leurs effets, des métadonnées complètes en français et en anglais, une catégorie de palette explicite, des alias de recherche, des paramètres et options libellés, des défauts valides, des ports de sortie calculés sans lever d’exception, des ports d’entrée conformes à la nature du nœud, et un execute qui rend toujours une promesse. Ajouter un nœud oblige à mettre à jour son inventaire. Les garde-fous i18n sont dans packages/nodes/src/i18n/.

sh
pnpm test --project nodes

Tests de l’interface ​

Les tests de l’interface tournent dans le projet ui, sous jsdom, avec @vue/test-utils pour monter les composants. Les garde-fous i18n de l’interface sont aussi des tests : packages/ui/src/i18n/hardcoded.test.ts (aucun texte en dur) et packages/ui/src/i18n/i18n.test.ts (mêmes clés en français et en anglais). Lancez-les après toute modification de l’interface :

sh
pnpm test --project ui

Tests de la documentation ​

Le générateur de documentation a ses propres tests, dans le projet docs :

sh
pnpm test --project docs

Ils testent les fonctions du générateur. La complétude de la documentation (une source pour chaque nœud, un guide pour chaque connexion, un frontmatter valide, aucun mot interdit) est vérifiée par le générateur lui-même quand il tourne :

sh
pnpm --filter @manko/docs generate

Il lit les paquets compilés : lancez d’abord pnpm build.

L’IA dans les tests ​

Les tests n’appellent jamais un vrai modèle de langage.

  • Les tests de nœuds utilisent createLlmService, une doublure qui enregistre la requête (le prompt est ce que vérifient la plupart des tests de nœuds IA) et rend le texte ou le JSON décidé par le test.
  • Le module LLM du serveur est testé contre un client de fournisseur et un magasin de clés en mémoire (packages/server/src/llm/testing.ts), erreurs, quotas, réparation de JSON et coûts compris.
  • Pendant un essai d’un workflow, le service LLM est remplacé par un service simulé : aucun appel réseau, aucun usage enregistré, et une sortie déterministe (un texte fixe, ou l’objet minimal qui satisfait le schéma JSON demandé).

Le seul code qui appelle un vrai modèle est le banc d’essai de l’analyseur de boîte, hors de la suite de tests. Il n’utilise qu’OpenAI, exige OPENAI_API_KEY et s’arrête sans elle ; --dry-run vérifie ses jeux de données sans aucun appel :

sh
pnpm bench:analyzer --dry-run
OPENAI_API_KEY=… pnpm bench:analyzer

Il utilise aussi le PostgreSQL de test jetable (TEST_DATABASE_URL, par défaut celui du port 55432). N’ajoutez ni test ni outil qui appelle l’API d’Anthropic.

Tests de bout en bout ​

Le dépôt n’a pas de suite de bout en bout dans un navigateur. La vérification la plus proche est le test fumigatoire de la CI : il construit et démarre la pile complète en conteneurs avec docker compose up, puis exige que /healthz et /readyz répondent.

Ce qu’exige la CI ​

La CI renseigne TEST_DATABASE_URL et TEST_S3_ENDPOINT vers de vrais services PostgreSQL 16 et MinIO, lance pnpm test avec un rapport JSON, puis échoue si :

  • un test a été sauté : en CI, toutes les dépendances sont là, donc un test sauté signale une variable du harnais mal câblée ;
  • le nombre de tests passe sous un plancher (MIN_EXPECTED_TESTS), ce qui détecte la disparition d’une suite entière du motif de fichiers d’un projet.

Chaque nuit, un job distinct rejoue la suite complète trois fois de suite sur la même base, et indique pour chaque passage les fichiers en échec et les hooks qui ont expiré. Un fichier qui n’échoue qu’à un seul des trois passages est instable.

Règles d’écriture des tests ​

  • Aucune vraie API externe : utilisez des doublures de services, des faux connecteurs, ou un serveur HTTP local sur le port 0.
  • Des tests déterministes : injectez la Clock (de @manko/engine) au lieu de lire l’heure courante dans le code métier, utilisez des graines fixes, et attendez une condition plutôt que de dormir.
  • Un bogue corrigé s’accompagne d’un test qui le reproduit.
  • Une suite qui exige une vraie dépendance utilise describe.skipIf(!enabled) avec un nom de suite qui dit pourquoi elle est sautée, jamais un saut muet.