323 lines
14 KiB
Go
323 lines
14 KiB
Go
package statistics
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/traggo/server/generated/gqlmodel"
|
|
"github.com/traggo/server/model"
|
|
"github.com/traggo/server/test"
|
|
"github.com/traggo/server/test/fake"
|
|
)
|
|
|
|
func TestSummary_fails(t *testing.T) {
|
|
db := test.InMemoryDB(t)
|
|
defer db.Close()
|
|
|
|
resolver := ResolverForStatistics{DB: db.DB}
|
|
|
|
_, err := resolver.Stats(fake.User(1), []*gqlmodel.Range{rangex("2019-06-11T10:00:00Z", "2019-06-11T09:00:00Z")}, []string{"type"}, nil, nil)
|
|
require.Error(t, err, "range start must be before range end ")
|
|
}
|
|
|
|
func TestStats(t *testing.T) {
|
|
data := []*testEntry{
|
|
name("simple").
|
|
Load(ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"))).
|
|
Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:00:10Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:00:10Z",
|
|
entry("type", "review", 10*time.Second))),
|
|
name("boundaries 1").
|
|
Load(ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"))).
|
|
Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:00:05Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:00:05Z",
|
|
entry("type", "review", 5*time.Second))),
|
|
name("boundaries 2").
|
|
Load(ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"))).
|
|
Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:05Z", "2019-06-11T10:00:10Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:05Z", "2019-06-11T10:00:10Z",
|
|
entry("type", "review", 5*time.Second))),
|
|
name("boundaries 3").
|
|
Load(ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"))).
|
|
Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:04Z", "2019-06-11T10:00:06Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:04Z", "2019-06-11T10:00:06Z",
|
|
entry("type", "review", 2*time.Second))),
|
|
name("exclude 1").
|
|
Load(ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"), tag("issue", "13"))).
|
|
Keys("type").
|
|
Exclude(tag("issue", "13")).
|
|
Ranges(rangex("2019-06-11T10:00:04Z", "2019-06-11T10:00:06Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:04Z", "2019-06-11T10:00:06Z", []*gqlmodel.StatisticsEntry{}...)),
|
|
name("empty range").
|
|
Load(ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"), tag("issue", "13"))).
|
|
Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:04Z", "2019-06-11T10:00:06Z"),
|
|
rangex("2019-06-12T10:00:04Z", "2019-06-12T10:00:06Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:04Z", "2019-06-11T10:00:06Z", entry("type", "review", 2*time.Second)),
|
|
result("2019-06-12T10:00:04Z", "2019-06-12T10:00:06Z", entry("type", "review", 0*time.Second))),
|
|
name("include 1").
|
|
Load(ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"), tag("issue", "13"))).
|
|
Keys("type").
|
|
Include(tag("issue", "13")).
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:00:10Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:00:10Z",
|
|
entry("type", "review", 10*time.Second))),
|
|
name("multiple 1").
|
|
Load(
|
|
ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:01:00Z", 10*time.Second, tag("type", "review"), tag("issue", "14")),
|
|
).Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:02:00Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:02:00Z",
|
|
entry("type", "review", 20*time.Second))),
|
|
name("multiple 2").
|
|
Load(
|
|
ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:01:00Z", 10*time.Second, tag("type", "review"), tag("issue", "14")),
|
|
).Keys("type").
|
|
Exclude(tag("issue", "14")).
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:02:00Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:02:00Z",
|
|
entry("type", "review", 10*time.Second))),
|
|
name("fill empty range").
|
|
Load(
|
|
ts("2019-06-11T10:00:00Z", 10*time.Second, tag("type", "review")),
|
|
).Keys("type").
|
|
Ranges(
|
|
rangex("2019-06-10T09:58:00Z", "2019-06-10T09:59:59Z"),
|
|
rangex("2019-06-10T10:00:00Z", "2019-06-11T10:02:00Z")).
|
|
Expected(
|
|
result("2019-06-10T09:58:00Z", "2019-06-10T09:59:59Z",
|
|
entry("type", "review", 0*time.Second)),
|
|
result("2019-06-10T10:00:00Z", "2019-06-11T10:02:00Z",
|
|
entry("type", "review", 10*time.Second))),
|
|
name("multiple 3").
|
|
Load(
|
|
ts("2019-06-11T10:00:00Z", 10*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:01:00Z", 60*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "12")),
|
|
ts("2019-06-11T10:02:00Z", 7*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:03:00Z", 14*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:04:00Z", 16*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "14")),
|
|
ts("2019-06-11T10:05:00Z", 33*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "10")),
|
|
ts("2019-06-11T10:05:00Z", 12*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "13")),
|
|
ts("2019-06-11T10:06:00Z", 90*time.Second, tag("type", "break")),
|
|
ts("2019-06-11T10:08:00Z", 33*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "10")),
|
|
ts("2019-06-11T10:09:00Z", 21*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:10:00Z", 18*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "12")),
|
|
ts("2019-06-11T10:11:00Z", 56*time.Second, tag("proj", "traggo"), tag("type", "review"), tag("issue", "11")),
|
|
ts("2019-06-11T10:12:00Z", 44*time.Second, tag("type", "break")),
|
|
).Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z",
|
|
entry("type", "break", 134*time.Second),
|
|
entry("type", "review", 133*time.Second),
|
|
entry("type", "support", 45*time.Second),
|
|
entry("type", "work", 102*time.Second))),
|
|
name("multiple 4").
|
|
Load(
|
|
ts("2019-06-11T10:00:00Z", 10*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:01:00Z", 60*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "12")),
|
|
ts("2019-06-11T10:02:00Z", 7*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:03:00Z", 14*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:04:00Z", 16*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "14")),
|
|
ts("2019-06-11T10:05:00Z", 33*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "10")),
|
|
ts("2019-06-11T10:05:00Z", 12*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "13")),
|
|
ts("2019-06-11T10:06:00Z", 90*time.Second, tag("type", "break")),
|
|
ts("2019-06-11T10:08:00Z", 32*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "10")),
|
|
ts("2019-06-11T10:09:00Z", 21*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:10:00Z", 18*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "12")),
|
|
ts("2019-06-11T10:11:00Z", 56*time.Second, tag("proj", "traggo"), tag("type", "review"), tag("issue", "11")),
|
|
ts("2019-06-11T10:12:00Z", 44*time.Second, tag("type", "break")),
|
|
).Keys("type", "issue").
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z",
|
|
entry("issue", "10", 65*time.Second),
|
|
entry("issue", "11", 56*time.Second),
|
|
entry("issue", "12", 78*time.Second),
|
|
entry("issue", "13", 29*time.Second),
|
|
entry("issue", "14", 16*time.Second),
|
|
entry("issue", "15", 35*time.Second),
|
|
entry("type", "break", 134*time.Second),
|
|
entry("type", "review", 133*time.Second),
|
|
entry("type", "support", 45*time.Second),
|
|
entry("type", "work", 101*time.Second))),
|
|
name("multiple 5").
|
|
Load(
|
|
ts("2019-06-11T10:00:00Z", 10*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:01:00Z", 60*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "12")),
|
|
ts("2019-06-11T10:02:00Z", 7*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:03:00Z", 14*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:04:00Z", 16*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "14")),
|
|
ts("2019-06-11T10:05:00Z", 33*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "10")),
|
|
ts("2019-06-11T10:05:00Z", 12*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "13")),
|
|
ts("2019-06-11T10:06:00Z", 90*time.Second, tag("type", "break")),
|
|
ts("2019-06-11T10:08:00Z", 32*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "10")),
|
|
ts("2019-06-11T10:09:00Z", 21*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:10:00Z", 18*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "12")),
|
|
ts("2019-06-11T10:11:00Z", 56*time.Second, tag("proj", "traggo"), tag("type", "review"), tag("issue", "11")),
|
|
ts("2019-06-11T10:12:00Z", 44*time.Second, tag("type", "break")),
|
|
).Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z")).
|
|
Include(tag("issue", "13"), tag("issue", "12")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z",
|
|
entry("type", "review", 77*time.Second),
|
|
entry("type", "support", 12*time.Second),
|
|
entry("type", "work", 18*time.Second))),
|
|
name("multiple 6").
|
|
Load(
|
|
ts("2019-06-11T10:00:00Z", 10*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:01:00Z", 60*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "12")),
|
|
ts("2019-06-11T10:02:00Z", 7*time.Second, tag("proj", "gotify"), tag("type", "review"), tag("issue", "13")),
|
|
ts("2019-06-11T10:03:00Z", 14*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:04:00Z", 16*time.Second, tag("proj", "gotify"), tag("type", "work"), tag("issue", "14")),
|
|
ts("2019-06-11T10:05:00Z", 33*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "10")),
|
|
ts("2019-06-11T10:05:00Z", 12*time.Second, tag("proj", "gotify"), tag("type", "support"), tag("issue", "13")),
|
|
ts("2019-06-11T10:06:00Z", 90*time.Second, tag("type", "break")),
|
|
ts("2019-06-11T10:08:00Z", 32*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "10")),
|
|
ts("2019-06-11T10:09:00Z", 21*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "15")),
|
|
ts("2019-06-11T10:10:00Z", 18*time.Second, tag("proj", "traggo"), tag("type", "work"), tag("issue", "12")),
|
|
ts("2019-06-11T10:11:00Z", 56*time.Second, tag("proj", "traggo"), tag("type", "review"), tag("issue", "11")),
|
|
ts("2019-06-11T10:12:00Z", 44*time.Second, tag("type", "break")),
|
|
).Keys("type").
|
|
Ranges(rangex("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z")).
|
|
Exclude(tag("issue", "10"), tag("issue", "11"), tag("issue", "14"), tag("issue", "15")).
|
|
Expected(
|
|
result("2019-06-11T10:00:00Z", "2019-06-11T10:15:00Z",
|
|
entry("type", "break", 134*time.Second),
|
|
entry("type", "review", 77*time.Second),
|
|
entry("type", "support", 12*time.Second),
|
|
entry("type", "work", 18*time.Second))),
|
|
}
|
|
|
|
for _, entry := range data {
|
|
t.Run(entry.name, func(t *testing.T) {
|
|
db := test.InMemoryDB(t)
|
|
defer db.Close()
|
|
|
|
db.User(1)
|
|
for _, timespan := range entry.timespans {
|
|
db.Create(×pan)
|
|
}
|
|
|
|
resolver := ResolverForStatistics{DB: db.DB}
|
|
|
|
stats, err := resolver.Stats(fake.User(1), entry.ranges, entry.keys, entry.exclude, entry.include)
|
|
require.NoError(t, err)
|
|
require.Equal(t, entry.expected, stats)
|
|
require.Len(t, stats, len(entry.expected))
|
|
})
|
|
}
|
|
}
|
|
|
|
func ts(start string, d time.Duration, tags ...model.TimeSpanTag) model.TimeSpan {
|
|
end := test.Time(start).Add(d)
|
|
return model.TimeSpan{
|
|
StartUserTime: test.Time(start),
|
|
EndUserTime: &end,
|
|
UserID: 1,
|
|
Tags: tags,
|
|
}
|
|
}
|
|
|
|
type testEntry struct {
|
|
timespans []model.TimeSpan
|
|
keys []string
|
|
exclude []*gqlmodel.InputTimeSpanTag
|
|
include []*gqlmodel.InputTimeSpanTag
|
|
name string
|
|
ranges []*gqlmodel.Range
|
|
expected []*gqlmodel.RangedStatisticsEntries
|
|
}
|
|
|
|
func name(name string) *testEntry {
|
|
return &testEntry{
|
|
name: name,
|
|
}
|
|
}
|
|
|
|
func (e *testEntry) Expected(expected ...*gqlmodel.RangedStatisticsEntries) *testEntry {
|
|
e.expected = expected
|
|
return e
|
|
}
|
|
|
|
func (e *testEntry) Keys(keys ...string) *testEntry {
|
|
e.keys = keys
|
|
return e
|
|
}
|
|
|
|
func (e *testEntry) Load(timespans ...model.TimeSpan) *testEntry {
|
|
e.timespans = timespans
|
|
return e
|
|
}
|
|
|
|
func (e *testEntry) Ranges(ranges ...*gqlmodel.Range) *testEntry {
|
|
e.ranges = ranges
|
|
return e
|
|
}
|
|
|
|
func (e *testEntry) Include(includes ...model.TimeSpanTag) *testEntry {
|
|
for _, entry := range includes {
|
|
e.include = append(e.include, &gqlmodel.InputTimeSpanTag{
|
|
Key: entry.Key,
|
|
Value: entry.StringValue,
|
|
})
|
|
}
|
|
return e
|
|
}
|
|
func (e *testEntry) Exclude(excludes ...model.TimeSpanTag) *testEntry {
|
|
for _, entry := range excludes {
|
|
e.exclude = append(e.exclude, &gqlmodel.InputTimeSpanTag{
|
|
Key: entry.Key,
|
|
Value: entry.StringValue,
|
|
})
|
|
}
|
|
return e
|
|
}
|
|
|
|
func tag(key string, value string) model.TimeSpanTag {
|
|
return model.TimeSpanTag{
|
|
Key: key,
|
|
StringValue: value,
|
|
}
|
|
}
|
|
|
|
func rangex(start, stop string) *gqlmodel.Range {
|
|
return &gqlmodel.Range{
|
|
Start: test.ModelTime(start),
|
|
End: test.ModelTime(stop),
|
|
}
|
|
}
|
|
|
|
func result(start, stop string, entries ...*gqlmodel.StatisticsEntry) *gqlmodel.RangedStatisticsEntries {
|
|
return &gqlmodel.RangedStatisticsEntries{
|
|
Start: test.ModelTimeUTC(start),
|
|
End: test.ModelTimeUTC(stop),
|
|
Entries: entries,
|
|
}
|
|
}
|
|
|
|
func entry(key string, value string, duration time.Duration) *gqlmodel.StatisticsEntry {
|
|
return &gqlmodel.StatisticsEntry{
|
|
Key: key,
|
|
Value: value,
|
|
TimeSpendInSeconds: duration.Seconds(),
|
|
}
|
|
}
|