feat(pack,docs): pack de démarrage 20 prompts, README, tests, CI, build
ci / verifier-pack (push) Successful in 1m29s
ci / verifier-pack (push) Successful in 1m29s
- 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
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
#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)
|
||||
Reference in New Issue
Block a user