mirror of
https://github.com/rancher/steve.git
synced 2025-09-07 18:31:13 +00:00
Hard-wire external associations: 3 sections in, this one is 4/7 (#645)
* Continue rebasing. * Wrote unit tests for external associations. * Fix the generated SQL. Some syntactic sugar (capitalizing the keywords), but use the 'ON' syntax on JOINs. * whitespace fix post rebase * We want "management.cattle.io.projects:spec.displayName" not "...spec.clusterName" * Fix the database calls: drop the key * Fix breakage during automatic rebase merging gone wrong. * ws fix - NFC * Post rebase-merge fixes * Fix rebase-driven merge. * Fix rebase breakage. * go-uber v0.5.2 prefers real arg names to '<argN>'
This commit is contained in:
@@ -14,7 +14,10 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/rancher/steve/pkg/sqlcache/sqltypes"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rancher/steve/pkg/sqlcache/db"
|
||||
@@ -22,6 +25,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.uber.org/mock/gomock"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func testStoreKeyFunc(obj interface{}) (string, error) {
|
||||
@@ -670,6 +674,96 @@ func TestResync(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type StringMatcher struct {
|
||||
expected string
|
||||
}
|
||||
|
||||
var ptn = regexp.MustCompile(`\s\s+`)
|
||||
|
||||
func dropWhiteSpace(s string) string {
|
||||
s1 := strings.TrimSpace(s)
|
||||
s2 := strings.ReplaceAll(s1, "\n", " ")
|
||||
s3 := strings.ReplaceAll(s2, "\r", " ")
|
||||
return ptn.ReplaceAllString(s3, " ")
|
||||
}
|
||||
|
||||
func (m StringMatcher) Matches(x any) bool {
|
||||
s, ok := x.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return dropWhiteSpace(s) == m.expected
|
||||
}
|
||||
|
||||
func (m StringMatcher) String() string {
|
||||
return m.expected
|
||||
}
|
||||
|
||||
func WSIgnoringMatcher(expected string) gomock.Matcher {
|
||||
return StringMatcher{
|
||||
expected: dropWhiteSpace(expected),
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddWithExternalUpdates(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
test func(t *testing.T)
|
||||
}
|
||||
testObject := testStoreObject{Id: "testStoreObject", Val: "a"}
|
||||
var tests []testCase
|
||||
tests = append(tests, testCase{description: "Add with no DB client errors", test: func(t *testing.T) {
|
||||
c, txC := SetupMockDB(t)
|
||||
stmts := NewMockStmt(gomock.NewController(t))
|
||||
store := SetupStoreWithExternalDependencies(t, c)
|
||||
|
||||
c.EXPECT().Upsert(txC, store.upsertStmt, "testStoreObject", testObject, store.shouldEncrypt).Return(nil)
|
||||
c.EXPECT().WithTransaction(gomock.Any(), true, gomock.Any()).Return(nil).Do(
|
||||
func(ctx context.Context, shouldEncrypt bool, f db.WithTransactionFunction) {
|
||||
err := f(txC)
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
}).Times(2)
|
||||
rawStmt := `SELECT DISTINCT f.key, ex2."spec.displayName" FROM "_v1_Namespace_fields" f
|
||||
LEFT OUTER JOIN "_v1_Namespace_labels" lt1 ON f.key = lt1.key
|
||||
JOIN "management.cattle.io_v3_Project_fields" ex2 ON lt1.value = ex2."metadata.name"
|
||||
WHERE lt1.label = ? AND f."spec.displayName" != ex2."spec.displayName"`
|
||||
c.EXPECT().Prepare(WSIgnoringMatcher(rawStmt))
|
||||
results1 := []any{"field.cattle.io/projectId"}
|
||||
c.EXPECT().QueryForRows(gomock.Any(), gomock.Any(), results1)
|
||||
c.EXPECT().ReadStrings2(gomock.Any()).Return([][]string{{"lego.cattle.io/fields1", "moose1"}}, nil)
|
||||
rawStmt2 := `UPDATE "_v1_Namespace_fields" SET "spec.displayName" = ? WHERE key = ?`
|
||||
c.EXPECT().Prepare(rawStmt2)
|
||||
txC.EXPECT().Stmt(gomock.Any()).Return(stmts)
|
||||
stmts.EXPECT().Exec("moose1", "lego.cattle.io/fields1")
|
||||
|
||||
rawStmt3 := `SELECT f.key, ex2."spec.projectName" FROM "_v1_Pods_fields" f
|
||||
JOIN "provisioner.cattle.io_v3_Cluster_fields" ex2 ON f."field.cattle.io/fixer" = ex2."metadata.name"
|
||||
WHERE f."spec.projectName" != ex2."spec.projectName"`
|
||||
c.EXPECT().Prepare(WSIgnoringMatcher(rawStmt3))
|
||||
results2 := []any{"field.cattle.io/fixer"}
|
||||
c.EXPECT().QueryForRows(gomock.Any(), gomock.Any(), results2)
|
||||
|
||||
c.EXPECT().ReadStrings2(gomock.Any()).Return([][]string{{"lego.cattle.io/fields2", "moose2"}}, nil)
|
||||
rawStmt4 := `UPDATE "_v1_Pods_fields" SET "spec.projectName" = ? WHERE key = ?`
|
||||
c.EXPECT().Prepare(rawStmt4)
|
||||
txC.EXPECT().Stmt(gomock.Any()).Return(stmts)
|
||||
stmts.EXPECT().Exec("moose2", "lego.cattle.io/fields2")
|
||||
|
||||
err := store.Add(testObject)
|
||||
assert.Nil(t, err)
|
||||
},
|
||||
})
|
||||
|
||||
t.Parallel()
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
test.test(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func SetupMockDB(t *testing.T) (*MockClient, *MockTXClient) {
|
||||
dbC := NewMockClient(gomock.NewController(t)) // add functionality once store expectation are known
|
||||
txC := NewMockTXClient(gomock.NewController(t))
|
||||
@@ -693,7 +787,42 @@ func SetupMockDB(t *testing.T) (*MockClient, *MockTXClient) {
|
||||
return dbC, txC
|
||||
}
|
||||
func SetupStore(t *testing.T, client *MockClient, shouldEncrypt bool) *Store {
|
||||
store, err := NewStore(context.Background(), testStoreObject{}, testStoreKeyFunc, client, shouldEncrypt, "testStoreObject")
|
||||
name := "testStoreObject"
|
||||
gvk := schema.GroupVersionKind{Group: "", Version: "v1", Kind: name}
|
||||
store, err := NewStore(context.Background(), testStoreObject{}, testStoreKeyFunc, client, shouldEncrypt, gvk, name, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
func gvkKey(group, version, kind string) string {
|
||||
return group + "_" + version + "_" + kind
|
||||
}
|
||||
|
||||
func SetupStoreWithExternalDependencies(t *testing.T, client *MockClient) *Store {
|
||||
name := "testStoreObject"
|
||||
gvk := schema.GroupVersionKind{Group: "", Version: "v1", Kind: name}
|
||||
namespaceProjectLabelDep := sqltypes.ExternalLabelDependency{
|
||||
SourceGVK: gvkKey("", "v1", "Namespace"),
|
||||
SourceLabelName: "field.cattle.io/projectId",
|
||||
TargetGVK: gvkKey("management.cattle.io", "v3", "Project"),
|
||||
TargetKeyFieldName: "metadata.name",
|
||||
TargetFinalFieldName: "spec.displayName",
|
||||
}
|
||||
namespaceNonLabelDep := sqltypes.ExternalDependency{
|
||||
SourceGVK: gvkKey("", "v1", "Pods"),
|
||||
SourceFieldName: "field.cattle.io/fixer",
|
||||
TargetGVK: gvkKey("provisioner.cattle.io", "v3", "Cluster"),
|
||||
TargetKeyFieldName: "metadata.name",
|
||||
TargetFinalFieldName: "spec.projectName",
|
||||
}
|
||||
updateInfo := sqltypes.ExternalGVKUpdates{
|
||||
AffectedGVK: schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"},
|
||||
ExternalDependencies: []sqltypes.ExternalDependency{namespaceNonLabelDep},
|
||||
ExternalLabelDependencies: []sqltypes.ExternalLabelDependency{namespaceProjectLabelDep},
|
||||
}
|
||||
store, err := NewStore(context.Background(), testStoreObject{}, testStoreKeyFunc, client, false, gvk, name, &updateInfo)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user