Files
Loki/internal/loki/chat_stuck_test.go
T
Loki de6551a153 Rebaptise AJEAN en Loki (fork, lignée conservée)
- module github.com/R0m1k3/Loki, cmd/loki, internal/loki (package loki)
- LOKI_HOME, LOKI_MODEL_DIRS, LOKI_SERVICE, LOKI_DL_CONNS ; /etc/loki ;
  units loki-engine / loki-ui ; binaire et aide CLI
- updateRepo pointe sur R0m1k3/Loki (l'auto-update ne tirera plus les
  binaires AJEAN amont)

Conservé à l'identique : le domaine ajean.link (service de tunnel amont),
les littéraux de migration 0.7.x (migrate_07.go), RELEASE_NOTES.md et
LICENSE (historique et licence de l'amont).

go build/vet/test : verts.
2026-08-14 21:33:15 +00:00

113 lines
3.5 KiB
Go

package loki
import (
"context"
"os"
"runtime"
"strings"
"testing"
"time"
)
// Le bouton stop doit VRAIMENT arrêter une commande shell. Elle naissait d'un
// contexte indépendant du tour : arrêter la génération ne l'arrêtait pas, et le
// tour restait suspendu jusqu'à l'expiration du délai (5 min au maximum).
func TestRunShellObeitAuStop(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("commande de veille propre à Unix")
}
testHome(t)
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(200 * time.Millisecond)
cancel()
}()
start := time.Now()
out := runShell(ctx, "sleep 30", 300)
if d := time.Since(start); d > 5*time.Second {
t.Fatalf("la commande a survécu à l'annulation (%s) : %q", d, out)
}
if !strings.Contains(out, "interrompue") {
t.Errorf("résultat attendu « interrompue », reçu %q", out)
}
}
// Une commande qui laisse un process en arrière-plan garde les tubes de sortie
// ouverts. Sans WaitDelay, Wait attend leur fermeture — donc pour toujours — et
// le tour ne se terminait JAMAIS (seul un redémarrage du service débloquait).
func TestRunShellNeBloquePasSurUnProcessDetache(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("syntaxe de détachement propre à Unix")
}
testHome(t)
start := time.Now()
out := runShell(context.Background(), "sleep 30 & echo lance", 2)
if d := time.Since(start); d > 10*time.Second {
t.Fatalf("runShell est resté accroché aux tubes du process détaché (%s)", d)
}
if !strings.Contains(out, "lance") && !strings.Contains(out, "timeout") {
t.Errorf("sortie inattendue : %q", out)
}
}
// Le dossier de travail est résolu une fois par process : s'il disparaît, plus
// AUCUNE commande ne passait (« chdir : no such file or directory ») jusqu'au
// redémarrage du service. Il doit être recréé au besoin.
func TestRunShellRecreeLeWorkspaceDisparu(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("echo se comporte différemment sous cmd.exe")
}
testHome(t)
ws := agentWorkspace()
if ws == "" {
t.Skip("pas de workspace résolu")
}
if err := os.RemoveAll(ws); err != nil {
t.Fatal(err)
}
out := runShell(context.Background(), "echo vivant", 10)
if !strings.Contains(out, "vivant") {
t.Fatalf("commande cassée par la disparition du workspace : %q", out)
}
}
// Reset doit TOUJOURS rendre la main, même sur un tour resté coincé : c'est le
// geste que l'on tente quand le chat est bloqué. Avant, Generating restait vrai
// et tout envoi suivant était refusé « génération en cours » jusqu'au
// redémarrage du service.
func TestResetDebloqueUnTourCoince(t *testing.T) {
c := newTestConv()
// Simule un tour parti et jamais terminé (commande accrochée, moteur disparu).
c.mu.Lock()
c.Generating = true
c.mu.Unlock()
c.Reset()
if st := c.state(); st["generating"] != false {
t.Fatal("après Reset, la conversation doit être déclarée libre")
}
}
// Le tour abandonné se termine parfois APRÈS le démarrage du suivant. Sa fin ne
// doit pas déclarer « libre » une génération toute neuve.
func TestTourPerimeNeLiberePasLeTourCourant(t *testing.T) {
c := newTestConv()
oldEpoch := c.epoch
c.Reset() // le tour d'epoch `oldEpoch` devient périmé
c.mu.Lock()
c.Generating = true // un nouveau tour démarre
c.mu.Unlock()
// Fin (tardive) du tour périmé : même code que le defer de generate.
c.mu.Lock()
if c.epoch == oldEpoch {
c.Generating = false
}
c.mu.Unlock()
if st := c.state(); st["generating"] != true {
t.Fatal("un tour périmé a libéré le tour courant")
}
}