package main import ( "context" "testing" "github.com/kami/maven/internal/decision" "github.com/kami/maven/internal/store" ) // A real turn leaves a persisted trace, not only a ring entry. This is the whole // of V-629: without one there is nothing to fit the routing heads from. func TestTurnPersistsTrace(t *testing.T) { ring := decision.NewRing() h := traceHandler(t, ring) h.traces = traceSink(h.dataStore) h.encoderID = "hash-1024" if reply := h.handleText(context.Background(), "web", "сколько сейчас времени"); reply == "" { t.Fatal("turn produced no reply") } got, err := h.dataStore.RecentRoutingTraces(context.Background(), 5) if err != nil { t.Fatal(err) } if len(got) != 1 { t.Fatalf("persisted %d traces, want 1", len(got)) } tr := got[0] if tr.Utterance != "сколько сейчас времени" { t.Errorf("utterance %q", tr.Utterance) } if tr.Source != string(sourceText) { t.Errorf("source %q, want %q", tr.Source, sourceText) } // A stage-0 clock rule answers this one, so the turn teaches the classifier // nothing and the trace has to say so. if !tr.ClaimedBeforeHead { t.Errorf("claimed_before_head false on winner %q", tr.Winner) } if tr.EncoderID != "hash-1024" { t.Errorf("encoder_id %q", tr.EncoderID) } if len(tr.Claims) < 3 { t.Errorf("claims %s: the losers and the never-asked are the point", tr.Claims) } } // No store, no trace, and no panic. A typed nil pointer in the interface would // pass the nil check and die on the first turn. func TestNoStoreNoTrace(t *testing.T) { ring := decision.NewRing() h := traceHandler(t, ring) h.traces = traceSink(nil) if reply := h.handleText(context.Background(), "web", "сколько сейчас времени"); reply == "" { t.Fatal("turn produced no reply") } if len(ring.Recent(5)) != 1 { t.Error("the ring is still the first sink and must still hold the turn") } } // An empty utterance writes nothing. A blank row carries no label and no // diagnosis, and it is his words the retention bound exists for. func TestEmptyUtteranceIsNotPersisted(t *testing.T) { h := traceHandler(t, decision.NewRing()) h.traces = traceSink(h.dataStore) h.persistDecision(context.Background(), &decision.Record{Utterance: " "}, sourceText) got, err := h.dataStore.RecentRoutingTraces(context.Background(), 5) if err != nil { t.Fatal(err) } if len(got) != 0 { t.Fatalf("persisted %d traces for a blank utterance", len(got)) } } var _ traceWriter = (*store.Store)(nil)