Route GORM's logger through slog and bound it (closes #178)
All checks were successful
check / check (push) Successful in 2m57s
All checks were successful
check / check (push) Successful in 2m57s
This commit was merged in pull request #182.
This commit is contained in:
@@ -219,3 +219,103 @@ func TestTruncate_DropsInvalidUTF8(t *testing.T) {
|
||||
assert.Equal(t, "abc", got)
|
||||
assert.True(t, utf8.ValidString(got))
|
||||
}
|
||||
|
||||
// encodedCost is what a whole string costs on a line, by the same
|
||||
// accounting Truncate spends its budget with.
|
||||
func encodedCost(s string) int {
|
||||
total := 0
|
||||
for _, r := range s {
|
||||
total += logfield.EncodedBytes(r)
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
// TestTruncate_SpendsEncodedBytesNotRawBytes is the zero-headroom
|
||||
// version of TestTruncate_SpendsNoMoreThanTheBudget above, and of the
|
||||
// line-length assertions elsewhere.
|
||||
//
|
||||
// A line ceiling has slack in it by construction, and a LessOrEqual
|
||||
// against the budget cannot tell a budget spent exactly from one
|
||||
// spent under. Here the budget is checked against exactly what it
|
||||
// bought: a value built from a single rune must keep exactly
|
||||
// MaxBytes/EncodedBytes(r) of them, with nothing spare. A raw-byte
|
||||
// budget — cost := utf8.RuneLen(r) — fails this for every rune the
|
||||
// handlers escape.
|
||||
func TestTruncate_SpendsEncodedBytesNotRawBytes(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for name, r := range map[string]rune{
|
||||
"plain": 'x',
|
||||
"quote": '"',
|
||||
"backslash": '\\',
|
||||
"tab": '\t',
|
||||
"newline": '\n',
|
||||
"carriage_return": '\r',
|
||||
"c0_control": '\x01',
|
||||
"del": '\x7f',
|
||||
// U+2028 LINE SEPARATOR, which only the JSON handler
|
||||
// escapes.
|
||||
"line_separator": '
',
|
||||
"astral_nonprintable": '\U0001000C',
|
||||
"multibyte_printable": 'é',
|
||||
// U+20AC, a three-byte printable rune, charged its own
|
||||
// UTF-8 bytes rather than an escape.
|
||||
"three_byte_printable": '€',
|
||||
"emoji_printable": '\U0001F600',
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cost := logfield.EncodedBytes(r)
|
||||
want := logfield.MaxBytes / cost
|
||||
|
||||
// Far past the budget under either accounting.
|
||||
in := strings.Repeat(string(r), logfield.MaxBytes*2)
|
||||
|
||||
got := logfield.Truncate(in, logfield.MaxBytes)
|
||||
|
||||
require.True(
|
||||
t, strings.HasSuffix(
|
||||
got, logfield.TruncationMarker,
|
||||
),
|
||||
"a value past the budget must be marked",
|
||||
)
|
||||
|
||||
kept := strings.TrimSuffix(
|
||||
got, logfield.TruncationMarker,
|
||||
)
|
||||
|
||||
assert.Equal(
|
||||
t, want, utf8.RuneCountInString(kept),
|
||||
"budget bought the wrong number of runes at "+
|
||||
"%d encoded bytes each", cost,
|
||||
)
|
||||
assert.LessOrEqual(
|
||||
t, encodedCost(kept), logfield.MaxBytes,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestTruncate_NeverSplitsARune covers a cut landing inside a
|
||||
// multi-byte encoding rather than between two of them.
|
||||
func TestTruncate_NeverSplitsARune(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// U+20AC, three bytes and printable, so a small budget lands
|
||||
// inside an encoding rather than on a boundary.
|
||||
in := strings.Repeat("€", logfield.MaxBytes)
|
||||
|
||||
for b := 1; b <= 16; b++ {
|
||||
got := strings.TrimSuffix(
|
||||
logfield.Truncate(in, b), logfield.TruncationMarker,
|
||||
)
|
||||
|
||||
assert.True(
|
||||
t, utf8.ValidString(got),
|
||||
"budget %d produced invalid UTF-8", b,
|
||||
)
|
||||
assert.LessOrEqual(t, encodedCost(got), b)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user