#Requires AutoHotkey v2.0 ; Tests autonomes de PromptKey (architecture §22) — à lancer sous Windows : ; AutoHotkey64.exe tests\run_tests.ahk ; Sortie : lignes OK / ÉCHEC sur la console (ou dans le fichier passé en premier argument), ; code de sortie 0 si tout passe, 1 sinon. Aucune GUI, aucune injection. #SingleInstance Off #Include ..\lib\Util.ahk #Include ..\lib\Json.ahk #Include ..\lib\Frontmatter.ahk #Include ..\lib\VariableEngine.ahk #Include ..\lib\PromptStore.ahk #Include ..\lib\Search.ahk global failures := 0 global outFile := A_Args.Length > 0 ? A_Args[1] : "" Report(ok, name, detail := "") { global failures, outFile if (!ok) failures++ line := (ok ? "OK " : "ÉCHEC") " " name (detail != "" ? " — " detail : "") "`n" if (outFile != "") FileAppend(line, outFile, "UTF-8") else FileAppend(line, "*") } Check(cond, name, detail := "") { Report(cond, name, detail) } ; ---------- Util : normalisation ---------- Check(Util.Normalize("Résumé ÉÉ") = "resume ee", "Normalize accents") Check(Util.Slugify("Revue de code !") = "revue-de-code", "Slugify") ; ---------- Frontmatter : analyse ---------- sample := " ( --- id: test-id-1 title: Résumer en 5 points tags: [résumé, synthèse, court] favorite: true pinned: 2 variables: - name: ton label: Ton de la réponse type: enum choices: [neutre, formel, amical] default: neutre --- Résume avec un ton {{ton}}. Texte : {{selection}} )" parsed := Frontmatter.Parse(sample) Check(parsed["errors"].Length = 0, "Frontmatter sans erreur", parsed["errors"].Length ? parsed["errors"][1] : "") Check(parsed["meta"]["title"] = "Résumer en 5 points", "Frontmatter title") Check(parsed["meta"]["tags"].Length = 3, "Frontmatter tags (3)") Check(parsed["meta"]["favorite"] = 1, "Frontmatter favorite booléen") Check(parsed["meta"]["variables"].Length = 1, "Frontmatter 1 variable") Check(parsed["meta"]["variables"][1]["choices"].Length = 3, "Frontmatter choices (3)") Check(InStr(parsed["body"], "{{selection}}") > 0, "Frontmatter corps conservé") ; Fichier sans en-tête : accepté, corps = tout. plain := Frontmatter.Parse("Juste un texte.") Check(plain["hasHeader"] = false && plain["body"] = "Juste un texte.", "Sans en-tête accepté") ; Erreur : en-tête jamais fermé. bad := Frontmatter.Parse("---`nid: x`ntitle: y`ncorps sans fermeture") Check(bad["errors"].Length > 0, "En-tête non fermé = erreur signalée") ; Aller-retour sérialisation. item := PromptItem("test.md", "Général", parsed) ser := Frontmatter.Serialize(item.Meta, item.Body) reparsed := Frontmatter.Parse(ser) Check(reparsed["errors"].Length = 0 && reparsed["meta"]["title"] = "Résumer en 5 points", "Aller-retour Serialize/Parse") ; ---------- Variables ---------- ctx := Map("selection", "TEXTE SÉLECTIONNÉ", "clipboardText", "PRESSE", "process", "chrome.exe") defs := VariableEngine.ManualVars(item) Check(defs.Length = 1 && defs[1]["name"] = "ton", "ManualVars : ton seulement (selection est automatique)") resolved := VariableEngine.Resolve(item.Body, Map("ton", "formel"), ctx) Check(InStr(resolved["text"], "ton formel") > 0, "Resolve remplace {{ton}}") Check(InStr(resolved["text"], "TEXTE SÉLECTIONNÉ") > 0, "Resolve remplace {{selection}}") Check(resolved["unresolved"].Length = 0, "Resolve sans non-résolue") withCursor := VariableEngine.Resolve("Début {{cursor}} fin", Map(), ctx) Check(withCursor["cursorFromEnd"] = 4 && withCursor["text"] = "Début fin", "Marqueur cursor : position et retrait") ; ---------- JSON ---------- obj := Json.Parse('{"a": 1, "b": [1, 2, "trois"], "c": {"d": "x\ny"}}') Check(obj["a"] = 1 && obj["b"].Length = 3 && obj["c"]["d"] = "x`ny", "JSON parse imbriqué") round := Json.Parse(Json.Stringify(obj)) Check(round["b"][3] = "trois", "JSON aller-retour") ; ---------- Recherche ---------- class FakePrompt { __New(title, cat, tags, body) { this.Id := title this.Title := title this.Category := cat this.Tags := tags this.Body := body this.Pinned := 0 this.Favorite := 0 } } prompts := [ FakePrompt("Résumer en 5 points", "Général", ["résumé"], "Résume le texte"), FakePrompt("Revue de code", "Code", ["code"], "Relis ce code"), FakePrompt("Traduire en anglais", "Écriture", ["traduction"], "Traduis")] found := Search.Filter(prompts, "resu", Map()) Check(found.Length = 1 && found[1].Title = "Résumer en 5 points", "Recherche 'resu' trouve 'Résumer' (accents)") found2 := Search.Filter(prompts, "code", Map()) Check(found2.Length >= 1 && found2[1].Title = "Revue de code", "Recherche 'code' classe la revue en premier") all := Search.Filter(prompts, "", Map()) Check(all.Length = 3, "Recherche vide : toute la bibliothèque") ; ---------- Encodage : aller-retour UTF-8 bit à bit (§19.4) ---------- tmpFile := A_Temp "\promptkey-test-encodage.md" tricky := "é à — ‘’ « » 😀`r`n{{variable}} deuxième ligne" f := FileOpen(tmpFile, "w", "UTF-8") f.Write(tricky) f.Close() back := FileRead(tmpFile, "UTF-8") Check(back = tricky, "UTF-8 aller-retour identique") FileDelete(tmpFile) ; ---------- Bilan ---------- if (outFile != "") FileAppend("----`n" (failures = 0 ? "TOUS LES TESTS PASSENT" : failures " ÉCHEC(S)") "`n", outFile, "UTF-8") else FileAppend("----`n" (failures = 0 ? "TOUS LES TESTS PASSENT" : failures " ÉCHEC(S)") "`n", "*") ExitApp(failures = 0 ? 0 : 1)