00.01 — Cartographie de l'écosystème

00.01 — Cartographie de l’écosystème#

L’écosystème Ocarina est constitué de six dépôts publics sur le compte GitHub mojo-molotov. Ils s’organisent en cinq rôles distincts qui se composent :

RôleDépôtForme
FrameworkocarinaBibliothèque Python publiée sur PyPI
Application sous test (SUT) publiqueigoristanSPA React + Vike déployée sur GitHub Pages
Backend de coordinationtests-workersAPI Vercel Edge + Upstash Redis
Suite d’exemplesocarina-example (canonique), ocarina-with-ai-example (IA)Projets Python e2e tournés vers l’Igoristan / CURA
Documentation publiqueocarina-holy-bookSite VitePress FR + EN, PDF, skills IA

Schéma d’ensemble (big picture)#

┌─────────────────────────────────────────────────────────────────────────────────┐
│                              OCARINA (BIG PICTURE)                              │
└─────────────────────────────────────────────────────────────────────────────────┘

   ┌──────────────────────────────┐         ┌──────────────────────────────────┐
   │  [PACKAGE]  ocarina          │ ──────► │  [PACKAGE]  ocarina-example      │
   │  Framework Python 3.14+      │   pip   │  Suite e2e contre l'Igoristan    │
   │  ROP / DSL / orchestration   │ install │  (Selenium + Firefox + Redis)    │
   └──────────────────────────────┘         └──────────────────────────────────┘
            │  │                                       │  ▲   │
            │  │                                       │  │  utilise
            │  │ pip install ocarina                   │  │  IGOR_API_KEY
            │  ▼                                       ▼  │   │
            │  ┌────────────────────────────────────┐  pilote │
            │  │ [PACKAGE]  ocarina-with-ai-example │  ┌──────┴──────────────────┐
            │  │ Suite e2e CURA Healthcare          │  │ [WEBSITE]  igoristan    │
            │  │ Claude Code, 99% machine           │  │ React 19 + Vike         │
            │  └────────────────────────────────────┘  │ GitHub Pages            │
            │              │                           └──────┬──────────────────┘
            │              │ pilote                           │ fetch OTP
            │              ▼                                  ▼
            │   ┌──────────────────────────────┐    ┌──────────────────────────────┐
            │   │ [WEBSITE]  CURA Healthcare   │    │ [BACKEND]  tests-workers     │
            │   │ Heroku, PHP open source      │    │ Vercel Edge Functions        │
            │   └──────────────────────────────┘    │ + Upstash Redis              │
            │                                       │ /api/otp, /otp-history,      │
            │                                       │ /api/corsicadex              │
            │                                       └──────────────────────────────┘
            │                                                 ▲
            │                                                 │ x-api-key
            │                                                 │ (IGOR_API_KEY ≡ API_SECRET)
            │                                                 │
            ▼
   ┌──────────────────────────────┐
   │  [DOCS]  ocarina-holy-book   │
   │  VitePress FR + EN + RU      │
   │  Skills IA, PDF, llms.txt    │
   │  GitHub Pages                │
   └──────────────────────────────┘
  1. ocarina → suites d’exemples : dépendance Python classique (pip install ocarina).
  2. Suites d’exemples → SUT (Igoristan / CURA) : pilotage navigateur via Selenium.
  3. Suite Igoristan ↔ tests-workers : appels HTTP (OTP, Corsicadex) ; le secret partagé IGOR_API_KEY (côté client) ≡ API_SECRET (côté serveur).

Responsabilités#

RôleContratBesoin
FrameworkDonner le DSL, l’orchestration, la pool de drivers, les reportersDoit être petit, auditable, sans dépendances cachées
SUT publicOffrir un terrain de jeu volontairement chaotique (random errors, OTP, formulaires capricieux)Hébergé en permanence sur GitHub Pages, sans backend lourd
Backend de coordinationCoordonner les workers sur l’OTP, fournir des données pour les tests parallèlesStateless côté code, l’état vit dans Redis (Upstash)
ExemplesDémontrer comment on utilise réellement Ocarina, à la fois sans IA (canonique) et avec IA (CURA)Doivent être publics, exécutables, complets, suivis en CI
DocumentationTransmettre le pourquoi, le comment, et l’usage IADoit exister en EN + FR + RU (tradition corso-russe), exposer des fichiers adaptés aux LLMs comme llms.txt, générer des PDF

Trois licences#

LicenceDépôts concernésPourquoi
MITocarina, ocarina-example, ocarina-with-ai-example, ocarina-holy-bookTout ce qui est code Python ou doc, transmissible « tel quel », souverain — voir ../11-independence/.
ISCtests-workersChoix par défaut du scaffold Vercel Edge, conservé.
(aucune licence)igoristanApplication démo publique, hébergée par l’auteur (à considérer comme WTFPL).

Points de friction volontaires entre dépôts#

Le design est volontairement asymétrique sur certains axes ; ces frictions sont des outils de test :

00.02 — Matrice stack / responsabilité / licence

00.02 — Matrice stack / responsabilité / licence#

DépôtRôleLangageVersionStack principaleStack outillageDistributionLicence
ocarinaFramework de test e2e navigateurPython3.14+hatchling, python-docx (seule dépendance d’exécution), selenium + playwright (dev seulement)ruff (ALL), mypy strict, pytest, hypothesis, pytest-mypy-plugins, syrupy, prysk (cram), allure-pytest, pre-commit, twine, buildPyPI, v1.1.10MIT
ocarina-exampleSuite canonique e2e contre l’IgoristanPython3.14+ocarina, selenium ≥ 4.40, python-dotenv, dogpile.cache, redisruff, mypy, pre-commitSource uniquementMIT
ocarina-with-ai-exampleSuite e2e CURA Healthcare co-écrite par Claude CodePython3.14+ocarina ≥ 1.0.3, selenium ≥ 4.40ruff, mypy, mypy-extensions, typing-extensions, pre-commitSource uniquementMIT
igoristanSUT public, application web volontairement chaotiqueTypeScript6.0.xReact 19.2, Vike 0.4.258 (SSG), Vite 7.3, TailwindCSS 4 (alpha), valibot, react-hook-form, react-dropzone, usehooks-ts, throttleit, lucide-react, uuidpnpm 11, Node 24, wireit (orchestrateur), eslint 9 (flat), prettier 3.5, husky, lint-staged, commitlint, commitizen, terser, rollup-plugin-visualizer, vite-plugin-compression, @qalisa/vike-plugin-sitemapGitHub Pages (/igoristan/)aucune
tests-workersBackend OTP / Corsicadex pour exercer la parallélisation et les tests avec appels APITypeScript5.9.3Vercel Edge Functions (runtime: "edge"), @upstash/redis, otplib, types next (NextRequest)pnpm 11.x, @vercel/node (types)Vercel : https://tests-workers.vercel.appISC
ocarina-holy-bookDocumentation publique + skills IA + PDFTypeScript6.0.xVitePress 2 alpha, theme @sugarat/theme, vue 3.5, pagefind, vitepress-plugin-image-optimizepnpm 11, Node 24, eslint 9, prettier 3.8, husky, lint-staged, commitlint, sass-embeddedGitHub Pages (/ocarina-holy-book/)MIT

Python early adopter#

Apport Python 3.12+Utilisation dans Ocarina
PEP 695 (class Foo[T]:)Signature générique partout (TestSuite[Driver], Watcher[Driver], Test[Driver], Result[T], Ok[T], Thunk[T], etc.)
PEP 695 type aliases (type X = ...)Tous les alias : type Result[T] = Ok[T] | Fail, type Effect = Callable[[], None], type Thunk[T] = Callable[[], T], type Mode = Literal[...]
TypeGuard (Python 3.10+)is_ok, is_fail, is_test_result_ok, is_test_result_fail, is_test_result_skipped
Self (3.11+)Retours fluides des POMs, des loggers, des invariants
@final (3.8+)Verrouille les classes critiques (Watcher, Test, TestCycle, ChainRunner, Ok, Fail, …)
Unpack (3.11+)Signature de HumanizedDriver côté ocarina-example
Protocol (3.8+)ScreenshotDriver, SupportsWrite[str]
Literal (3.8+)Toutes les énumérations (SeleniumCliStoreKeys, LOGGERS_CHOICES, Mode)
Never (3.11+)Signature de _SilentArgumentParser.error

Conséquence pratique : il existe un workflow CI dédié unstable_python_full_build.yml qui pousse Python 3.15-dev mensuellement (cron: "0 3 1 * *"), parce qu’Ocarina veut être prêt pour le prochain interpréteur dès qu’il sort. Voir ../10-cicd/02-ocarina-workflows.md

00.03 — Flux d'exécution global d'une campagne e2e

00.03 — Flux d’exécution global d’une campagne e2e#

Un python -u src/main.py … lancé sur l’une des deux suites (ocarina-example ou ocarina-with-ai-example) déclenche cette chaîne :

USER  ────────────────────────────────────────────────────────────────────
       python -u src/main.py --browser firefox --workers 3 [...]
              │
              ▼
       (1) parse CLI
           CliStoreSingleton.push(create_selenium_auto_cli_store())
              │
              ▼
       (2) build pool
           create_selenium_drivers_pool(max_size=N)
              │
              ▼
       (3) warm-up dépendances externes
           - Redis (ocarina-example)
           - Heroku dyno (ai-example, via curl --retry 6)
              │
              ▼
       (4) bootstrap(
              test_cycle  = create_e2e_test_cycle(drivers_pool),
              run_plugins = lambda results: run_plugins(
                                generate_docx_proof, generate_json_results,
                                exceptions_logger=...),
              post_exec   = pretty_print_results + sys.exit(1) si fail
           )
              │
OCARINA  ─────┼──────────────────────────────────────────────────────────
              │
              ▼
       (5) TestCycle.run_all (saturate_workers=True)
              ├─ smoke_tests_campaigns      [mode : fail-fast | wait-for-all]
              │     └─ TestCampaign.run_all
              │           └─ TestSuite.run (max_workers, saturate_workers)
              │                 └─ ThreadPoolExecutor → TestFlow.run
              │                       └─ pool.acquire() → TestExecutor.execute
              │                             ├─ setup()                  (optionnel)
              │                             ├─ watchers.start()         (daemon threads)
              │                             ├─ chain_runners            (DSL Railway)
              │                             ├─ watchers.stop()
              │                             └─ teardown()               (toujours)
              ├─ campaigns (main)
              │  (skippées si un smoke a fail)
              │
SUT   ────────┼────────────────────────────────────────────────────────────
              │
              ▼
       Selenium WebDriver ⇄ navigateur réel
       parfois : OTP HTTP GET + Redis (côté ocarina-example)
              │
              ▼
       (6) run_plugins(results)
              ├─ generate_docx_proof  (lit l'arbre de logs, fabrique des .docx)
              ├─ generate_json_results (sérialise results en .json)
              ├─ d'autres si déclarés (parallélisés via ThreadPoolExecutor)
              │
              ▼
       (6') post_exec(results)
              ├─ pretty_print_results (ANSI, hiérarchique)
              └─ has_test_cycle_failed → sys.exit(1) le cas échéant

Détail des étapes#

(1) Parse CLI#

create_selenium_auto_cli_store() détecte l’OS via platform.system() :

00.04 — Relations entre les six dépôts

00.04 — Relations entre les six dépôts#

Chaque arête du graphe est un contrat : un secret, une URL, un type de payload, ou une version. On les détaille ici, par paire.

ocarinaocarina-example#

DirectionMécanisme
ocarina → ocarina-examplepip install ocarina depuis PyPI
ocarina-example → ocarinaAucune (l’exemple ne pousse rien)

L’exemple écrit ses propres adapters au-dessus du framework :

  • lib/ext/ocarina/adapters/agnostic/act.py → ajoute le hook on_failure qui transforme une page d’erreur HTTP (titre matché par ERROR_PAGE_REGEX) en HttpErrorPageReachedError.
  • lib/ext/ocarina/adapters/agnostic/match_page.py → create_match_page(raised_exceptions=transient_errors).
  • lib/ext/ocarina/adapters/agnostic/env_getters.py → typed EnvGetters[_CredsKeys, _ValuesKeys].
  • lib/ext/ocarina/adapters/selenium/test_suite.py → fige max_retries_per_test=8, transient_errors=..., autoscreen_on_fail=True, propage --only/--exclude.
  • lib/ext/ocarina/adapters/selenium/test_campaign.py → fige max_workers=get_max_workers().

ocarinaocarina-with-ai-example#

  • pip install . ruff mypy mypy-extensions typing-extensions pre-commit car l’exemple IA a la dépendance fixée dans son pyproject.toml : ocarina>=1.0.3.
  • L’adapter act est minimaliste ici : pas de on_failure (CURA n’a pas de page d’erreur volontairement aléatoire à intercepter). Voir ../08-ai-example/
  • L’adapter create_drivers_pool est surchargé pour bâtir un Chrome clean (password manager off, leak detection off). Voir ../08-ai-example/06-data-gaps.md

ocarina-exampleigoristan#

DirectionContratDétail
ocarina-example → igoristanURLs publiquesTout passe par https://mojo-molotov.github.io/igoristan/<route>
igoristan → ocarina-exampleaucun (le SUT ne sait pas qu’il est testé) — 

src/constants/pages/ :