Found on the PoC: a streamed list came out as "1. 2. 3." — each item cut at one release and dropped at the next, which sent the engine's own events. A property test now cuts an answer every way and checks it arrives whole. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
43 lines
1.5 KiB
Go
43 lines
1.5 KiB
Go
package guard
|
|
|
|
import (
|
|
"context"
|
|
"math/rand/v2"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Found on the PoC: a word held back at one release was dropped at the next,
|
|
// when that release had nothing to rewrite and sent the engine's own events.
|
|
func TestAWordHeldBackIsNeverLost(t *testing.T) {
|
|
g := gate(t, lib(map[string]Policy{"p": {Output: Rules{PII: map[string]string{"email": "redact"}}}}, "p"))
|
|
got, _, _ := stream(g.Output(context.Background(), Subject{Stream: true}),
|
|
"1", ".", " Red", "\n", "2", ".", " Green", "\n", "3", ".", " Blue")
|
|
if got != "1. Red\n2. Green\n3. Blue" {
|
|
t.Errorf("client got %q", got)
|
|
}
|
|
}
|
|
|
|
// However an answer is cut into tokens, the client is sent exactly the
|
|
// whole answer, rewritten.
|
|
func TestEverySplitOfAnAnswerArrivesWhole(t *testing.T) {
|
|
g := gate(t, lib(map[string]Policy{"p": {Output: Rules{PII: map[string]string{"email": "redact"}}}}, "p"))
|
|
text := "Hello. Mail bob@example.com or ann@example.org! Lists:\n1. Red\n2. Green and a long tail without stops " +
|
|
strings.Repeat("word ", 50) + "end?"
|
|
want := strings.NewReplacer("bob@example.com", "[EMAIL_1]", "ann@example.org", "[EMAIL_2]").Replace(text)
|
|
rng := rand.New(rand.NewPCG(1, 2))
|
|
for i := range 300 {
|
|
var parts []string
|
|
for rest := text; rest != ""; {
|
|
n := 1 + rng.IntN(8)
|
|
n = min(n, len(rest))
|
|
parts = append(parts, rest[:n])
|
|
rest = rest[n:]
|
|
}
|
|
got, _, _ := stream(g.Output(context.Background(), Subject{Stream: true}), parts...)
|
|
if got != want {
|
|
t.Fatalf("split %d: client got\n%q\nwant\n%q", i, got, want)
|
|
}
|
|
}
|
|
}
|