mirror of
https://github.com/rancher/steve.git
synced 2025-09-25 14:49:38 +00:00
Convert tests to real SQL store (#652)
This commit is contained in:
@@ -946,7 +946,7 @@ func isLabelsFieldList(fields []string) bool {
|
|||||||
|
|
||||||
// toUnstructuredList turns a slice of unstructured objects into an unstructured.UnstructuredList
|
// toUnstructuredList turns a slice of unstructured objects into an unstructured.UnstructuredList
|
||||||
func toUnstructuredList(items []any) *unstructured.UnstructuredList {
|
func toUnstructuredList(items []any) *unstructured.UnstructuredList {
|
||||||
objectItems := make([]map[string]any, len(items))
|
objectItems := make([]any, len(items))
|
||||||
result := &unstructured.UnstructuredList{
|
result := &unstructured.UnstructuredList{
|
||||||
Items: make([]unstructured.Unstructured, len(items)),
|
Items: make([]unstructured.Unstructured, len(items)),
|
||||||
Object: map[string]interface{}{"items": objectItems},
|
Object: map[string]interface{}{"items": objectItems},
|
||||||
|
@@ -11,21 +11,54 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/rancher/steve/pkg/sqlcache/db"
|
"github.com/rancher/steve/pkg/sqlcache/db"
|
||||||
|
"github.com/rancher/steve/pkg/sqlcache/encryption"
|
||||||
"github.com/rancher/steve/pkg/sqlcache/partition"
|
"github.com/rancher/steve/pkg/sqlcache/partition"
|
||||||
"github.com/rancher/steve/pkg/sqlcache/sqltypes"
|
"github.com/rancher/steve/pkg/sqlcache/sqltypes"
|
||||||
|
"github.com/rancher/steve/pkg/sqlcache/store"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/mock/gomock"
|
"go.uber.org/mock/gomock"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func makeListOptionIndexer(ctx context.Context, fields [][]string) (*ListOptionIndexer, error) {
|
||||||
|
gvk := schema.GroupVersionKind{
|
||||||
|
Group: "",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
}
|
||||||
|
example := &unstructured.Unstructured{}
|
||||||
|
example.SetGroupVersionKind(gvk)
|
||||||
|
name := informerNameFromGVK(gvk)
|
||||||
|
m, err := encryption.NewManager()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := db.NewClient(nil, m, m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := store.NewStore(ctx, example, cache.DeletionHandlingMetaNamespaceKeyFunc, db, false, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listOptionIndexer, err := NewListOptionIndexer(ctx, fields, s, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return listOptionIndexer, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewListOptionIndexer(t *testing.T) {
|
func TestNewListOptionIndexer(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
description string
|
description string
|
||||||
@@ -264,26 +297,103 @@ func TestNewListOptionIndexer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListByOptions(t *testing.T) {
|
func TestNewListOptionIndexerEasy(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
description string
|
description string
|
||||||
listOptions sqltypes.ListOptions
|
listOptions sqltypes.ListOptions
|
||||||
partitions []partition.Partition
|
partitions []partition.Partition
|
||||||
ns string
|
ns string
|
||||||
expectedCountStmt string
|
|
||||||
expectedCountStmtArgs []any
|
items []*unstructured.Unstructured
|
||||||
expectedStmt string
|
|
||||||
expectedStmtArgs []any
|
extraIndexedFields [][]string
|
||||||
extraIndexedFields []string
|
|
||||||
expectedList *unstructured.UnstructuredList
|
expectedList *unstructured.UnstructuredList
|
||||||
returnList []any
|
expectedTotal int
|
||||||
expectedContToken string
|
expectedContToken string
|
||||||
expectedErr error
|
expectedErr error
|
||||||
}
|
}
|
||||||
|
foo := map[string]any{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": "obj1",
|
||||||
|
"namespace": "ns-a",
|
||||||
|
"somefield": "foo",
|
||||||
|
"sortfield": "4",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
bar := map[string]any{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": "obj2",
|
||||||
|
"namespace": "ns-a",
|
||||||
|
"somefield": "bar",
|
||||||
|
"sortfield": "1",
|
||||||
|
"labels": map[string]any{
|
||||||
|
"cows": "milk",
|
||||||
|
"horses": "saddles",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
baz := map[string]any{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": "obj3",
|
||||||
|
"namespace": "ns-a",
|
||||||
|
"somefield": "baz",
|
||||||
|
"sortfield": "2",
|
||||||
|
"labels": map[string]any{
|
||||||
|
"horses": "saddles",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"status": map[string]any{
|
||||||
|
"someotherfield": "helloworld",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
toto := map[string]any{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": "obj4",
|
||||||
|
"namespace": "ns-a",
|
||||||
|
"somefield": "toto",
|
||||||
|
"sortfield": "2",
|
||||||
|
"labels": map[string]any{
|
||||||
|
"cows": "milk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
lodgePole := map[string]any{
|
||||||
|
"metadata": map[string]any{
|
||||||
|
"name": "obj5",
|
||||||
|
"namespace": "ns-b",
|
||||||
|
"unknown": "hi",
|
||||||
|
"labels": map[string]any{
|
||||||
|
"guard.cattle.io": "lodgepole",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
testObject := testStoreObject{Id: "something", Val: "a"}
|
makeList := func(t *testing.T, objs ...map[string]any) *unstructured.UnstructuredList {
|
||||||
unstrTestObjectMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&testObject)
|
t.Helper()
|
||||||
assert.Nil(t, err)
|
|
||||||
|
if len(objs) == 0 {
|
||||||
|
return &unstructured.UnstructuredList{Object: map[string]any{"items": []any{}}, Items: []unstructured.Unstructured{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
var items []any
|
||||||
|
for _, obj := range objs {
|
||||||
|
items = append(items, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
list := &unstructured.Unstructured{
|
||||||
|
Object: map[string]any{
|
||||||
|
"items": items,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
itemList, err := list.ToList()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return itemList
|
||||||
|
}
|
||||||
|
itemList := makeList(t, foo, bar, baz, toto, lodgePole)
|
||||||
|
|
||||||
var tests []testCase
|
var tests []testCase
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
@@ -291,13 +401,8 @@ func TestListByOptions(t *testing.T) {
|
|||||||
listOptions: sqltypes.ListOptions{},
|
listOptions: sqltypes.ListOptions{},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 0,
|
||||||
WHERE
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
returnList: []any{},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{}}, Items: []unstructured.Unstructured{}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -308,12 +413,8 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 0,
|
||||||
WHERE
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{}}, Items: []unstructured.Unstructured{}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -324,7 +425,7 @@ func TestListByOptions(t *testing.T) {
|
|||||||
[]sqltypes.Filter{
|
[]sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"somevalue"},
|
Matches: []string{"foo"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: true,
|
Partial: true,
|
||||||
},
|
},
|
||||||
@@ -332,17 +433,10 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 1,
|
||||||
WHERE
|
|
||||||
(f."metadata.somefield" LIKE ? ESCAPE '\') AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"%somevalue%"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -353,7 +447,7 @@ func TestListByOptions(t *testing.T) {
|
|||||||
[]sqltypes.Filter{
|
[]sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"somevalue"},
|
Matches: []string{"foo"},
|
||||||
Op: sqltypes.NotEq,
|
Op: sqltypes.NotEq,
|
||||||
Partial: true,
|
Partial: true,
|
||||||
},
|
},
|
||||||
@@ -361,17 +455,10 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, bar, baz, toto, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 4,
|
||||||
WHERE
|
|
||||||
(f."metadata.somefield" NOT LIKE ? ESCAPE '\') AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"%somevalue%"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -382,7 +469,7 @@ func TestListByOptions(t *testing.T) {
|
|||||||
[]sqltypes.Filter{
|
[]sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"somevalue"},
|
Matches: []string{"o"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: true,
|
Partial: true,
|
||||||
},
|
},
|
||||||
@@ -390,17 +477,10 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, toto),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 2,
|
||||||
WHERE
|
|
||||||
(f."metadata.somefield" LIKE ? ESCAPE '\') AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"%somevalue%"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -411,19 +491,19 @@ func TestListByOptions(t *testing.T) {
|
|||||||
[]sqltypes.Filter{
|
[]sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"somevalue"},
|
Matches: []string{"foo"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: true,
|
Partial: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"someothervalue"},
|
Matches: []string{"bar"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: true,
|
Partial: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"somethirdvalue"},
|
Matches: []string{"toto"},
|
||||||
Op: sqltypes.NotEq,
|
Op: sqltypes.NotEq,
|
||||||
Partial: true,
|
Partial: true,
|
||||||
},
|
},
|
||||||
@@ -431,17 +511,10 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, bar, baz, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 4,
|
||||||
WHERE
|
|
||||||
((f."metadata.somefield" LIKE ? ESCAPE '\') OR (f."metadata.somefield" LIKE ? ESCAPE '\') OR (f."metadata.somefield" NOT LIKE ? ESCAPE '\')) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"%somevalue%", "%someothervalue%", "%somethirdvalue%"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -452,13 +525,13 @@ func TestListByOptions(t *testing.T) {
|
|||||||
Filters: []sqltypes.Filter{
|
Filters: []sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"value1"},
|
Matches: []string{"foo"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: false,
|
Partial: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Field: []string{"status", "someotherfield"},
|
Field: []string{"status", "someotherfield"},
|
||||||
Matches: []string{"value2"},
|
Matches: []string{"helloworld"},
|
||||||
Op: sqltypes.NotEq,
|
Op: sqltypes.NotEq,
|
||||||
Partial: false,
|
Partial: false,
|
||||||
},
|
},
|
||||||
@@ -468,7 +541,7 @@ func TestListByOptions(t *testing.T) {
|
|||||||
Filters: []sqltypes.Filter{
|
Filters: []sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"value3"},
|
Matches: []string{"toto"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: false,
|
Partial: false,
|
||||||
},
|
},
|
||||||
@@ -476,19 +549,10 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "test4",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, toto),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 1,
|
||||||
WHERE
|
|
||||||
((f."metadata.somefield" = ?) OR (f."status.someotherfield" != ?)) AND
|
|
||||||
(f."metadata.somefield" = ?) AND
|
|
||||||
(f."metadata.namespace" = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"value1", "value2", "value3", "test4"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -507,26 +571,17 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "test41",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 1,
|
||||||
LEFT OUTER JOIN "something_labels" lt1 ON o.key = lt1.key
|
|
||||||
WHERE
|
|
||||||
(lt1.label = ? AND lt1.value LIKE ? ESCAPE '\') AND
|
|
||||||
(f."metadata.namespace" = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"guard.cattle.io", "%lodgepole%", "test41"},
|
|
||||||
returnList: []any{},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{}}, Items: []unstructured.Unstructured{}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ListByOptions with two labels filters should use a self-join",
|
description: "ListByOptions with two labels filters should use a self-join",
|
||||||
listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{
|
listOptions: sqltypes.ListOptions{
|
||||||
|
Filters: []sqltypes.OrFilter{
|
||||||
{
|
{
|
||||||
Filters: []sqltypes.Filter{
|
Filters: []sqltypes.Filter{
|
||||||
{
|
{
|
||||||
@@ -549,25 +604,13 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "test42",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, bar),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 1,
|
||||||
LEFT OUTER JOIN "something_labels" lt1 ON o.key = lt1.key
|
|
||||||
LEFT OUTER JOIN "something_labels" lt2 ON o.key = lt2.key
|
|
||||||
WHERE
|
|
||||||
(lt1.label = ? AND lt1.value = ?) AND
|
|
||||||
(lt2.label = ? AND lt2.value = ?) AND
|
|
||||||
(f."metadata.namespace" = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"cows", "milk", "horses", "saddles", "test42"},
|
|
||||||
returnList: []any{},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{}}, Items: []unstructured.Unstructured{}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ListByOptions with a mix of one label and one non-label query can still self-join",
|
description: "ListByOptions with a mix of one label and one non-label query can still self-join",
|
||||||
listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{
|
listOptions: sqltypes.ListOptions{Filters: []sqltypes.OrFilter{
|
||||||
@@ -575,7 +618,7 @@ func TestListByOptions(t *testing.T) {
|
|||||||
Filters: []sqltypes.Filter{
|
Filters: []sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "labels", "cows"},
|
Field: []string{"metadata", "labels", "cows"},
|
||||||
Matches: []string{"butter"},
|
Matches: []string{"milk"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: false,
|
Partial: false,
|
||||||
},
|
},
|
||||||
@@ -585,7 +628,7 @@ func TestListByOptions(t *testing.T) {
|
|||||||
Filters: []sqltypes.Filter{
|
Filters: []sqltypes.Filter{
|
||||||
{
|
{
|
||||||
Field: []string{"metadata", "somefield"},
|
Field: []string{"metadata", "somefield"},
|
||||||
Matches: []string{"wheat"},
|
Matches: []string{"toto"},
|
||||||
Op: sqltypes.Eq,
|
Op: sqltypes.Eq,
|
||||||
Partial: false,
|
Partial: false,
|
||||||
},
|
},
|
||||||
@@ -593,24 +636,13 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "test43",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, toto),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 1,
|
||||||
LEFT OUTER JOIN "something_labels" lt1 ON o.key = lt1.key
|
|
||||||
WHERE
|
|
||||||
(lt1.label = ? AND lt1.value = ?) AND
|
|
||||||
(f."metadata.somefield" = ?) AND
|
|
||||||
(f."metadata.namespace" = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"cows", "butter", "wheat", "test43"},
|
|
||||||
returnList: []any{},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{}}, Items: []unstructured.Unstructured{}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ListByOptions with only one Sort.Field set should sort on that field only, in ascending order in prepared sql.Stmt",
|
description: "ListByOptions with only one Sort.Field set should sort on that field only, in ascending order in prepared sql.Stmt",
|
||||||
listOptions: sqltypes.ListOptions{
|
listOptions: sqltypes.ListOptions{
|
||||||
@@ -623,21 +655,13 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "test5",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, lodgePole, bar, baz, foo, toto),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
WHERE
|
|
||||||
(f."metadata.namespace" = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.somefield" ASC`,
|
|
||||||
expectedStmtArgs: []any{"test5"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "sort one field descending",
|
description: "sort one field descending",
|
||||||
listOptions: sqltypes.ListOptions{
|
listOptions: sqltypes.ListOptions{
|
||||||
@@ -650,164 +674,132 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "test5a",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, toto, foo, baz, bar, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
WHERE
|
|
||||||
(f."metadata.namespace" = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.somefield" DESC`,
|
|
||||||
expectedStmtArgs: []any{"test5a"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "sort one unbound label descending",
|
description: "sort one unbound field descending",
|
||||||
listOptions: sqltypes.ListOptions{
|
listOptions: sqltypes.ListOptions{
|
||||||
SortList: sqltypes.SortList{
|
SortList: sqltypes.SortList{
|
||||||
SortDirectives: []sqltypes.Sort{
|
SortDirectives: []sqltypes.Sort{
|
||||||
{
|
{
|
||||||
Fields: []string{"metadata", "labels", "flip"},
|
Fields: []string{"metadata", "unknown"},
|
||||||
Order: sqltypes.DESC,
|
Order: sqltypes.DESC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "test5a",
|
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
|
||||||
LEFT OUTER JOIN "something_labels" lt1 ON o.key = lt1.key
|
|
||||||
WHERE
|
|
||||||
(lt1.label = ?) AND
|
|
||||||
(f."metadata.namespace" = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY (CASE lt1.label WHEN ? THEN lt1.value ELSE NULL END) DESC NULLS FIRST`,
|
|
||||||
expectedStmtArgs: []any{"flip", "test5a", "flip"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
|
||||||
expectedErr: nil,
|
|
||||||
})
|
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
|
||||||
description: "ListByOptions sorting on two complex fields should sort on the first field in ascending order first and then sort on the second labels field in ascending order in prepared sql.Stmt",
|
|
||||||
listOptions: sqltypes.ListOptions{
|
|
||||||
SortList: sqltypes.SortList{
|
|
||||||
SortDirectives: []sqltypes.Sort{
|
|
||||||
{
|
|
||||||
Fields: []string{"metadata", "fields", "3"},
|
|
||||||
Order: sqltypes.ASC,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Fields: []string{"metadata", "labels", "stub.io/candy"},
|
|
||||||
Order: sqltypes.ASC,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
extraIndexedFields: []string{"metadata.fields[3]", "metadata.labels[stub.io/candy]"},
|
|
||||||
partitions: []partition.Partition{},
|
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT DISTINCT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, lodgePole, toto, baz, bar, foo),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
LEFT OUTER JOIN "something_labels" lt1 ON o.key = lt1.key
|
|
||||||
WHERE
|
|
||||||
(lt1.label = ?) AND
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.fields[3]" ASC, (CASE lt1.label WHEN ? THEN lt1.value ELSE NULL END) ASC NULLS LAST`,
|
|
||||||
expectedStmtArgs: []any{"stub.io/candy", "stub.io/candy"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
// tests = append(tests, testCase{
|
||||||
|
// description: "sort one unbound label descending",
|
||||||
|
// listOptions: sqltypes.ListOptions{
|
||||||
|
// SortList: sqltypes.SortList{
|
||||||
|
// SortDirectives: []sqltypes.Sort{
|
||||||
|
// {
|
||||||
|
// Fields: []string{"metadata", "labels", "flip"},
|
||||||
|
// Order: sqltypes.DESC,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// partitions: []partition.Partition{{All: true}},
|
||||||
|
// ns: "",
|
||||||
|
// expectedList: makeList(t, lodgePole, toto, baz, bar, foo),
|
||||||
|
// expectedTotal: 5,
|
||||||
|
// expectedContToken: "",
|
||||||
|
// expectedErr: nil,
|
||||||
|
// })
|
||||||
|
// tests = append(tests, testCase{
|
||||||
|
// description: "ListByOptions sorting on two complex fields should sort on the first field in ascending order first and then sort on the second labels field in ascending order in prepared sql.Stmt",
|
||||||
|
// listOptions: sqltypes.ListOptions{
|
||||||
|
// SortList: sqltypes.SortList{
|
||||||
|
// SortDirectives: []sqltypes.Sort{
|
||||||
|
// {
|
||||||
|
// Fields: []string{"metadata", "sortfield"},
|
||||||
|
// Order: sqltypes.ASC,
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Fields: []string{"metadata", "labels", "cows"},
|
||||||
|
// Order: sqltypes.ASC,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// partitions: []partition.Partition{{All: true}},
|
||||||
|
// ns: "",
|
||||||
|
// expectedList: makeList(t),
|
||||||
|
// expectedTotal: 5,
|
||||||
|
// expectedContToken: "",
|
||||||
|
// expectedErr: nil,
|
||||||
|
// })
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ListByOptions sorting on two fields should sort on the first field in ascending order first and then sort on the second field in ascending order in prepared sql.Stmt",
|
description: "ListByOptions sorting on two fields should sort on the first field in ascending order first and then sort on the second field in ascending order in prepared sql.Stmt",
|
||||||
listOptions: sqltypes.ListOptions{
|
listOptions: sqltypes.ListOptions{
|
||||||
SortList: sqltypes.SortList{
|
SortList: sqltypes.SortList{
|
||||||
SortDirectives: []sqltypes.Sort{
|
SortDirectives: []sqltypes.Sort{
|
||||||
{
|
{
|
||||||
Fields: []string{"metadata", "somefield"},
|
Fields: []string{"metadata", "sortfield"},
|
||||||
Order: sqltypes.ASC,
|
Order: sqltypes.ASC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Fields: []string{"status", "someotherfield"},
|
Fields: []string{"metadata", "somefield"},
|
||||||
Order: sqltypes.ASC,
|
Order: sqltypes.ASC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, lodgePole, bar, baz, toto, foo),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
WHERE
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.somefield" ASC, f."status.someotherfield" ASC`,
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ListByOptions sorting on two fields should sort on the first field in descending order first and then sort on the second field in ascending order in prepared sql.Stmt",
|
description: "ListByOptions sorting on two fields should sort on the first field in descending order first and then sort on the second field in ascending order in prepared sql.Stmt",
|
||||||
listOptions: sqltypes.ListOptions{
|
listOptions: sqltypes.ListOptions{
|
||||||
SortList: sqltypes.SortList{
|
SortList: sqltypes.SortList{
|
||||||
SortDirectives: []sqltypes.Sort{
|
SortDirectives: []sqltypes.Sort{
|
||||||
{
|
{
|
||||||
Fields: []string{"metadata", "somefield"},
|
Fields: []string{"metadata", "sortfield"},
|
||||||
Order: sqltypes.DESC,
|
Order: sqltypes.DESC,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Fields: []string{"status", "someotherfield"},
|
Fields: []string{"metadata", "somefield"},
|
||||||
Order: sqltypes.ASC,
|
Order: sqltypes.ASC,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, baz, toto, bar, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
WHERE
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.somefield" DESC, f."status.someotherfield" ASC`,
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ListByOptions with Pagination.PageSize set should set limit to PageSize in prepared sql.Stmt",
|
description: "ListByOptions with Pagination.PageSize set should set limit to PageSize in prepared sql.Stmt",
|
||||||
listOptions: sqltypes.ListOptions{
|
listOptions: sqltypes.ListOptions{
|
||||||
Pagination: sqltypes.Pagination{
|
Pagination: sqltypes.Pagination{
|
||||||
PageSize: 10,
|
PageSize: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, bar, baz),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
WHERE
|
expectedContToken: "3",
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC
|
|
||||||
LIMIT ?`,
|
|
||||||
expectedStmtArgs: []any{10},
|
|
||||||
expectedCountStmt: `SELECT COUNT(*) FROM (SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
|
||||||
WHERE
|
|
||||||
(FALSE))`,
|
|
||||||
expectedCountStmtArgs: []any{},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
@@ -817,67 +809,27 @@ func TestListByOptions(t *testing.T) {
|
|||||||
Page: 2,
|
Page: 2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
partitions: []partition.Partition{},
|
partitions: []partition.Partition{{All: true}},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, bar, baz, toto, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
WHERE
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
|
||||||
expectedErr: nil,
|
|
||||||
})
|
|
||||||
tests = append(tests, testCase{
|
|
||||||
description: "ListByOptions with Pagination.Page and PageSize set limit to PageSize and offset to PageSize * (Page - 1) in prepared sql.Stmt",
|
|
||||||
listOptions: sqltypes.ListOptions{
|
|
||||||
Pagination: sqltypes.Pagination{
|
|
||||||
PageSize: 10,
|
|
||||||
Page: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
partitions: []partition.Partition{},
|
|
||||||
ns: "",
|
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
|
||||||
WHERE
|
|
||||||
(FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC
|
|
||||||
LIMIT ?
|
|
||||||
OFFSET ?`,
|
|
||||||
expectedStmtArgs: []any{10, 10},
|
|
||||||
|
|
||||||
expectedCountStmt: `SELECT COUNT(*) FROM (SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
|
||||||
WHERE
|
|
||||||
(FALSE))`,
|
|
||||||
expectedCountStmtArgs: []any{},
|
|
||||||
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
|
||||||
expectedErr: nil,
|
|
||||||
})
|
|
||||||
tests = append(tests, testCase{
|
|
||||||
description: "ListByOptions with a Namespace Partition should select only items where metadata.namespace is equal to Namespace and all other conditions are met in prepared sql.Stmt",
|
|
||||||
partitions: []partition.Partition{
|
|
||||||
{
|
|
||||||
Namespace: "somens",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ns: "",
|
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
|
||||||
WHERE
|
|
||||||
(f."metadata.namespace" = ? AND FALSE)
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"somens"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
|
// tests = append(tests, testCase{
|
||||||
|
// description: "ListByOptions with a Namespace Partition should select only items where metadata.namespace is equal to Namespace and all other conditions are met in prepared sql.Stmt",
|
||||||
|
// partitions: []partition.Partition{
|
||||||
|
// {
|
||||||
|
// Namespace: "ns-b",
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// // XXX: Why do I need to specify the namespace here too?
|
||||||
|
// ns: "ns-b",
|
||||||
|
// expectedList: makeList(t, lodgePole),
|
||||||
|
// expectedTotal: 1,
|
||||||
|
// expectedContToken: "",
|
||||||
|
// expectedErr: nil,
|
||||||
|
// })
|
||||||
tests = append(tests, testCase{
|
tests = append(tests, testCase{
|
||||||
description: "ListByOptions with a All Partition should select all items that meet all other conditions in prepared sql.Stmt",
|
description: "ListByOptions with a All Partition should select all items that meet all other conditions in prepared sql.Stmt",
|
||||||
partitions: []partition.Partition{
|
partitions: []partition.Partition{
|
||||||
@@ -886,11 +838,8 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, bar, baz, toto, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -902,11 +851,8 @@ func TestListByOptions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, bar, baz, toto, lodgePole),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 5,
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
@@ -914,95 +860,43 @@ func TestListByOptions(t *testing.T) {
|
|||||||
description: "ListByOptions with a Names Partition should select only items where metadata.name equals an items in Names and all other conditions are met in prepared sql.Stmt",
|
description: "ListByOptions with a Names Partition should select only items where metadata.name equals an items in Names and all other conditions are met in prepared sql.Stmt",
|
||||||
partitions: []partition.Partition{
|
partitions: []partition.Partition{
|
||||||
{
|
{
|
||||||
Names: sets.New[string]("someid", "someotherid"),
|
Names: sets.New("obj1", "obj2"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ns: "",
|
ns: "",
|
||||||
expectedStmt: `SELECT o.object, o.objectnonce, o.dekid FROM "something" o
|
expectedList: makeList(t, foo, bar),
|
||||||
JOIN "something_fields" f ON o.key = f.key
|
expectedTotal: 2,
|
||||||
WHERE
|
|
||||||
(f."metadata.name" IN (?, ?))
|
|
||||||
ORDER BY f."metadata.name" ASC `,
|
|
||||||
expectedStmtArgs: []any{"someid", "someotherid"},
|
|
||||||
returnList: []any{&unstructured.Unstructured{Object: unstrTestObjectMap}, &unstructured.Unstructured{Object: unstrTestObjectMap}},
|
|
||||||
expectedList: &unstructured.UnstructuredList{Object: map[string]interface{}{"items": []map[string]interface{}{unstrTestObjectMap, unstrTestObjectMap}}, Items: []unstructured.Unstructured{{Object: unstrTestObjectMap}, {Object: unstrTestObjectMap}}},
|
|
||||||
expectedContToken: "",
|
expectedContToken: "",
|
||||||
expectedErr: nil,
|
expectedErr: nil,
|
||||||
})
|
})
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.description, func(t *testing.T) {
|
t.Run(test.description, func(t *testing.T) {
|
||||||
txClient := NewMockTXClient(gomock.NewController(t))
|
fields := [][]string{
|
||||||
store := NewMockStore(gomock.NewController(t))
|
{"metadata", "somefield"},
|
||||||
stmts := NewMockStmt(gomock.NewController(t))
|
{"status", "someotherfield"},
|
||||||
i := &Indexer{
|
{"metadata", "unknown"},
|
||||||
Store: store,
|
{"metadata", "sortfield"},
|
||||||
}
|
}
|
||||||
lii := &ListOptionIndexer{
|
fields = append(fields, test.extraIndexedFields...)
|
||||||
Indexer: i,
|
|
||||||
indexedFields: []string{"metadata.somefield", "status.someotherfield"},
|
loi, err := makeListOptionIndexer(ctx, fields)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
for _, item := range itemList.Items {
|
||||||
|
err = loi.Add(&item)
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
if len(test.extraIndexedFields) > 0 {
|
|
||||||
lii.indexedFields = append(lii.indexedFields, test.extraIndexedFields...)
|
list, total, contToken, err := loi.ListByOptions(ctx, &test.listOptions, test.partitions, test.ns)
|
||||||
}
|
|
||||||
queryInfo, err := lii.constructQuery(&test.listOptions, test.partitions, test.ns, "something")
|
|
||||||
if test.expectedErr != nil {
|
if test.expectedErr != nil {
|
||||||
assert.Equal(t, test.expectedErr, err)
|
assert.Error(t, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, test.expectedStmt, queryInfo.query)
|
|
||||||
if test.expectedStmtArgs == nil {
|
|
||||||
test.expectedStmtArgs = []any{}
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.expectedStmtArgs, queryInfo.params)
|
|
||||||
assert.Equal(t, test.expectedCountStmt, queryInfo.countQuery)
|
|
||||||
assert.Equal(t, test.expectedCountStmtArgs, queryInfo.countParams)
|
|
||||||
|
|
||||||
stmt := &sql.Stmt{}
|
|
||||||
rows := &sql.Rows{}
|
|
||||||
objType := reflect.TypeOf(testObject)
|
|
||||||
txClient.EXPECT().Stmt(gomock.Any()).Return(stmts).AnyTimes()
|
|
||||||
store.EXPECT().Prepare(test.expectedStmt).Do(func(a ...any) {
|
|
||||||
fmt.Println(a)
|
|
||||||
}).Return(stmt)
|
|
||||||
if args := test.expectedStmtArgs; args != nil {
|
|
||||||
stmts.EXPECT().QueryContext(gomock.Any(), gomock.Any()).Return(rows, nil).AnyTimes()
|
|
||||||
} else if strings.Contains(test.expectedStmt, "LIMIT") {
|
|
||||||
stmts.EXPECT().QueryContext(gomock.Any(), args...).Return(rows, nil)
|
|
||||||
txClient.EXPECT().Stmt(gomock.Any()).Return(stmts)
|
|
||||||
stmts.EXPECT().QueryContext(gomock.Any()).Return(rows, nil)
|
|
||||||
} else {
|
|
||||||
stmts.EXPECT().QueryContext(gomock.Any()).Return(rows, nil)
|
|
||||||
}
|
|
||||||
store.EXPECT().GetType().Return(objType)
|
|
||||||
store.EXPECT().GetShouldEncrypt().Return(false)
|
|
||||||
store.EXPECT().ReadObjects(rows, objType, false).Return(test.returnList, nil)
|
|
||||||
store.EXPECT().CloseStmt(stmt).Return(nil)
|
|
||||||
|
|
||||||
store.EXPECT().WithTransaction(gomock.Any(), false, gomock.Any()).Return(nil).Do(
|
|
||||||
func(ctx context.Context, shouldEncrypt bool, f db.WithTransactionFunction) {
|
|
||||||
err := f(txClient)
|
|
||||||
if test.expectedErr == nil {
|
|
||||||
assert.Nil(t, err)
|
|
||||||
} else {
|
|
||||||
assert.Equal(t, test.expectedErr, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if test.expectedCountStmt != "" {
|
|
||||||
store.EXPECT().Prepare(test.expectedCountStmt).Return(stmt)
|
|
||||||
store.EXPECT().ReadInt(rows).Return(len(test.expectedList.Items), nil)
|
|
||||||
store.EXPECT().CloseStmt(stmt).Return(nil)
|
|
||||||
}
|
|
||||||
list, total, contToken, err := lii.executeQuery(context.Background(), queryInfo)
|
|
||||||
if test.expectedErr == nil {
|
|
||||||
assert.Nil(t, err)
|
|
||||||
} else {
|
|
||||||
assert.Equal(t, test.expectedErr, err)
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.expectedList, list)
|
assert.Equal(t, test.expectedList, list)
|
||||||
assert.Equal(t, len(test.expectedList.Items), total)
|
assert.Equal(t, test.expectedTotal, total)
|
||||||
assert.Equal(t, test.expectedContToken, contToken)
|
assert.Equal(t, test.expectedContToken, contToken)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user