fix(queue): fertige Ansagen nicht mehr als „abgebrochen" protokollieren
Der Worker rief cancel() auf seinem eigenen Kontext, BEVOR er ctx.Err() prüfte — damit war jede normal beendete Ansage im Protokoll „abgebrochen", auch bei Erfolg. Der Abbruch wird jetzt vor dem eigenen cancel() abgelesen. Regressionstest: fertig heißt „fertig nach", ein echter Cancel heißt „abgebrochen". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
bc26a73aeb
commit
79314dfb0f
2 files changed
+60
-1
No files matched your search
@@ -317,6 +317,9 @@ func (q *Queue) loop() {
|
||||
start := time.Now()
|
||||
err := q.speak(ctx, item)
|
||||
dur := time.Since(start)
|
||||
// Abbruch VOR dem eigenen cancel() ablesen — danach wäre ctx.Err()
|
||||
// immer gesetzt und jede fertige Ansage hieße „abgebrochen".
|
||||
cancelled := ctx.Err() != nil
|
||||
cancel()
|
||||
|
||||
q.mu.Lock()
|
||||
@@ -325,7 +328,7 @@ func (q *Queue) loop() {
|
||||
q.mu.Unlock()
|
||||
|
||||
switch {
|
||||
case ctx.Err() != nil:
|
||||
case cancelled:
|
||||
q.logf("queue: ansage %s abgebrochen", item.ID)
|
||||
case err != nil:
|
||||
q.logf("queue: ansage %s fehlgeschlagen nach %s: %v", item.ID, dur.Round(time.Millisecond), err)
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -552,3 +554,57 @@ func TestInactiveQuietWindow(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// syncLog ist ein nebenläufigkeitssicherer Log-Puffer für Worker-Zeilen.
|
||||
type syncLog struct {
|
||||
mu sync.Mutex
|
||||
b strings.Builder
|
||||
}
|
||||
|
||||
func (l *syncLog) Write(p []byte) (int, error) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
return l.b.Write(p)
|
||||
}
|
||||
|
||||
func (l *syncLog) String() string {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
return l.b.String()
|
||||
}
|
||||
|
||||
// TestProtokollFertigNichtAbgebrochen: Eine normal beendete Ansage wird als
|
||||
// „fertig" protokolliert, nicht als „abgebrochen" — der Worker darf seinen
|
||||
// eigenen cancel() nicht als Abbruch deuten. Ein echter Cancel heißt
|
||||
// dagegen „abgebrochen".
|
||||
func TestProtokollFertigNichtAbgebrochen(t *testing.T) {
|
||||
logb := &syncLog{}
|
||||
g := neueSpielerin()
|
||||
q := New(Laws{}, g.speak, logb)
|
||||
defer q.Close()
|
||||
|
||||
q.Enqueue(Item{Text: "normal zu ende"})
|
||||
g.warteBis(t, 1)
|
||||
g.step <- struct{}{}
|
||||
waitFor(t, func() bool { return strings.Contains(logb.String(), "fertig nach") })
|
||||
if strings.Contains(logb.String(), "abgebrochen") {
|
||||
t.Errorf("fertige Ansage als abgebrochen protokolliert:\n%s", logb.String())
|
||||
}
|
||||
|
||||
q.Enqueue(Item{Text: "wird abgebrochen"})
|
||||
g.warteBis(t, 2)
|
||||
q.Cancel()
|
||||
waitFor(t, func() bool { return strings.Contains(logb.String(), "abgebrochen") })
|
||||
}
|
||||
|
||||
// waitFor pollt cond bis zu zwei Sekunden.
|
||||
func waitFor(t *testing.T, cond func() bool) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for !cond() {
|
||||
if time.Now().After(deadline) {
|
||||
t.Fatal("Bedingung nicht binnen 2 s erfüllt")
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user