View a markdown version of this page

Guide de test des plugins - Amazon Inspector

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Guide de test des plugins

Les auteurs de plugins écrivent et testent les plugins Lua entièrement dans Lua. Aucune chaîne d'outils Go n'est requise. Les tests sont colocalisés avec le plugin ci-dessous init_test.lua et exécutés via la inspector-sbomgen plugin test commande.

Pour la création générale de plugins, consultez leGuide du développeur de plugins. Pour le catalogue complet des fonctions (y compris l'testing.*API), consultez leRéférence de l'API du plugin.

-- init_test.lua (next to init.lua) function test_discovers_curl_version() local result = testing.scan_directory("_testdata/include/curl") testing.assert_equals(1, #result.findings) testing.assert_equals("libcurl", result.findings[1].name) testing.assert_equals("8.14.1", result.findings[1].version) end
# Run every test under a plugin directory inspector-sbomgen plugin test --path ./my-plugins # Verbose output — show each test name and result inspector-sbomgen plugin test --path ./my-plugins -v

Quick Start

1. Création d'un fichier de test

Placez à init_test.lua côté de celui de votre plugin init.lua :

my-plugin/ ├── discovery/cross-platform/extra-ecosystems/curl/ │ ├── init.lua │ ├── init_test.lua │ └── _testdata/ │ └── include/curl/curlver.h

2. Écrire des fonctions de test

Toute fonction globale commençant par test_ est découverte et exécutée :

function test_finds_libcurl() local result = testing.scan_directory("_testdata/include/curl") testing.assert_equals(1, #result.findings) testing.assert_equals("libcurl", result.findings[1].name) end function test_no_findings_for_empty_dir() local result = testing.scan_directory("_testdata/empty") testing.assert_equals(0, #result.findings) end

3. Exécuter des tests

inspector-sbomgen plugin test --path ./my-plugin

Disposition du répertoire

Structure du plugin

Les fichiers de test et les données de test sont situés au même endroit que le plugin :

my-plugins/ ├── discovery/ │ └── cross-platform/ │ └── extra-ecosystems/ │ └── curl/ │ ├── init.lua # plugin source │ ├── init_test.lua # test file │ └── _testdata/ # test fixtures │ ├── include/curl/curlver.h │ └── binaries/unix/curl ├── collection/ │ └── cross-platform/ │ └── extra-ecosystems/ │ └── curl-installation/ │ ├── init.lua │ └── init_test.lua

Nommage des fichiers de test

  • Par défaut : à init_test.lua côté du plugin init.lua

  • Plusieurs fichiers de test par plugin : tout fichier correspondant *_test.lua est découvert

  • Exemples : init_test.lua, parsing_test.lua, discovery_test.lua

Données de test : _testdata/

Les données de test se _testdata/ trouvent à côté du plugin. Le trait de soulignement principal est une convention qui permet de séparer visuellement les appareils de la source du plugin ; la plugin test commande n'est pas abordée _testdata/ lors de la recherche de *_test.lua fichiers, de sorte que les appareils ne sont jamais confondus avec des fichiers de test.

Les fichiers de test font référence aux appareils avec des chemins relatifs :

local result = testing.scan_directory("_testdata/include/curl")

Les chemins sont résolus par rapport au répertoire contenant le fichier de test.

API testing.*

Fonctions de numérisation

Chaque fonction de numérisation crée un artefact, exécute le pipeline de découverte → de collecte du plugin et renvoie les résultats. L'auteur du test ne crée jamais manuellement d'artefacts, de bus d'événements ou de registres.

-- Scan a directory of test fixtures (most common) local result = testing.scan_directory("_testdata/curl") -- Alias for scan_directory (archives use the same backend) local result = testing.scan_archive("_testdata/app.tar.gz") -- Scan as a localhost artifact local result = testing.scan_localhost("_testdata/curl") -- Scan a compiled binary local result = testing.scan_binary("_testdata/binaries/curl") -- Scan a mounted volume local result = testing.scan_volume("_testdata/volume-root") -- Scan a container image tarball local result = testing.scan_container("_testdata/images/alpine.tar")

Chaque fonction de numérisation :

  • Crée un nouvel artefact pour chaque appel (aucune fuite d'état entre les appels)

  • Charge uniquement la paire découverte et collection du plugin actuel

  • Renvoie un tableau de résultats

Tableau des résultats

result.findings -- array of finding tables result.findings[1].name -- package name (string) result.findings[1].version -- package version (string) result.findings[1].purl -- package URL (string) result.findings[1].component_type -- component type (string) result.findings[1].properties -- table<string, string> result.findings[1].children -- array of nested finding tables (same shape)

Assertions

-- Equality testing.assert_equals(expected, actual, message?) testing.assert_not_equals(expected, actual, message?) -- Truthiness testing.assert_true(value, message?) testing.assert_false(value, message?) -- Nil checks testing.assert_nil(value, message?) testing.assert_not_nil(value, message?) -- String testing.assert_contains(haystack, needle, message?) testing.assert_matches(string, pattern, message?) -- Tables testing.assert_length(table, expected_length, message?) -- Control flow testing.fail(message) -- immediately fail the current test testing.skip(message) -- skip the current test (not a failure)

sbomgen.*API standard

L'sbomgen.*API complète (E/S de fichiers, regex, informations système, journalisation, etc.) est disponible dans les fichiers de test, comme dans les plugins de production. Cependant, sbomgen.* les fonctions qui nécessitent un artefact (par exemple,sbomgen.read_file()) ne fonctionnent que dans un testing.scan_* rappel ; elles ne sont pas disponibles au niveau supérieur d'une fonction de test.

Exécution de tests

# Run all tests under a plugin directory inspector-sbomgen plugin test --path ./my-plugins # Filter by regex pattern inspector-sbomgen plugin test --path ./my-plugins --run curl # Verbose output (show each test name and result) inspector-sbomgen plugin test --path ./my-plugins -v # Stop on the first failing test inspector-sbomgen plugin test --path ./my-plugins --fail-fast

L'--pathindicateur accepte un répertoire racine du plugin (contenant discovery/ and/or collection/) ou un répertoire unique de l'écosystème (détecté automatiquement). La commande sort d'une valeur différente de zéro en cas d'échec d'un test.

Format de sortie

Avec-v, chaque test imprime une === RUN ligne et une ligne de résultat (--- PASS--- FAIL, ou--- SKIP). Sans-v, seuls les tests qui échouent sont imprimés. Une ligne récapitulative est imprimée à la fin :

=== RUN curl/discovery/init_test/test_discovers_libcurl_header --- PASS: curl/discovery/init_test/test_discovers_libcurl_header (0.04s) === RUN curl/discovery/init_test/test_discovers_curl_binary_unix --- PASS: curl/discovery/init_test/test_discovers_curl_binary_unix (0.04s) === RUN curl/discovery/init_test/test_no_findings_for_unrelated_files --- PASS: curl/discovery/init_test/test_no_findings_for_unrelated_files (0.04s) ok 3 tests passed

Tester les aides aux plugins

Le fichier de test est chargé dans la même machine virtuelle Lua que celle du init.lua plugin. Les fonctions globales définies dans le plugin sont appelables à partir de tests. Pour tester les fonctions d'assistance, exposez-les sous forme de variables globales ou dans une table de modules :

-- init.lua M = {} function M.parse_version(raw) return string.match(raw, "(%d+%.%d+%.%d+)") end function collect(file_path) local ver = M.parse_version(...) -- ... end
-- init_test.lua function test_parse_version_extracts_semver() testing.assert_equals("1.2.3", M.parse_version("curl/1.2.3")) end function test_parse_version_returns_nil_for_garbage() testing.assert_nil(M.parse_version("not-a-version")) end

Les fonctions déclarées local dans ne init.lua sont pas visibles dans le fichier de test. Il s'agit du scoping Lua standard.

Comportement et invariants

VM partagée, état partagé

Les fonctions de test au sein d'un même fichier partagent une machine virtuelle Lua. Une variable globale définie dans une test_* fonction est visible pour les fonctions suivantes. Si deux fonctions définissent la même valeur globale, la seconde remplace la première. Chaque test doit être autonome et ne pas dépendre de l'état des autres tests.

Ordre d'exécution non déterministe

Les fonctions de test sont découvertes en itérant la table globale de Lua, qui utilise un ordre basé sur le hachage. Il n'est pas garanti que les tests s'exécutent dans l'ordre dans lequel ils ont été définis. N'écrivez pas de tests qui dépendent de l'ordre d'exécution.

Artefact frais par appel de numérisation

Chaque appel à testing.scan_directory() (ou toute autre fonction de numérisation) crée un artefact complètement nouveau. Aucun état n'est transmis entre les appels de scan au sein d'un test ou entre les tests.

Chargement du plugin

Les plugins sont chargés une fois par test, et non une fois par fichier de test. Le lanceur de tests charge tous les plugins à partir du système de fichiers fourni, puis associe chaque fichier de test à la machine virtuelle de plug-in correspondante par phase, plate-forme, catégorie et écosystème.

Comportement d'assertion

Lorsqu'une assertion échoue, l'échec est enregistré mais la fonction de test continue de s'exécuter. Les assertions et instructions suivantes sont toujours exécutées. Si plusieurs assertions échouent au cours du même test, l'échec le plus récent est le message indiqué dans le résumé ; les échecs antérieurs sont remplacés. Pour arrêter un test lors de son premier échec, revenez de la fonction après l'échec de l'assertion (ou utilisez-la testing.fail() dans un conditionnel).

Limitations

  • localles fonctions ne sont pas testables. Seules les fonctions globales de init.lua sont visibles dans le fichier de test. Exposez les assistants via une table de modules s'ils ont besoin d'être testés.

  • Aucun sbomgen.* fichier I/O en dehors des appels de numérisation. Des fonctions comme sbomgen.read_file() celles-ci nécessitent un contexte d'artefact, qui n'existe que dans testing.scan_* les appels.

  • Aucun crochet lié au cycle de vie. Il n'y a pas before_eachafter_each,setup, outeardown. Chaque fonction de test gère son propre état.

  • Aucun délai d'attente pour le test. Une fonction de test qui tourne en boucle indéfiniment bloquera le coureur.

  • Aucun rapport de couverture. Il n'existe aucun moyen de mesurer quelles lignes init.lua ont été exercées.

  • Pas de points de repère. Le framework de test ne prend pas en charge les benchmarks de performance.

Responsabilités du développeur

Lors de l'écriture d'un nouveau plugin Lua

  • Créez à init_test.lua côté de votre init.lua

  • Créez _testdata/ avec un minimum de fonctionnalités qui mettent en œuvre la logique de votre plugin

  • Rédiger test_* des fonctions couvrant : la détection réussie, l'extraction de version, les cas extrêmes et les scénarios de non-correspondance

  • Exécutez les tests localement avant de les soumettre

Directives relatives aux données de test

  • Réduisez le nombre de fixations au minimum : utilisez le plus petit fichier qui exerce le comportement

  • Évitez de valider de gros fichiers binaires _testdata/ alors qu'un petit dispositif de texte suffirait

  • Chaque plugin _testdata/ doit être autonome : aucune référence à des fichiers situés en dehors du répertoire du plugin