summaryrefslogtreecommitdiff
path: root/internal/mapr/groupset_percentage_test.go
blob: 12738594ad7fdcef9bc8f30941ba3c0f5a363eba (plain)
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
package mapr

import (
	"strconv"
	"testing"
)

func TestGroupSetResultPercentageAndPercentile(t *testing.T) {
	query, err := NewQuery("select percentage(value),percentile(value) from stats group by host order by percentage(value)")
	if err != nil {
		t.Fatalf("Unable to parse query: %v", err)
	}

	groupSet := NewGroupSet()

	setA := groupSet.GetSet("host-a")
	if err := setA.Aggregate("percentage(value)", Percentage, "10", false); err != nil {
		t.Fatalf("Unable to aggregate percentage for host-a: %v", err)
	}
	if err := setA.Aggregate("percentile(value)", Percentile, "10", false); err != nil {
		t.Fatalf("Unable to aggregate percentile for host-a: %v", err)
	}

	setB := groupSet.GetSet("host-b")
	if err := setB.Aggregate("percentage(value)", Percentage, "30", false); err != nil {
		t.Fatalf("Unable to aggregate percentage for host-b: %v", err)
	}
	if err := setB.Aggregate("percentile(value)", Percentile, "30", false); err != nil {
		t.Fatalf("Unable to aggregate percentile for host-b: %v", err)
	}

	setC := groupSet.GetSet("host-c")
	if err := setC.Aggregate("percentage(value)", Percentage, "20", false); err != nil {
		t.Fatalf("Unable to aggregate percentage for host-c: %v", err)
	}
	if err := setC.Aggregate("percentile(value)", Percentile, "20", false); err != nil {
		t.Fatalf("Unable to aggregate percentile for host-c: %v", err)
	}

	rows, _, err := groupSet.result(query, false)
	if err != nil {
		t.Fatalf("Unable to build result rows: %v", err)
	}
	if len(rows) != 3 {
		t.Fatalf("Expected 3 result rows, got %d", len(rows))
	}

	if rows[0].groupKey != "host-b" {
		t.Fatalf("Expected rows to be ordered by percentage descending, first row=%s", rows[0].groupKey)
	}

	valuesByGroup := map[string][]float64{}
	for _, row := range rows {
		parsedValues := make([]float64, 0, len(row.values))
		for _, value := range row.values {
			parsedValue, err := strconv.ParseFloat(value, 64)
			if err != nil {
				t.Fatalf("Unable to parse result value %q: %v", value, err)
			}
			parsedValues = append(parsedValues, parsedValue)
		}
		valuesByGroup[row.groupKey] = parsedValues
	}

	assertAlmostEqual(t, valuesByGroup["host-a"][0], 16.6666666667, 0.0001, "host-a percentage")
	assertAlmostEqual(t, valuesByGroup["host-a"][1], 33.3333333333, 0.0001, "host-a percentile")
	assertAlmostEqual(t, valuesByGroup["host-b"][0], 50.0, 0.0001, "host-b percentage")
	assertAlmostEqual(t, valuesByGroup["host-b"][1], 100.0, 0.0001, "host-b percentile")
	assertAlmostEqual(t, valuesByGroup["host-c"][0], 33.3333333333, 0.0001, "host-c percentage")
	assertAlmostEqual(t, valuesByGroup["host-c"][1], 66.6666666667, 0.0001, "host-c percentile")
}

func TestGroupSetPercentageReturnsZeroWhenTotalIsZero(t *testing.T) {
	query, err := NewQuery("select percentage(value) from stats group by host")
	if err != nil {
		t.Fatalf("Unable to parse query: %v", err)
	}

	groupSet := NewGroupSet()
	for _, host := range []string{"host-a", "host-b"} {
		set := groupSet.GetSet(host)
		if err := set.Aggregate("percentage(value)", Percentage, "0", false); err != nil {
			t.Fatalf("Unable to aggregate percentage for %s: %v", host, err)
		}
	}

	rows, _, err := groupSet.result(query, false)
	if err != nil {
		t.Fatalf("Unable to build result rows: %v", err)
	}
	if len(rows) != 2 {
		t.Fatalf("Expected 2 result rows, got %d", len(rows))
	}
	for _, row := range rows {
		if len(row.values) != 1 {
			t.Fatalf("Expected one result value, got %d for %s", len(row.values), row.groupKey)
		}
		value, err := strconv.ParseFloat(row.values[0], 64)
		if err != nil {
			t.Fatalf("Unable to parse percentage result %q: %v", row.values[0], err)
		}
		assertAlmostEqual(t, value, 0.0, 0.0001, row.groupKey+" percentage")
	}
}

func TestPercentileRank(t *testing.T) {
	sortedValues := []float64{10, 20, 30}

	tests := []struct {
		name     string
		value    float64
		expected float64
	}{
		{name: "below minimum", value: 5, expected: 0},
		{name: "first bucket", value: 10, expected: 33.3333333333},
		{name: "middle bucket", value: 20, expected: 66.6666666667},
		{name: "maximum", value: 30, expected: 100},
		{name: "above maximum", value: 40, expected: 100},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assertAlmostEqual(t, percentileRank(tt.value, sortedValues), tt.expected, 0.0001, tt.name)
		})
	}

	assertAlmostEqual(t, percentileRank(10, []float64{10, 10, 30}), 66.6666666667, 0.0001, "duplicate percentile rank")
	if got := percentileRank(10, nil); got != 0 {
		t.Fatalf("Expected empty percentile input to return 0, got %f", got)
	}
}

func assertAlmostEqual(t *testing.T, got, expected, tolerance float64, label string) {
	t.Helper()

	diff := got - expected
	if diff < 0 {
		diff = -diff
	}
	if diff > tolerance {
		t.Fatalf("Unexpected %s: got=%f expected=%f tolerance=%f", label, got, expected, tolerance)
	}
}