Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix(coderd): use stable sorting for insights and improve test coverage#9250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
mafredri merged 33 commits intomainfrommafredri/test-improve-template-insights-tests
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
33 commits
Select commitHold shift + click to select a range
ffc7391
test(coderd): add extended template insights test suite
mafredriAug 22, 2023
95ad848
add initial golden files
mafredriAug 22, 2023
d4d58cf
add some more usage and verify golden
mafredriAug 22, 2023
e4f901a
fixes
mafredriAug 22, 2023
f333170
add an hour of app usage
mafredriAug 22, 2023
6fb49a9
add 15 min of app usage -> active user on last day
mafredriAug 22, 2023
e9a967e
add placeholder data
mafredriAug 22, 2023
9879462
add identical usage in two templates for one user
mafredriAug 22, 2023
fc1a98d
test app merging
mafredriAug 22, 2023
f14ddf6
fix(codersdk): use url values for query params
mafredriAug 22, 2023
8a60568
improve stability of template id
mafredriAug 22, 2023
7d93d13
add sao paulo tz test (invalid interval times)
mafredriAug 22, 2023
f9fdb47
fix start/end time tz returned in interval report
mafredriAug 22, 2023
54638b0
cleanup
mafredriAug 22, 2023
7b0ac4c
refactor template creation with stable IDs
mafredriAug 23, 2023
1e7d860
add parameter test
mafredriAug 23, 2023
9d96052
refactor
mafredriAug 23, 2023
930d7ef
add parameter no data test
mafredriAug 23, 2023
07cdc0b
add 1h usage to other user
mafredriAug 23, 2023
73367d1
add usage outside of utc timeframe
mafredriAug 23, 2023
c2a0496
add third user with ssh and web term usage
mafredriAug 23, 2023
b738799
add third user app usage
mafredriAug 23, 2023
79bdbf9
add webterminal app usage
mafredriAug 23, 2023
ce2bb78
use stable sorting in Go and minor query tweaks and fakedb fixes
mafredriAug 23, 2023
0896439
use github.com/google/go-cmp/cmp for diffs
mafredriAug 23, 2023
5b4733a
restructure
mafredriAug 23, 2023
3607a80
more fakedb fixes
mafredriAug 23, 2023
688e1d0
fix fakedb logic bugs
mafredriAug 23, 2023
8c294d0
fix commented code
mafredriAug 23, 2023
decbdfe
fix test bug
mafredriAug 24, 2023
870aff1
add test for tpl1 param4 option2
mafredriAug 24, 2023
75a96d7
add outside timeframe tests for apps
mafredriAug 24, 2023
e72e265
remove previous insights test in favor of new one
mafredriAug 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletionMakefile
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -564,7 +564,7 @@ coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS)
./scripts/apidocgen/generate.sh
pnpm run format:write:only ./docs/api ./docs/manifest.json ./coderd/apidoc/swagger.json

update-golden-files: cli/testdata/.gen-golden helm/coder/tests/testdata/.gen-golden helm/provisioner/tests/testdata/.gen-golden scripts/ci-report/testdata/.gen-golden enterprise/cli/testdata/.gen-golden
update-golden-files: cli/testdata/.gen-golden helm/coder/tests/testdata/.gen-golden helm/provisioner/tests/testdata/.gen-golden scripts/ci-report/testdata/.gen-golden enterprise/cli/testdata/.gen-golden coderd/.gen-golden
.PHONY: update-golden-files

cli/testdata/.gen-golden: $(wildcard cli/testdata/*.golden) $(wildcard cli/*.tpl) $(GO_SRC_FILES) $(wildcard cli/*_test.go)
Expand All@@ -583,6 +583,10 @@ helm/provisioner/tests/testdata/.gen-golden: $(wildcard helm/provisioner/tests/t
go test ./helm/provisioner/tests -run=TestUpdateGoldenFiles -update
touch "$@"

coderd/.gen-golden: $(wildcard coderd/testdata/*/*.golden) $(GO_SRC_FILES) $(wildcard coderd/*_test.go)
go test ./coderd -run="Test.*Golden$$" -update
touch "$@"

scripts/ci-report/testdata/.gen-golden: $(wildcard scripts/ci-report/testdata/*) $(wildcard scripts/ci-report/*.go)
go test ./scripts/ci-report -run=TestOutputMatchesGoldenFile -update
touch "$@"
Expand Down
4 changes: 4 additions & 0 deletionscoderd/coderd_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ package coderd_test

import (
"context"
"flag"
"io"
"net/http"
"net/netip"
Expand All@@ -23,6 +24,9 @@ import (
"github.com/coder/coder/v2/testutil"
)

// updateGoldenFiles is a flag that can be set to update golden files.
var updateGoldenFiles = flag.Bool("update", false, "Update golden files")

func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
Expand Down
48 changes: 35 additions & 13 deletionscoderd/database/db2sdk/db2sdk.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,9 +3,10 @@ package db2sdk

import (
"encoding/json"
"sort"
"strings"

"github.com/google/uuid"
"golang.org/x/exp/slices"

"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/parameter"
Expand DownExpand Up@@ -125,9 +126,34 @@ func Role(role rbac.Role) codersdk.Role {
}

func TemplateInsightsParameters(parameterRows []database.GetTemplateParameterInsightsRow) ([]codersdk.TemplateParameterUsage, error) {
parametersByNum := make(map[int64]*codersdk.TemplateParameterUsage)
// Use a stable sort, similarly to how we would sort in the query, note that
// we don't sort in the query because order varies depending on the table
// collation.
//
// ORDER BY utp.name, utp.type, utp.display_name, utp.description, utp.options, wbp.value
slices.SortFunc(parameterRows, func(a, b database.GetTemplateParameterInsightsRow) int {
if a.Name != b.Name {
return strings.Compare(a.Name, b.Name)
}
if a.Type != b.Type {
return strings.Compare(a.Type, b.Type)
}
if a.DisplayName != b.DisplayName {
return strings.Compare(a.DisplayName, b.DisplayName)
}
if a.Description != b.Description {
return strings.Compare(a.Description, b.Description)
}
if string(a.Options) != string(b.Options) {
return strings.Compare(string(a.Options), string(b.Options))
}
return strings.Compare(a.Value, b.Value)
})

parametersUsage := []codersdk.TemplateParameterUsage{}
indexByNum := make(map[int64]int)
for _, param := range parameterRows {
if _, ok :=parametersByNum[param.Num]; !ok {
if _, ok :=indexByNum[param.Num]; !ok {
var opts []codersdk.TemplateVersionParameterOption
err := json.Unmarshal(param.Options, &opts)
if err != nil {
Expand All@@ -139,28 +165,24 @@ func TemplateInsightsParameters(parameterRows []database.GetTemplateParameterIns
return nil, err
}

parametersByNum[param.Num] =&codersdk.TemplateParameterUsage{
parametersUsage =append(parametersUsage,codersdk.TemplateParameterUsage{
TemplateIDs: param.TemplateIDs,
Name: param.Name,
Type: param.Type,
DisplayName: param.DisplayName,
Description: plaintextDescription,
Options: opts,
}
})
indexByNum[param.Num] = len(parametersUsage) - 1
}
parametersByNum[param.Num].Values = append(parametersByNum[param.Num].Values, codersdk.TemplateParameterValue{

i := indexByNum[param.Num]
parametersUsage[i].Values = append(parametersUsage[i].Values, codersdk.TemplateParameterValue{
Value: param.Value,
Count: param.Count,
})
}
parametersUsage := []codersdk.TemplateParameterUsage{}
for _, param := range parametersByNum {
parametersUsage = append(parametersUsage, *param)
}

sort.Slice(parametersUsage, func(i, j int) bool {
return parametersUsage[i].Name < parametersUsage[j].Name
})
return parametersUsage, nil
}

Expand Down
33 changes: 22 additions & 11 deletionscoderd/database/dbfake/dbfake.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2018,6 +2018,10 @@ func (q *FakeQuerier) GetTemplateAppInsights(ctx context.Context, arg database.G
return nil, err
}

if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, w.TemplateID) {
continue
}

app, _ := q.getWorkspaceAppByAgentIDAndSlugNoLock(ctx, database.GetWorkspaceAppByAgentIDAndSlugParams{
AgentID: s.AgentID,
Slug: s.SlugOrPort,
Expand DownExpand Up@@ -2095,6 +2099,8 @@ func (q *FakeQuerier) GetTemplateAppInsights(ctx context.Context, arg database.G
})
}

// NOTE(mafredri): Add sorting if we decide on how to handle PostgreSQL collations.
// ORDER BY access_method, slug_or_port, display_name, icon, is_app
return rows, nil
}

Expand DownExpand Up@@ -2264,7 +2270,6 @@ func (q *FakeQuerier) GetTemplateDailyInsights(ctx context.Context, arg database
}
ds.userSet[s.UserID] = struct{}{}
ds.templateIDSet[s.TemplateID] = struct{}{}
break
}
}

Expand All@@ -2278,24 +2283,27 @@ func (q *FakeQuerier) GetTemplateDailyInsights(ctx context.Context, arg database
continue
}

w, err := q.getWorkspaceByIDNoLock(ctx, s.WorkspaceID)
if err != nil {
return nil, err
}

if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, w.TemplateID) {
continue
}

for _, ds := range dailyStats {
// (was.session_started_at >= ts.from_ AND was.session_started_at < ts.to_)
// OR (was.session_ended_at > ts.from_ AND was.session_ended_at < ts.to_)
// OR (was.session_started_at < ts.from_ AND was.session_ended_at >= ts.to_)
if !(((s.SessionStartedAt.After(arg.StartTime) || s.SessionStartedAt.Equal(arg.StartTime)) && s.SessionStartedAt.Before(arg.EndTime)) ||
(s.SessionEndedAt.After(arg.StartTime) && s.SessionEndedAt.Before(arg.EndTime)) ||
(s.SessionStartedAt.Before(arg.StartTime) && (s.SessionEndedAt.After(arg.EndTime) || s.SessionEndedAt.Equal(arg.EndTime)))) {
if !(((s.SessionStartedAt.After(ds.startTime) || s.SessionStartedAt.Equal(ds.startTime)) && s.SessionStartedAt.Before(ds.endTime)) ||
(s.SessionEndedAt.After(ds.startTime) && s.SessionEndedAt.Before(ds.endTime)) ||
(s.SessionStartedAt.Before(ds.startTime) && (s.SessionEndedAt.After(ds.endTime) || s.SessionEndedAt.Equal(ds.endTime)))) {
continue
}

w, err := q.getWorkspaceByIDNoLock(ctx, s.WorkspaceID)
if err != nil {
return nil, err
}

ds.userSet[s.UserID] = struct{}{}
ds.templateIDSet[w.TemplateID] = struct{}{}
break
}
}

Expand DownExpand Up@@ -2430,7 +2438,8 @@ func (q *FakeQuerier) GetTemplateParameterInsights(ctx context.Context, arg data
if tvp.TemplateVersionID != tv.ID {
continue
}
key := fmt.Sprintf("%s:%s:%s:%s", tvp.Name, tvp.DisplayName, tvp.Description, tvp.Options)
// GROUP BY tvp.name, tvp.type, tvp.display_name, tvp.description, tvp.options
key := fmt.Sprintf("%s:%s:%s:%s:%s", tvp.Name, tvp.Type, tvp.DisplayName, tvp.Description, tvp.Options)
if _, ok := uniqueTemplateParams[key]; !ok {
num++
uniqueTemplateParams[key] = &database.GetTemplateParameterInsightsRow{
Expand DownExpand Up@@ -2480,6 +2489,8 @@ func (q *FakeQuerier) GetTemplateParameterInsights(ctx context.Context, arg data
}
}

// NOTE(mafredri): Add sorting if we decide on how to handle PostgreSQL collations.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

out of curiosity: what is the default PostgreSQL behavior considering the "most default" configuration?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It will depend on the systems locale. Essentially if e.g. ifLC_ALL=C orLC_ALL=en_US.UTF-8 is set when callinginitdb, it will produce different results.

// ORDER BY utp.name, utp.type, utp.display_name, utp.description, utp.options, wbp.value
return rows, nil
}

Expand Down
8 changes: 4 additions & 4 deletionscoderd/database/queries.sql.go
View file
Open in desktop

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

8 changes: 4 additions & 4 deletionscoderd/database/queries/insights.sql
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -230,13 +230,13 @@ WITH latest_workspace_builds AS (
array_agg(DISTINCT wb.template_id)::uuid[] AS template_ids,
array_agg(wb.id)::uuid[] AS workspace_build_ids,
tvp.name,
tvp.type,
tvp.display_name,
tvp.description,
tvp.options,
tvp.type
tvp.options
FROM latest_workspace_builds wb
JOIN template_version_parameters tvp ON (tvp.template_version_id = wb.template_version_id)
GROUP BY tvp.name, tvp.display_name, tvp.description, tvp.options, tvp.type
GROUP BY tvp.name, tvp.type, tvp.display_name, tvp.description, tvp.options
)

SELECT
Expand All@@ -251,4 +251,4 @@ SELECT
COUNT(wbp.value) AS count
FROM unique_template_params utp
JOIN workspace_build_parameters wbp ON (utp.workspace_build_ids @> ARRAY[wbp.workspace_build_id] AND utp.name = wbp.name)
GROUP BY utp.num, utp.name, utp.display_name, utp.description, utp.options, utp.template_ids, utp.type, wbp.value;
GROUP BY utp.num, utp.template_ids, utp.name, utp.type, utp.display_name, utp.description, utp.options, wbp.value;
33 changes: 31 additions & 2 deletionscoderd/insights.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"strings"
"time"

"github.com/google/uuid"
Expand DownExpand Up@@ -288,8 +289,10 @@ func (api *API) insightsTemplates(rw http.ResponseWriter, r *http.Request) {
}
for _, row := range dailyUsage {
resp.IntervalReports = append(resp.IntervalReports, codersdk.TemplateInsightsIntervalReport{
StartTime: row.StartTime,
EndTime: row.EndTime,
// NOTE(mafredri): This might not be accurate over DST since the
// parsed location only contains the offset.
StartTime: row.StartTime.In(startTime.Location()),
EndTime: row.EndTime.In(startTime.Location()),
Interval: interval,
TemplateIDs: row.TemplateIDs,
ActiveUsers: row.ActiveUsers,
Expand DownExpand Up@@ -377,6 +380,32 @@ func convertTemplateInsightsApps(usage database.GetTemplateInsightsRow, appUsage
},
}

// Use a stable sort, similarly to how we would sort in the query, note that
// we don't sort in the query because order varies depending on the table
// collation.
//
// ORDER BY access_method, slug_or_port, display_name, icon, is_app
slices.SortFunc(appUsage, func(a, b database.GetTemplateAppInsightsRow) int {
if a.AccessMethod != b.AccessMethod {
return strings.Compare(a.AccessMethod, b.AccessMethod)
}
if a.SlugOrPort != b.SlugOrPort {
return strings.Compare(a.SlugOrPort, b.SlugOrPort)
}
if a.DisplayName.String != b.DisplayName.String {
return strings.Compare(a.DisplayName.String, b.DisplayName.String)
}
if a.Icon.String != b.Icon.String {
return strings.Compare(a.Icon.String, b.Icon.String)
}
if !a.IsApp && b.IsApp {
return -1
} else if a.IsApp && !b.IsApp {
return 1
}
return 0
})

// Template apps.
for _, app := range appUsage {
if !app.IsApp {
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp