Files
Spark 258287b2ea
ci / verifier-pack (push) Successful in 1m29s
feat(pack,docs): pack de démarrage 20 prompts, README, tests, CI, build
- packs/essentiels : 20 prompts en 4 catégories (Général, Écriture, Code,
  Analyse), en-têtes conformes au parseur, variables déclarées = utilisées
- tests/run_tests.ahk : suites Frontmatter, Variables, JSON, Recherche,
  encodage UTF-8 (à exécuter sous Windows avec AutoHotkey v2)
- scripts/check_pack.py + .gitea/workflows/ci.yml : cohérence du pack
- build.ps1 : tests puis compilation Ahk2Exe et zip portable
- docs : document d'architecture et de conception v1.0
2026-10-11 10:17:30 -04:00

138 lines
5.3 KiB
AutoHotkey

#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)