Repository navigation
Expand file tree
/
Copy pathtransform_test.go
More file actions
111 lines (104 loc) · 2.92 KB
/
Copy pathtransform_test.go
File metadata and controls
111 lines (104 loc) · 2.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package log
import (
"bytes"
"strings"
"testing"
"time"
)
var upper = NewTransform(func(dst, src []byte) []byte {
return append(dst, bytes.ToUpper(src)...)
})
var boom = NewTransform(func(dst, src []byte) []byte { panic("xform boom") })
func TestTransform(t *testing.T) {
var buf lockedBuffer
c := New(&buf, nil)
c.Logger().Info("m",
upper("u", []byte("shout")),
upper("empty", nil),
Str("plain", "left alone"),
)
if err := c.Close(); err != nil {
t.Fatal(err)
}
m := parseLines(t, buf.Bytes())[0]
if m["u"] != "SHOUT" || m["empty"] != "" || m["plain"] != "left alone" {
t.Fatalf("got %v", m)
}
}
func TestTransformPanicAndTruncation(t *testing.T) {
var buf lockedBuffer
c := New(&buf, nil)
big := bytes.Repeat([]byte("x"), maxStrLen+100)
c.Logger().Info("m", boom("b", []byte("v")), upper("big", big))
if err := c.Close(); err != nil {
t.Fatal(err)
}
m := parseLines(t, buf.Bytes())[0]
if s, _ := m["b"].(string); !strings.HasPrefix(s, "!PANIC:") {
t.Fatalf("b = %v", m["b"])
}
if got := len(m["big"].(string)); got != maxStrLen {
t.Fatalf("input not capped before transform: len %d", got)
}
}
// A Transform fn that logs while the queue is full must not deadlock: the
// consumer's own push bypasses the capacity gate (it is the only drainer).
func TestTransformLogNoSelfDeadlock(t *testing.T) {
var buf lockedBuffer
c := New(&buf, &Options{QueueSize: 2})
l := c.Logger()
inside := make(chan struct{})
filled := make(chan struct{})
reentrant := NewTransform(func(dst, src []byte) []byte {
close(inside) // consumer is here; its batch is already swapped out
<-filled
l.Info("from transform") // queue is full: must bypass, not wait
return append(dst, src...)
})
l.Info("m", reentrant("r", []byte("v")))
<-inside
l.Info("f1")
l.Info("f2") // queue now at capacity while the consumer is inside fn
close(filled)
done := make(chan error, 1)
go func() { done <- c.Close() }()
select {
case err := <-done:
if err != nil {
t.Fatal(err)
}
case <-time.After(5 * time.Second):
t.Fatal("consumer deadlocked on its own push")
}
ms := parseLines(t, buf.Bytes())
if len(ms) != 4 {
t.Fatalf("want 4 lines, got %d: %v", len(ms), ms)
}
seen := map[string]bool{}
for _, m := range ms {
seen[m["msg"].(string)] = true
}
if !seen["from transform"] {
t.Fatalf("transform's log dropped: %v", ms)
}
}
// The transform output is escaped like any string value — it cannot break
// the JSON structure.
func TestTransformOutputEscaped(t *testing.T) {
quoteInjector := NewTransform(func(dst, src []byte) []byte {
return append(dst, `evil",%injected":1,"x":"`...)
})
var buf lockedBuffer
c := New(&buf, nil)
c.Logger().Info("m", quoteInjector("v", nil))
if err := c.Close(); err != nil {
t.Fatal(err)
}
m := parseLines(t, buf.Bytes())[0]
if _, ok := m["%injected"]; ok {
t.Fatal("transform output broke JSON structure")
}
if !strings.Contains(m["v"].(string), `evil"`) {
t.Fatalf("v = %q", m["v"])
}
}