package obs import ( "strings" "testing" ) // logging_truncate_test.go: the logged-body truncation keeps BOTH ends (mini-run finding Д3). The // mini-run enabled LOG_LLM_BODIES specifically to read the banknote block a model appends at the END // of its answer, and a tail-cut had removed exactly that — the debug channel was documented as the way // to see it and could not show it. func TestTruncateForLogKeepsBothEnds(t *testing.T) { body := []byte("HEAD-MARKER" + strings.Repeat("x", 5000) + "TAIL-MARKER") got := truncateForLog(body, 200) if !strings.Contains(got, "HEAD-MARKER") { t.Errorf("the head must survive truncation:\n%s", got) } if !strings.Contains(got, "TAIL-MARKER") { t.Errorf("the TAIL must survive truncation — the banknote block lives there:\n%s", got) } if !strings.Contains(got, "truncated") { t.Errorf("a truncated body must say so (else it reads as a short one):\n%s", got) } if len(got) > 400 { t.Errorf("truncation must actually bound the output, got %d bytes", len(got)) } } // TestTruncateForLogShortBodyUnchanged: a body under the cap is passed through byte-for-byte. func TestTruncateForLogShortBodyUnchanged(t *testing.T) { body := []byte("короткое тело ответа") if got := truncateForLog(body, 4096); got != string(body) { t.Errorf("a short body must be unchanged, got %q", got) } } // TestTruncateForLogCutsOnRuneBoundaries: the cut must not split a multi-byte rune, or the log line // becomes invalid UTF-8 — the exact shape that makes a Cyrillic transcript unreadable in a viewer. func TestTruncateForLogCutsOnRuneBoundaries(t *testing.T) { for cap := 20; cap < 60; cap++ { got := truncateForLog([]byte(strings.Repeat("ы", 400)), cap) if !isValidUTF8(got) { t.Fatalf("cap=%d produced invalid UTF-8: %q", cap, got) } } } func isValidUTF8(s string) bool { for _, r := range s { if r == '�' { return false } } return true }