SQLTemplates: Add helper to ensure all templates have a test-case (#103964)

* SQLTemplates: Add helper to ensure all templates have a test-case associated

* UnifiedStorage: Add missing sql template test case

* LegacyDashboards: Add sql templates fs to test cases for exhaustiveness check

* RBACStore: Add sql templates fs to test cases for exhaustiveness check

* LegacyIAM: Add missing sql template test cases
This commit is contained in:
Matheus Macabu
2025-04-22 11:21:51 +02:00
committed by GitHub
parent 512df0091a
commit fc9f32a9f6
36 changed files with 513 additions and 4 deletions
@@ -30,7 +30,8 @@ func TestDashboardQueries(t *testing.T) {
}
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
RootDir: "testdata",
RootDir: "testdata",
SQLTemplatesFS: sqlTemplatesFS,
Templates: map[*template.Template][]mocks.TemplateTestCase{
sqlQueryDashboards: {
{
@@ -7,6 +7,7 @@ import (
"time"
claims "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/registry/apis/iam/common"
"github.com/grafana/grafana/pkg/storage/legacysql"
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
@@ -231,6 +232,10 @@ type listServiceAccountTokensQuery struct {
OrgUserTable string
}
func (listServiceAccountTokensQuery) Validate() error {
return nil // TODO
}
func (s *legacySQLStore) ListServiceAccountTokens(ctx context.Context, ns claims.NamespaceInfo, query ListServiceAccountTokenQuery) (*ListServiceAccountTokenResult, error) {
// for continue
query.Pagination.Limit += 1
+109 -1
View File
@@ -54,8 +54,21 @@ func TestIdentityQueries(t *testing.T) {
return &v
}
listServiceAccounts := func(q *ListServiceAccountsQuery) sqltemplate.SQLTemplate {
v := newListServiceAccounts(nodb, q)
v.SQLTemplate = mocks.NewTestingSQLTemplate()
return &v
}
listServiceAccountTokens := func(q *ListServiceAccountTokenQuery) sqltemplate.SQLTemplate {
v := newListServiceAccountTokens(nodb, q)
v.SQLTemplate = mocks.NewTestingSQLTemplate()
return &v
}
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
RootDir: "testdata",
RootDir: "testdata",
SQLTemplatesFS: sqlTemplatesFS,
Templates: map[*template.Template][]mocks.TemplateTestCase{
sqlQueryTeamsTemplate: {
{
@@ -192,6 +205,101 @@ func TestIdentityQueries(t *testing.T) {
}),
},
},
sqlQueryUserInternalIDTemplate: {
{
Name: "user_internal_id",
Data: &getUserInternalIDQuery{
SQLTemplate: mocks.NewTestingSQLTemplate(),
UserTable: nodb.Table("user"),
OrgUserTable: nodb.Table("org_user"),
Query: &GetUserInternalIDQuery{
UID: "user-1",
OrgID: 1,
},
},
},
},
sqlQueryTeamInternalIDTemplate: {
{
Name: "team_internal_id",
Data: &getTeamInternalIDQuery{
SQLTemplate: mocks.NewTestingSQLTemplate(),
TeamTable: nodb.Table("team"),
Query: &GetTeamInternalIDQuery{
UID: "team-1",
OrgID: 1,
},
},
},
},
sqlQueryServiceAccountInternalIDTemplate: {
{
Name: "basic",
Data: &getServiceAccountInternalIDQuery{
SQLTemplate: mocks.NewTestingSQLTemplate(),
UserTable: nodb.Table("user"),
OrgUserTable: nodb.Table("org_user"),
Query: &GetServiceAccountInternalIDQuery{
OrgID: 1,
UID: "sa-1",
},
},
},
},
sqlQueryServiceAccountsTemplate: {
{
Name: "service_accounts",
Data: listServiceAccounts(&ListServiceAccountsQuery{
UID: "sa-1",
OrgID: 1,
Pagination: common.Pagination{Limit: 1},
}),
},
{
Name: "service_accounts_page_1",
Data: listServiceAccounts(&ListServiceAccountsQuery{
OrgID: 1,
Pagination: common.Pagination{Limit: 5},
}),
},
{
Name: "service_accounts_page_2",
Data: listServiceAccounts(&ListServiceAccountsQuery{
OrgID: 1,
Pagination: common.Pagination{
Limit: 1,
Continue: 2,
},
}),
},
},
sqlQueryServiceAccountTokensTemplate: {
{
Name: "service_account_tokens",
Data: listServiceAccountTokens(&ListServiceAccountTokenQuery{
UID: "sa-1",
OrgID: 1,
Pagination: common.Pagination{Limit: 1},
}),
},
{
Name: "service_account_tokens_page_1",
Data: listServiceAccountTokens(&ListServiceAccountTokenQuery{
OrgID: 1,
Pagination: common.Pagination{Limit: 5},
}),
},
{
Name: "service_accounts_tokens_page_2",
Data: listServiceAccountTokens(&ListServiceAccountTokenQuery{
OrgID: 1,
Pagination: common.Pagination{
Limit: 1,
Continue: 2,
},
}),
},
},
},
})
}
@@ -0,0 +1,7 @@
SELECT u.id
FROM `grafana`.`user` as u
INNER JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.uid = 'sa-1'
AND u.is_service_account
LIMIT 1;
@@ -0,0 +1,16 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM `grafana`.`api_key` as t
INNER JOIN `grafana`.`user` as u ON t.service_account_id = u.id
INNER JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = 'sa-1'
ORDER BY t.id asc
LIMIT 1
@@ -0,0 +1,16 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM `grafana`.`api_key` as t
INNER JOIN `grafana`.`user` as u ON t.service_account_id = u.id
INNER JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = ''
ORDER BY t.id asc
LIMIT 5
@@ -0,0 +1,17 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM `grafana`.`api_key` as t
INNER JOIN `grafana`.`user` as u ON t.service_account_id = u.id
INNER JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = ''
AND t.id >= 2
ORDER BY t.id asc
LIMIT 1
@@ -0,0 +1,13 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = 'sa-1'
ORDER BY u.id asc
LIMIT 1
@@ -0,0 +1,12 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
ORDER BY u.id asc
LIMIT 5
@@ -0,0 +1,13 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.id >= 2
ORDER BY u.id asc
LIMIT 1
@@ -0,0 +1,5 @@
SELECT t.id
FROM `grafana`.`team` as t
WHERE t.org_id = 1
AND t.uid = 'team-1'
LIMIT 1;
@@ -0,0 +1,7 @@
SELECT u.id
FROM `grafana`.`user` as u
INNER JOIN `grafana`.`org_user` as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.uid = 'user-1'
AND NOT u.is_service_account
LIMIT 1;
@@ -0,0 +1,7 @@
SELECT u.id
FROM "grafana"."user" as u
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.uid = 'sa-1'
AND u.is_service_account
LIMIT 1;
@@ -0,0 +1,16 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM "grafana"."api_key" as t
INNER JOIN "grafana"."user" as u ON t.service_account_id = u.id
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = 'sa-1'
ORDER BY t.id asc
LIMIT 1
@@ -0,0 +1,16 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM "grafana"."api_key" as t
INNER JOIN "grafana"."user" as u ON t.service_account_id = u.id
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = ''
ORDER BY t.id asc
LIMIT 5
@@ -0,0 +1,17 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM "grafana"."api_key" as t
INNER JOIN "grafana"."user" as u ON t.service_account_id = u.id
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = ''
AND t.id >= 2
ORDER BY t.id asc
LIMIT 1
@@ -0,0 +1,13 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = 'sa-1'
ORDER BY u.id asc
LIMIT 1
@@ -0,0 +1,12 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
ORDER BY u.id asc
LIMIT 5
@@ -0,0 +1,13 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.id >= 2
ORDER BY u.id asc
LIMIT 1
@@ -0,0 +1,5 @@
SELECT t.id
FROM "grafana"."team" as t
WHERE t.org_id = 1
AND t.uid = 'team-1'
LIMIT 1;
@@ -0,0 +1,7 @@
SELECT u.id
FROM "grafana"."user" as u
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.uid = 'user-1'
AND NOT u.is_service_account
LIMIT 1;
@@ -0,0 +1,7 @@
SELECT u.id
FROM "grafana"."user" as u
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.uid = 'sa-1'
AND u.is_service_account
LIMIT 1;
@@ -0,0 +1,16 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM "grafana"."api_key" as t
INNER JOIN "grafana"."user" as u ON t.service_account_id = u.id
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = 'sa-1'
ORDER BY t.id asc
LIMIT 1
@@ -0,0 +1,16 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM "grafana"."api_key" as t
INNER JOIN "grafana"."user" as u ON t.service_account_id = u.id
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = ''
ORDER BY t.id asc
LIMIT 5
@@ -0,0 +1,17 @@
SELECT
t.id,
t.name,
t.is_revoked,
t.last_used_at,
t.expires,
t.created,
t.updated
FROM "grafana"."api_key" as t
INNER JOIN "grafana"."user" as u ON t.service_account_id = u.id
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = ''
AND t.id >= 2
ORDER BY t.id asc
LIMIT 1
@@ -0,0 +1,13 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.uid = 'sa-1'
ORDER BY u.id asc
LIMIT 1
@@ -0,0 +1,12 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
ORDER BY u.id asc
LIMIT 5
@@ -0,0 +1,13 @@
SELECT
u.id,
u.uid,
u.name,
u.is_disabled,
u.created,
u.updated
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.is_service_account
AND u.id >= 2
ORDER BY u.id asc
LIMIT 1
@@ -0,0 +1,5 @@
SELECT t.id
FROM "grafana"."team" as t
WHERE t.org_id = 1
AND t.uid = 'team-1'
LIMIT 1;
@@ -0,0 +1,7 @@
SELECT u.id
FROM "grafana"."user" as u
INNER JOIN "grafana"."org_user" as o ON u.id = o.user_id
WHERE o.org_id = 1
AND u.uid = 'user-1'
AND NOT u.is_service_account
LIMIT 1;
+2 -1
View File
@@ -42,7 +42,8 @@ func TestIdentityQueries(t *testing.T) {
}
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
RootDir: "testdata",
RootDir: "testdata",
SQLTemplatesFS: sqlTemplatesFS,
Templates: map[*template.Template][]mocks.TemplateTestCase{
sqlUserIdentifiers: {
{
+12 -1
View File
@@ -12,7 +12,8 @@ import (
func TestUnifiedStorageQueries(t *testing.T) {
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
RootDir: "testdata",
RootDir: "testdata",
SQLTemplatesFS: sqlTemplatesFS,
Templates: map[*template.Template][]mocks.TemplateTestCase{
sqlResourceDelete: {
{
@@ -335,6 +336,16 @@ func TestUnifiedStorageQueries(t *testing.T) {
},
},
sqlResourceVersionList: {
{
Name: "single path",
Data: &sqlResourceVersionListRequest{
SQLTemplate: mocks.NewTestingSQLTemplate(),
groupResourceVersion: new(groupResourceVersion),
},
},
},
sqlResourceStats: {
{
Name: "global",
@@ -2,6 +2,7 @@ package mocks
import (
"fmt"
"io/fs"
"os"
"path/filepath"
reflect "reflect"
@@ -10,6 +11,7 @@ import (
"text/template"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
sqltemplate "github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
@@ -82,6 +84,10 @@ type TemplateTestSetup struct {
// Check a set of templates against example inputs
Templates map[*template.Template][]TemplateTestCase
// The (embedded) filesystem containing the SQL query templates
// If not nil, a test will be run to ensure all templates in that folder have a test-case
SQLTemplatesFS fs.FS
}
func CheckQuerySnapshots(t *testing.T, setup TemplateTestSetup) {
@@ -96,6 +102,10 @@ func CheckQuerySnapshots(t *testing.T, setup TemplateTestSetup) {
}
}
if setup.SQLTemplatesFS != nil {
ensureAllTemplatesHaveTestCases(t, setup)
}
for tmpl, cases := range setup.Templates {
t.Run(tmpl.Name(), func(t *testing.T) {
t.Parallel()
@@ -146,3 +156,40 @@ func CheckQuerySnapshots(t *testing.T, setup TemplateTestSetup) {
})
}
}
func ensureAllTemplatesHaveTestCases(t *testing.T, setup TemplateTestSetup) {
t.Helper()
// Folder containing SQL query templates
sqlFiles := make([]string, 0, len(setup.Templates))
err := fs.WalkDir(setup.SQLTemplatesFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if name := d.Name(); strings.HasSuffix(name, ".sql") {
sqlFiles = append(sqlFiles, name)
}
return nil
})
require.NoError(t, err)
// Makes sure all SQL files in the folder have a test-case
for _, file := range sqlFiles {
found := false
for template := range setup.Templates {
if template.Name() == file {
found = true
break
}
}
assert.True(t, found, "File '%s' does not have a test case", file)
}
}
@@ -0,0 +1,6 @@
SELECT
`resource_version`,
`group`,
`resource`
FROM `resource_version`
;
@@ -0,0 +1,6 @@
SELECT
"resource_version",
"group",
"resource"
FROM "resource_version"
;
@@ -0,0 +1,6 @@
SELECT
"resource_version",
"group",
"resource"
FROM "resource_version"
;