v0.1.8 tests converted to standard library from testify

This commit is contained in:
2026-07-24 13:48:21 -04:00
parent 5553aeaec4
commit 24b7deebdb
19 changed files with 2399 additions and 1795 deletions
+254 -213
View File
@@ -4,220 +4,306 @@ import (
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
)
func eq[T comparable](tb testing.TB, got, want T, ctx string) {
tb.Helper()
if got != want {
tb.Errorf("%s: got %#v, want %#v", ctx, got, want)
}
}
func TestNewSanitizer(t *testing.T) {
// Default passthrough behavior
// No rules configured means full passthrough
s := New()
input := "abc\x00xyz"
assert.Equal(t, input, s.Sanitize(input), "default sanitizer should pass through all characters")
in := "abc\x00xyz"
eq(t, s.Sanitize(in), in, "default passthrough")
}
func TestSingleRule(t *testing.T) {
t.Run("strip non-printable", func(t *testing.T) {
s := New().Rule(FilterNonPrintable, TransformStrip)
assert.Equal(t, "ab", s.Sanitize("a\x00b"))
assert.Equal(t, "test", s.Sanitize("test\x01\x02\x03"))
})
tests := []struct {
name string
sanitizer *Sanitizer
in, want string
}{
{"strip non-printable", New().Rule(FilterNonPrintable, TransformStrip), "a\x00b", "ab"},
{"strip non-printable run", New().Rule(FilterNonPrintable, TransformStrip), "test\x01\x02\x03", "test"},
{"hex encode non-printable", New().Rule(FilterNonPrintable, TransformHexEncode), "a\x00b", "a<00>b"},
{"hex encode bell and tab", New().Rule(FilterNonPrintable, TransformHexEncode), "bell\x07tab\x09", "bell<07>tab<09>"},
{"json escape newline", New().Rule(FilterControl, TransformJSONEscape), "line1\nline2", `line1\nline2`},
{"json escape tab", New().Rule(FilterControl, TransformJSONEscape), "tab\there", `tab\there`},
{"json escape nul", New().Rule(FilterControl, TransformJSONEscape), "null\x00byte", `null\u0000byte`},
{"strip whitespace", New().Rule(FilterWhitespace, TransformStrip), "no spaces here", "nospaceshere"},
{"strip tabs", New().Rule(FilterWhitespace, TransformStrip), "tabs\t\tgone", "tabsgone"},
{"strip shell semicolon", New().Rule(FilterShellSpecial, TransformStrip), "cmd; echo test", "cmd echo test"},
{"strip shell pipe", New().Rule(FilterShellSpecial, TransformStrip), "no | pipes", "no pipes"},
{"strip shell dollar", New().Rule(FilterShellSpecial, TransformStrip), "$var", "var"},
}
t.Run("hex encode non-printable", func(t *testing.T) {
s := New().Rule(FilterNonPrintable, TransformHexEncode)
assert.Equal(t, "a<00>b", s.Sanitize("a\x00b"))
assert.Equal(t, "bell<07>tab<09>", s.Sanitize("bell\x07tab\x09"))
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
eq(t, tt.sanitizer.Sanitize(tt.in), tt.want, tt.name)
})
}
}
t.Run("JSON escape control", func(t *testing.T) {
s := New().Rule(FilterControl, TransformJSONEscape)
assert.Equal(t, "line1\\nline2", s.Sanitize("line1\nline2"))
assert.Equal(t, "tab\\there", s.Sanitize("tab\there"))
assert.Equal(t, "null\\u0000byte", s.Sanitize("null\x00byte"))
})
func TestRuleFunc(t *testing.T) {
// Predicate rules take priority over filter evaluation within the same rule
s := New().RuleFunc(func(r rune) bool { return r == 'x' }, TransformStrip)
eq(t, s.Sanitize("axbxc"), "abc", "predicate strip")
eq(t, s.Sanitize("clean"), "clean", "predicate miss")
t.Run("strip whitespace", func(t *testing.T) {
s := New().Rule(FilterWhitespace, TransformStrip)
assert.Equal(t, "nospaceshere", s.Sanitize("no spaces here"))
assert.Equal(t, "tabsgone", s.Sanitize("tabs\t\tgone"))
})
t.Run("strip shell special", func(t *testing.T) {
s := New().Rule(FilterShellSpecial, TransformStrip)
assert.Equal(t, "cmd echo test", s.Sanitize("cmd; echo test"))
assert.Equal(t, "no pipes", s.Sanitize("no | pipes"))
assert.Equal(t, "var", s.Sanitize("$var"))
})
s = New().RuleFunc(func(r rune) bool { return r > 0x7f }, TransformHexEncode)
eq(t, s.Sanitize("a√b"), "a<e2889a>b", "predicate hex encode")
}
func TestPolicy(t *testing.T) {
t.Run("PolicyTxt", func(t *testing.T) {
s := New().Policy(PolicyTxt)
assert.Equal(t, "hello<07>world", s.Sanitize("hello\x07world"))
assert.Equal(t, "clean text", s.Sanitize("clean text"))
})
tests := []struct {
name string
policy PolicyPreset
in, want string
}{
{"txt control", PolicyTxt, "hello\x07world", "hello<07>world"},
{"txt clean", PolicyTxt, "clean text", "clean text"},
// Tab is non-printable per strconv.IsPrint and is encoded like any control byte
{"txt tab", PolicyTxt, "col1\tcol2", "col1<09>col2"},
{"json newline", PolicyJSON, "line1\nline2", `line1\nline2`},
{"json tab", PolicyJSON, "\ttab", `\ttab`},
{"shell semicolon", PolicyShell, "cmd; echo", "cmdecho"},
{"shell whitespace", PolicyShell, "no spaces", "nospaces"},
{"raw passthrough", PolicyRaw, "a\x00b", "a\x00b"},
}
t.Run("PolicyJSON", func(t *testing.T) {
s := New().Policy(PolicyJSON)
assert.Equal(t, "line1\\nline2", s.Sanitize("line1\nline2"))
assert.Equal(t, "\\ttab", s.Sanitize("\ttab"))
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
eq(t, New().Policy(tt.policy).Sanitize(tt.in), tt.want, tt.name)
})
}
t.Run("PolicyShellArg", func(t *testing.T) {
s := New().Policy(PolicyShell)
assert.Equal(t, "cmdecho", s.Sanitize("cmd; echo"))
assert.Equal(t, "nospaces", s.Sanitize("no spaces"))
t.Run("unknown policy is a no-op", func(t *testing.T) {
s := New().Policy(PolicyPreset("bogus"))
eq(t, s.Sanitize("a\x00b"), "a\x00b", "unknown preset")
})
}
func TestRulePrecedence(t *testing.T) {
// With append + forward iteration: Policy is checked before Rule
s := New().Policy(PolicyTxt).Rule(FilterControl, TransformStrip)
func TestPolicyShellExtended(t *testing.T) {
s := New().Policy(PolicyShell)
eq(t, s.Sanitize(`a'b"c`), "abc", "quotes")
eq(t, s.Sanitize(`a\b`), "ab", "backslash")
eq(t, s.Sanitize("file*?"), "file", "glob")
eq(t, s.Sanitize("rm -rf *"), "rm-rf", "whitespace and glob")
eq(t, s.Sanitize("a\x00\x1bb"), "ab", "control")
eq(t, s.Sanitize("a{b}c[d]e~f!g"), "abcdefg", "braces, brackets, tilde, bang")
}
// \x07 is both control AND non-printable - matches PolicyTxt first
// \x00 is both control AND non-printable - matches PolicyTxt first
input := "a\x07b\x00c"
expected := "a<07>b<00>c" // FIXED: Policy wins now
result := s.Sanitize(input)
func TestRuleOrdering(t *testing.T) {
t.Run("policy precedes later rules", func(t *testing.T) {
// Rules append in call order and the first match wins, so a Policy
// registered first shadows overlapping custom rules
s := New().Policy(PolicyTxt).Rule(FilterControl, TransformStrip)
eq(t, s.Sanitize("a\x07b\x00c"), "a<07>b<00>c", "policy wins")
})
assert.Equal(t, expected, result,
"Policy() is now checked before Rule() - non-printable chars get hex encoded")
t.Run("first rule wins", func(t *testing.T) {
s := New().
Rule(FilterControl, TransformStrip).
Rule(FilterControl, TransformHexEncode) // unreachable
eq(t, s.Sanitize("a\x00b"), "ab", "first rule")
})
t.Run("chained distinct filters", func(t *testing.T) {
s := New().
Rule(FilterWhitespace, TransformStrip).
Rule(FilterShellSpecial, TransformHexEncode)
eq(t, s.Sanitize("cmd; echo hello"), "cmd<3b>echohello", "chained")
})
t.Run("policy plus custom rules", func(t *testing.T) {
s := New().
Policy(PolicyTxt).
Rule(FilterControl, TransformStrip).
Rule(FilterWhitespace, TransformJSONEscape)
// \x07 and \x7F are non-printable and match PolicyTxt first;
// the space matches the whitespace rule but JSON-escapes to itself
eq(t, s.Sanitize("a\x07b c\x7Fd"), "a<07>b c<7f>d", "combined")
})
}
func TestCompositeFilter(t *testing.T) {
s := New().Rule(FilterShellSpecial|FilterWhitespace, TransformStrip)
assert.Equal(t, "cmdechohello", s.Sanitize("cmd; echo hello"))
assert.Equal(t, "nopipesnospaces", s.Sanitize("no |pipes| no spaces"))
eq(t, s.Sanitize("cmd; echo hello"), "cmdechohello", "composite mask")
eq(t, s.Sanitize("no |pipes| no spaces"), "nopipesnospaces", "composite mask")
}
func TestChaining(t *testing.T) {
s := New().
Rule(FilterWhitespace, TransformStrip).
Rule(FilterShellSpecial, TransformHexEncode)
// Rules append in call order; first match wins.
// Whitespace rule strips spaces; shell rule hex-encodes ';'.
assert.Equal(t, "cmd<3b>echohello", s.Sanitize("cmd; echo hello"))
}
func TestMultipleRulesOrder(t *testing.T) {
// Test that first matching rule wins
s := New().
Rule(FilterControl, TransformStrip).
Rule(FilterControl, TransformHexEncode) // This should never match
assert.Equal(t, "ab", s.Sanitize("a\x00b"), "first rule should win")
func TestTransformPriority(t *testing.T) {
// applyTransform evaluates Strip first; only one transform applies per rule
s := New().Rule(FilterControl, TransformStrip|TransformHexEncode)
eq(t, s.Sanitize("a\x00b"), "ab", "strip precedence")
}
func TestEdgeCases(t *testing.T) {
t.Run("empty string", func(t *testing.T) {
s := New().Rule(FilterNonPrintable, TransformStrip)
assert.Equal(t, "", s.Sanitize(""))
strip := New().Rule(FilterNonPrintable, TransformStrip)
hex := New().Rule(FilterNonPrintable, TransformHexEncode)
eq(t, strip.Sanitize(""), "", "empty string")
eq(t, strip.Sanitize("\x00\x01\x02\x03"), "", "fully stripped")
eq(t, hex.Sanitize("Hello 世界 ✓"), "Hello 世界 ✓", "printable UTF-8 passthrough")
// U+0085 (NEL) is one non-printable rune encoded as two UTF-8 bytes
eq(t, hex.Sanitize("line1\u0085line2"), "line1<c285>line2", "multi-byte control")
}
func TestHexMarkerEscaping(t *testing.T) {
s := New().Policy(PolicyTxt)
eq(t, s.Sanitize("a\x00b"), "a<00>b", "actual NUL")
// Literal '<' is encoded so input cannot forge a marker
eq(t, s.Sanitize("a<00>b"), "a<3c>00>b", "literal marker text")
}
func TestSanitizeCleanFastPath(t *testing.T) {
s := New().Policy(PolicyTxt)
in := "clean ascii text"
eq(t, s.Sanitize(in), in, "unchanged")
if n := testing.AllocsPerRun(100, func() { _ = s.Sanitize(in) }); n != 0 {
t.Errorf("clean input allocated %v times, want 0", n)
}
}
func TestAppendSanitize(t *testing.T) {
s := New().Policy(PolicyTxt)
buf := append([]byte(nil), "prefix:"...)
buf = s.AppendSanitize(buf, "a\x00b")
eq(t, string(buf), "prefix:a<00>b", "append with rules")
// No rules configured appends verbatim
buf = append([]byte(nil), "prefix:"...)
buf = New().AppendSanitize(buf, "a\x00b")
eq(t, string(buf), "prefix:a\x00b", "append passthrough")
}
func TestSanitizerConcurrent(t *testing.T) {
s := New().Policy(PolicyTxt)
var wg sync.WaitGroup
for range 16 {
wg.Add(1)
go func() {
defer wg.Done()
for range 500 {
// Errorf is goroutine-safe; Fatal variants are not
if got := s.Sanitize("a\x00b\x07c"); got != "a<00>b<07>c" {
t.Errorf("concurrent Sanitize: got %q", got)
return
}
}
}()
}
wg.Wait()
}
func TestSerializerWriteString(t *testing.T) {
t.Run("raw applies sanitizer", func(t *testing.T) {
se := NewSerializer("raw", New().Rule(FilterNonPrintable, TransformHexEncode))
var buf []byte
se.WriteString(&buf, "test\x00data")
eq(t, string(buf), "test<00>data", "raw")
})
t.Run("only sanitizable characters", func(t *testing.T) {
s := New().Rule(FilterNonPrintable, TransformStrip)
assert.Equal(t, "", s.Sanitize("\x00\x01\x02\x03"))
t.Run("txt quotes conditionally", func(t *testing.T) {
se := NewSerializer("txt", New())
var buf []byte
se.WriteString(&buf, "hello world")
eq(t, string(buf), `"hello world"`, "quoted")
buf = nil
se.WriteString(&buf, "nospace")
eq(t, string(buf), "nospace", "unquoted")
buf = nil
se.WriteString(&buf, `has"quote`)
eq(t, string(buf), `"has\"quote"`, "escaped quote")
})
t.Run("multi-byte UTF-8", func(t *testing.T) {
s := New().Rule(FilterNonPrintable, TransformHexEncode)
input := "Hello 世界 ✓"
assert.Equal(t, input, s.Sanitize(input), "UTF-8 should pass through")
t.Run("json escapes transport characters", func(t *testing.T) {
se := NewSerializer("json", New())
var buf []byte
se.WriteString(&buf, "line1\nline2\t\"quoted\"")
eq(t, string(buf), `"line1\nline2\t\"quoted\""`, "escapes")
buf = nil
se.WriteString(&buf, "null\x00byte")
eq(t, string(buf), `"null\u0000byte"`, "control escape")
buf = nil
se.WriteString(&buf, "héllo 世界")
eq(t, string(buf), `"héllo 世界"`, "UTF-8 passthrough")
})
t.Run("multi-byte control character", func(t *testing.T) {
s := New().Rule(FilterNonPrintable, TransformHexEncode)
// NEL (Next Line) is U+0085, encoded as C2 85 in UTF-8
assert.Equal(t, "line1<c285>line2", s.Sanitize("line1\u0085line2"))
t.Run("json applies sanitizer before escaping", func(t *testing.T) {
se := NewSerializer("json", New().Policy(PolicyTxt))
var buf []byte
se.WriteString(&buf, "a\x00b")
eq(t, string(buf), `"a<00>b"`, "layered")
})
}
func TestSerializer(t *testing.T) {
t.Run("raw format with sanitizer", func(t *testing.T) {
san := New().Rule(FilterNonPrintable, TransformHexEncode)
handler := NewSerializer("raw", san)
func TestSerializerScalars(t *testing.T) {
san := New()
t.Run("numbers and booleans", func(t *testing.T) {
se := NewSerializer("json", san)
var buf []byte
handler.WriteString(&buf, "test\x00data")
assert.Equal(t, "test<00>data", string(buf))
se.WriteNumber(&buf, "42")
se.WriteBool(&buf, true)
se.WriteBool(&buf, false)
eq(t, string(buf), "42truefalse", "scalars are unquoted")
})
t.Run("txt format with quotes", func(t *testing.T) {
san := New() // No sanitization
handler := NewSerializer("txt", san)
t.Run("nil per format", func(t *testing.T) {
var buf []byte
handler.WriteString(&buf, "hello world")
assert.Equal(t, `"hello world"`, string(buf))
NewSerializer("raw", san).WriteNil(&buf)
eq(t, string(buf), "nil", "raw nil")
buf = nil
handler.WriteString(&buf, "nospace")
assert.Equal(t, "nospace", string(buf))
})
t.Run("json format escaping", func(t *testing.T) {
san := New() // JSON handler does its own escaping
handler := NewSerializer("json", san)
var buf []byte
handler.WriteString(&buf, "line1\nline2\t\"quoted\"")
assert.Equal(t, `"line1\nline2\t\"quoted\""`, string(buf))
NewSerializer("json", san).WriteNil(&buf)
eq(t, string(buf), "null", "json nil")
buf = nil
handler.WriteString(&buf, "null\x00byte")
assert.Equal(t, `"null\u0000byte"`, string(buf))
NewSerializer("txt", san).WriteNil(&buf)
eq(t, string(buf), "null", "txt nil")
})
t.Run("complex value handling", func(t *testing.T) {
san := New()
handler := NewSerializer("raw", san)
t.Run("complex values", func(t *testing.T) {
var buf []byte
handler.WriteComplex(&buf, map[string]int{"a": 1})
assert.Contains(t, string(buf), "map[")
})
t.Run("json utf8 passthrough", func(t *testing.T) {
handler := NewSerializer("json", New())
var buf []byte
handler.WriteString(&buf, "héllo 世界")
assert.Equal(t, `"héllo 世界"`, string(buf))
})
t.Run("json sanitizer applied", func(t *testing.T) {
handler := NewSerializer("json", New().Policy(PolicyTxt))
var buf []byte
handler.WriteString(&buf, "a\x00b")
assert.Equal(t, `"a<00>b"`, string(buf))
})
t.Run("nil handling", func(t *testing.T) {
san := New()
rawHandler := NewSerializer("raw", san)
var buf []byte
rawHandler.WriteNil(&buf)
assert.Equal(t, "nil", string(buf))
jsonHandler := NewSerializer("json", san)
buf = nil
jsonHandler.WriteNil(&buf)
assert.Equal(t, "null", string(buf))
NewSerializer("raw", san).WriteComplex(&buf, map[string]int{"a": 1})
eq(t, string(buf), "map[a:1]", "map formatting")
})
}
func TestPolicyWithCustomRules(t *testing.T) {
s := New().
Policy(PolicyTxt).
Rule(FilterControl, TransformStrip).
Rule(FilterWhitespace, TransformJSONEscape)
func TestNeedsQuotes(t *testing.T) {
tests := []struct {
format string
in string
want bool
}{
{"json", "anything", true},
{"raw", "anything", false},
{"txt", "", true},
{"txt", "plain", false},
{"txt", "has space", true},
{"txt", "semi;colon", true},
{"txt", "pipe|char", true},
{"txt", "brace{x}", true},
{"txt", "percent%", true},
{"txt", "equals=", true},
{"txt", "ctrl\x01", true},
{"txt", "dash-underscore_", false},
}
// \x07 is non-printable AND control - matches PolicyTxt first (hex encode)
// \x7F is non-printable but NOT control - matches PolicyTxt (hex encode)
input := "a\x07b c\x7Fd"
result := s.Sanitize(input)
assert.Equal(t, "a<07>b c<7f>d", result) // FIXED: \x07 now hex encoded
for _, tt := range tests {
se := NewSerializer(tt.format, New())
if got := se.NeedsQuotes(tt.in); got != tt.want {
t.Errorf("NeedsQuotes(%s, %q) = %v, want %v", tt.format, tt.in, got, tt.want)
}
}
}
func BenchmarkSanitizer(b *testing.B) {
@@ -238,65 +324,20 @@ func BenchmarkSanitizer(b *testing.B) {
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.ReportAllocs()
for b.Loop() {
_ = bm.sanitizer.Sanitize(input)
}
})
}
}
func TestTransformPriority(t *testing.T) {
// Test that only one transform is applied per rule
s := New().Rule(FilterControl, TransformStrip|TransformHexEncode)
// Should strip (first flag checked), not hex encode
assert.Equal(t, "ab", s.Sanitize("a\x00b"))
}
func TestSanitizerConcurrent(t *testing.T) {
func BenchmarkSanitizerClean(b *testing.B) {
s := New().Policy(PolicyTxt)
var wg sync.WaitGroup
for i := 0; i < 16; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 500; j++ {
if got := s.Sanitize("a\x00b\x07c"); got != "a<00>b<07>c" {
t.Errorf("got %q", got)
return
}
}
}()
input := strings.Repeat("clean ascii text ", 100)
b.ReportAllocs()
for b.Loop() {
_ = s.Sanitize(input)
}
wg.Wait()
}
func TestSanitizeCleanFastPath(t *testing.T) {
s := New().Policy(PolicyTxt)
in := "clean ascii text"
assert.Equal(t, in, s.Sanitize(in))
assert.Zero(t, testing.AllocsPerRun(100, func() { _ = s.Sanitize(in) }))
}
func TestAppendSanitize(t *testing.T) {
s := New().Policy(PolicyTxt)
buf := append([]byte(nil), "prefix:"...)
buf = s.AppendSanitize(buf, "a\x00b")
assert.Equal(t, "prefix:a<00>b", string(buf))
}
func TestHexMarkerEscaping(t *testing.T) {
s := New().Policy(PolicyTxt)
assert.Equal(t, "a<00>b", s.Sanitize("a\x00b")) // actual NUL
assert.Equal(t, "a<3c>00>b", s.Sanitize("a<00>b")) // literal text "<00>" — unambiguous
}
func TestPolicyShellExtended(t *testing.T) {
s := New().Policy(PolicyShell)
assert.Equal(t, "abc", s.Sanitize(`a'b"c`))
assert.Equal(t, "ab", s.Sanitize(`a\b`))
assert.Equal(t, "file", s.Sanitize("file*?"))
assert.Equal(t, "rm-rf", s.Sanitize("rm -rf *"))
assert.Equal(t, "ab", s.Sanitize("a\x00\x1bb")) // control stripped
}