-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcluster_test.go
More file actions
158 lines (149 loc) · 4.41 KB
/
cluster_test.go
File metadata and controls
158 lines (149 loc) · 4.41 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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package qshape
import "testing"
func TestGroupAggregatesCalls(t *testing.T) {
in := []Query{
{Raw: "SELECT id FROM users WHERE id = 1", Calls: 100},
{Raw: "SELECT id FROM users WHERE id = 99", Calls: 200},
}
out, err := Group(in)
if err != nil {
t.Fatal(err)
}
if len(out) != 1 {
t.Fatalf("expected 1 cluster, got %d", len(out))
}
if out[0].TotalCalls != 300 {
t.Errorf("TotalCalls = %d, want 300", out[0].TotalCalls)
}
if len(out[0].Members) != 2 {
t.Errorf("Members len = %d, want 2", len(out[0].Members))
}
}
func TestGroupAggregatesTiming(t *testing.T) {
in := []Query{
{Raw: "SELECT id FROM users WHERE id = 1", Calls: 100, TotalExecTimeMs: 250.0, Rows: 100},
{Raw: "SELECT id FROM users WHERE id = 99", Calls: 400, TotalExecTimeMs: 750.0, Rows: 400},
}
out, err := Group(in)
if err != nil {
t.Fatal(err)
}
if len(out) != 1 {
t.Fatalf("expected 1 cluster, got %d", len(out))
}
if out[0].TotalExecTimeMs != 1000.0 {
t.Errorf("TotalExecTimeMs = %v, want 1000.0", out[0].TotalExecTimeMs)
}
if out[0].Rows != 500 {
t.Errorf("Rows = %d, want 500", out[0].Rows)
}
wantMean := 1000.0 / 500.0
if out[0].MeanExecTimeMs != wantMean {
t.Errorf("MeanExecTimeMs = %v, want %v", out[0].MeanExecTimeMs, wantMean)
}
}
func TestGroupSortsByTimingWhenPresent(t *testing.T) {
in := []Query{
{Raw: "SELECT id FROM users", Calls: 1000, TotalExecTimeMs: 50.0},
{Raw: "SELECT name FROM users", Calls: 10, TotalExecTimeMs: 5000.0},
}
out, err := Group(in)
if err != nil {
t.Fatal(err)
}
if len(out) != 2 {
t.Fatalf("expected 2 clusters, got %d", len(out))
}
if out[0].TotalExecTimeMs < out[1].TotalExecTimeMs {
t.Errorf("expected sort by TotalExecTimeMs desc, got %v then %v",
out[0].TotalExecTimeMs, out[1].TotalExecTimeMs)
}
}
func TestGroupOrdering(t *testing.T) {
in := []Query{
{Raw: "SELECT name FROM users", Calls: 10},
{Raw: "SELECT id FROM users", Calls: 500},
}
out, err := Group(in)
if err != nil {
t.Fatal(err)
}
if len(out) != 2 {
t.Fatalf("expected 2 clusters, got %d", len(out))
}
if out[0].TotalCalls < out[1].TotalCalls {
t.Errorf("clusters not sorted by TotalCalls desc: %d then %d",
out[0].TotalCalls, out[1].TotalCalls)
}
}
// Alias-only variants collapse (reshape strips decorative aliases);
// the LIMIT variant stays in its own cluster because LIMIT changes plan
// shape and LIMIT subsumption is intentionally out of scope.
func TestGroupORMVariantsCurrentBehavior(t *testing.T) {
in := []Query{
{Raw: "SELECT id, name FROM users WHERE id = $1", Calls: 1},
{Raw: "SELECT u.id, u.name FROM users u WHERE u.id = $1", Calls: 1},
{Raw: "SELECT id, name FROM users WHERE id = $1 LIMIT $2", Calls: 1},
}
out, err := Group(in)
if err != nil {
t.Fatal(err)
}
if len(out) != 2 {
t.Errorf("expected 2 clusters (alias variants collapse, LIMIT stays separate), got %d", len(out))
}
total := int64(0)
for _, c := range out {
total += c.TotalCalls
}
if total != 3 {
t.Errorf("total calls across clusters = %d, want 3", total)
}
}
// Safe ORM variants — alias-only, optional AS, AND-predicate reorder —
// must collapse to a single canonical fingerprint.
func TestGroupORMVariantsCollapse(t *testing.T) {
in := []Query{
{Raw: "SELECT id, name FROM users WHERE id = $1 AND status = $2", Calls: 1},
{Raw: "SELECT id, name FROM users WHERE status = $2 AND id = $1", Calls: 1},
{Raw: "SELECT u.id, u.name FROM users u WHERE u.id = $1 AND u.status = $2", Calls: 1},
{Raw: "SELECT u.id, u.name FROM users AS u WHERE u.status = $2 AND u.id = $1", Calls: 1},
}
out, err := Group(in)
if err != nil {
t.Fatal(err)
}
if len(out) != 1 {
t.Fatalf("expected 1 cluster, got %d: %+v", len(out), out)
}
if out[0].TotalCalls != int64(len(in)) {
t.Errorf("TotalCalls = %d, want %d", out[0].TotalCalls, len(in))
}
}
func TestGroupUnparseable(t *testing.T) {
in := []Query{
{Raw: "SELECT FROM WHERE", Calls: 5},
}
out, err := Group(in)
if err != nil {
t.Fatal(err)
}
if len(out) != 1 {
t.Fatalf("expected 1 cluster, got %d", len(out))
}
if out[0].Fingerprint != "" {
t.Errorf("unparseable cluster should have empty fingerprint, got %q", out[0].Fingerprint)
}
if out[0].Canonical != "SELECT FROM WHERE" {
t.Errorf("unparseable Canonical should be raw, got %q", out[0].Canonical)
}
}
func TestGroupEmpty(t *testing.T) {
out, err := Group(nil)
if err != nil {
t.Fatal(err)
}
if len(out) != 0 {
t.Errorf("expected empty slice, got %d clusters", len(out))
}
}