2023-02-17 22:26:12 +01:00
|
|
|
package testsuite
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"forge.cadoles.com/arcad/edge/pkg/storage"
|
|
|
|
"forge.cadoles.com/arcad/edge/pkg/storage/filter"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
type documentStoreOpsTestCase struct {
|
|
|
|
Name string
|
|
|
|
Run func(ctx context.Context, store storage.DocumentStore) error
|
|
|
|
}
|
|
|
|
|
|
|
|
var documentStoreOpsTestCases = []documentStoreOpsTestCase{
|
|
|
|
{
|
|
|
|
Name: "Basic query",
|
|
|
|
Run: func(ctx context.Context, store storage.DocumentStore) error {
|
|
|
|
collection := "simple_select"
|
|
|
|
|
|
|
|
docs := []storage.Document{
|
|
|
|
{
|
|
|
|
"attr1": "Foo",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"attr1": "Bar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, d := range docs {
|
|
|
|
if _, err := store.Upsert(ctx, collection, d); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
filter := filter.New(
|
|
|
|
filter.NewEqOperator(map[string]interface{}{
|
|
|
|
"attr1": "Foo",
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
|
2023-03-01 12:12:11 +01:00
|
|
|
results, err := store.Query(ctx, collection, filter)
|
2023-02-17 22:26:12 +01:00
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 1, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := "Foo", results[0]["attr1"]; e != g {
|
|
|
|
return errors.Errorf("results[0][\"Attr1\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
2023-02-21 15:05:01 +01:00
|
|
|
{
|
|
|
|
Name: "Query on _id",
|
|
|
|
Run: func(ctx context.Context, store storage.DocumentStore) error {
|
|
|
|
collection := "query_on_id"
|
|
|
|
|
|
|
|
doc := storage.Document{
|
|
|
|
"attr1": "Foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
upsertedDoc, err := store.Upsert(ctx, collection, doc)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
docID, ok := upsertedDoc.ID()
|
|
|
|
if !ok {
|
|
|
|
return errors.Errorf("")
|
|
|
|
}
|
|
|
|
|
|
|
|
filter := filter.New(
|
|
|
|
filter.NewEqOperator(map[string]interface{}{
|
|
|
|
"_id": docID,
|
|
|
|
}),
|
|
|
|
)
|
|
|
|
|
2023-03-01 12:12:11 +01:00
|
|
|
results, err := store.Query(ctx, collection, filter)
|
2023-02-21 15:05:01 +01:00
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 1, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
2023-02-17 22:26:12 +01:00
|
|
|
{
|
|
|
|
Name: "Query with 'IN' operator",
|
|
|
|
Run: func(ctx context.Context, store storage.DocumentStore) error {
|
|
|
|
docs := []storage.Document{
|
|
|
|
{
|
|
|
|
"counter": 1,
|
|
|
|
"tags": []string{"foo", "bar"},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"counter": 1,
|
|
|
|
"tags": []string{"nope"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
collection := "in_operator"
|
|
|
|
|
|
|
|
for _, doc := range docs {
|
|
|
|
if _, err := store.Upsert(ctx, collection, doc); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
filter := filter.New(
|
|
|
|
filter.NewAndOperator(
|
|
|
|
filter.NewEqOperator(map[string]any{
|
|
|
|
"counter": 1,
|
|
|
|
}),
|
|
|
|
filter.NewInOperator(map[string]any{
|
|
|
|
"tags": "foo",
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
2023-03-01 12:12:11 +01:00
|
|
|
results, err := store.Query(ctx, collection, filter)
|
2023-02-17 22:26:12 +01:00
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 1, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Double upsert",
|
|
|
|
Run: func(ctx context.Context, store storage.DocumentStore) error {
|
|
|
|
collection := "double_upsert"
|
|
|
|
|
|
|
|
oriDoc := storage.Document{
|
|
|
|
"attr1": "Foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
// Upsert document for the first time
|
|
|
|
upsertedDoc, err := store.Upsert(ctx, collection, oriDoc)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
id, exists := upsertedDoc.ID()
|
|
|
|
if !exists {
|
|
|
|
return errors.New("id, exists := upsertedDoc.ID(): 'exists' should be true")
|
|
|
|
}
|
|
|
|
|
|
|
|
if id == storage.DocumentID("") {
|
|
|
|
return errors.New("id, exists := upsertedDoc.ID(): 'id' should not be an empty string")
|
|
|
|
}
|
|
|
|
|
|
|
|
createdAt, exists := upsertedDoc.CreatedAt()
|
|
|
|
if !exists {
|
|
|
|
return errors.New("createdAt, exists := upsertedDoc.CreatedAt(): 'exists' should be true")
|
|
|
|
}
|
|
|
|
|
|
|
|
if createdAt.IsZero() {
|
|
|
|
return errors.New("createdAt, exists := upsertedDoc.CreatedAt(): 'createdAt' should not be zero time")
|
|
|
|
}
|
|
|
|
|
|
|
|
updatedAt, exists := upsertedDoc.UpdatedAt()
|
|
|
|
if !exists {
|
|
|
|
return errors.New("updatedAt, exists := upsertedDoc.UpdatedAt(): 'exists' should be true")
|
|
|
|
}
|
|
|
|
|
|
|
|
if updatedAt.IsZero() {
|
|
|
|
return errors.New("updatedAt, exists := upsertedDoc.UpdatedAt(): 'updatedAt' should not be zero time")
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := oriDoc["attr1"], upsertedDoc["attr1"]; e != g {
|
|
|
|
return errors.Errorf("upsertedDoc[\"attr1\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
2024-01-11 19:30:30 +01:00
|
|
|
upsertedDocRevision, _ := upsertedDoc.Revision()
|
|
|
|
if e, g := 0, upsertedDocRevision; e != g {
|
|
|
|
return errors.Errorf("upsertedDoc.Revision(): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
2023-02-17 22:26:12 +01:00
|
|
|
// Check that document does not have unexpected properties
|
2024-01-11 19:30:30 +01:00
|
|
|
if e, g := 5, len(upsertedDoc); e != g {
|
2023-02-17 22:26:12 +01:00
|
|
|
return errors.Errorf("len(upsertedDoc): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Upsert document for the second time
|
|
|
|
upsertedDoc2, err := store.Upsert(ctx, collection, upsertedDoc)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
prevID, _ := upsertedDoc.ID()
|
|
|
|
newID, _ := upsertedDoc2.ID()
|
|
|
|
|
|
|
|
if e, g := prevID, newID; e != g {
|
|
|
|
return errors.Errorf("newID: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
createdAt1, _ := upsertedDoc.CreatedAt()
|
|
|
|
createdAt2, _ := upsertedDoc2.CreatedAt()
|
|
|
|
|
|
|
|
if e, g := createdAt1, createdAt2; e != g {
|
|
|
|
return errors.Errorf("upsertedDoc2.CreatedAt(): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
updatedAt1, _ := upsertedDoc.UpdatedAt()
|
|
|
|
updatedAt2, _ := upsertedDoc2.UpdatedAt()
|
|
|
|
|
|
|
|
if e, g := updatedAt1, updatedAt2; e == g {
|
|
|
|
return errors.New("upsertedDoc2.UpdatedAt() should have been different than upsertedDoc.UpdatedAt()")
|
|
|
|
}
|
|
|
|
|
2024-01-11 19:30:30 +01:00
|
|
|
upsertedDoc2Revision, _ := upsertedDoc2.Revision()
|
|
|
|
if e, g := 1, upsertedDoc2Revision; e != g {
|
|
|
|
return errors.Errorf("upsertedDoc.Revision(): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
2023-02-17 22:26:12 +01:00
|
|
|
// Verify that there is no additional created document in the collection
|
|
|
|
|
2023-03-01 12:12:11 +01:00
|
|
|
results, err := store.Query(ctx, collection, nil)
|
2023-02-17 22:26:12 +01:00
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 1, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
2024-01-11 19:30:30 +01:00
|
|
|
firstResultRevision, _ := results[0].Revision()
|
|
|
|
if e, g := 1, firstResultRevision; e != g {
|
|
|
|
return errors.Errorf("results[0].Revision(): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
2023-03-01 12:12:11 +01:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Query order by document field",
|
|
|
|
Run: func(ctx context.Context, store storage.DocumentStore) error {
|
|
|
|
docs := []storage.Document{
|
|
|
|
{
|
|
|
|
"sortedField": 0,
|
|
|
|
"name": "Item 1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"sortedField": 1,
|
|
|
|
"name": "Item 2",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"sortedField": 2,
|
|
|
|
"name": "Item 3",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
collection := "ordered_query_by_document_field"
|
|
|
|
|
|
|
|
for _, doc := range docs {
|
|
|
|
if _, err := store.Upsert(ctx, collection, doc); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
results, err := store.Query(
|
|
|
|
ctx, collection, nil,
|
|
|
|
storage.WithOrderBy("sortedField"),
|
|
|
|
storage.WithOrderDirection(storage.OrderDirectionAsc),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 3, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[0]["name"], results[0]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[0][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[2]["name"], results[2]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[2][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
results, err = store.Query(
|
|
|
|
ctx, collection, nil,
|
|
|
|
storage.WithOrderBy("sortedField"),
|
|
|
|
storage.WithOrderDirection(storage.OrderDirectionDesc),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 3, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[2]["name"], results[0]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[0][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[0]["name"], results[2]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[2][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Query order by special attr",
|
|
|
|
Run: func(ctx context.Context, store storage.DocumentStore) error {
|
|
|
|
docs := []storage.Document{
|
|
|
|
{
|
|
|
|
"name": "Item 1",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "Item 2",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "Item 3",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
collection := "ordered_query_by_special_attr"
|
|
|
|
|
|
|
|
for _, doc := range docs {
|
|
|
|
if _, err := store.Upsert(ctx, collection, doc); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
results, err := store.Query(
|
|
|
|
ctx, collection, nil,
|
|
|
|
storage.WithOrderBy(storage.DocumentAttrCreatedAt),
|
|
|
|
storage.WithOrderDirection(storage.OrderDirectionAsc),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 3, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[0]["name"], results[0]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[0][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[2]["name"], results[2]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[2][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
results, err = store.Query(
|
|
|
|
ctx, collection, nil,
|
|
|
|
storage.WithOrderBy(storage.DocumentAttrCreatedAt),
|
|
|
|
storage.WithOrderDirection(storage.OrderDirectionDesc),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 3, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[2]["name"], results[0]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[0][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[0]["name"], results[2]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[2][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Query limit and offset",
|
|
|
|
Run: func(ctx context.Context, store storage.DocumentStore) error {
|
|
|
|
docs := []storage.Document{
|
|
|
|
{"name": "Item 1"},
|
|
|
|
{"name": "Item 2"},
|
|
|
|
{"name": "Item 3"},
|
|
|
|
{"name": "Item 4"},
|
|
|
|
}
|
|
|
|
|
|
|
|
collection := "query_limit_and_offset"
|
|
|
|
|
|
|
|
for _, doc := range docs {
|
|
|
|
if _, err := store.Upsert(ctx, collection, doc); err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
results, err := store.Query(
|
|
|
|
ctx, collection, nil,
|
|
|
|
storage.WithLimit(2),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 2, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[0]["name"], results[0]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[0][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[1]["name"], results[1]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[1][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
results, err = store.Query(
|
|
|
|
ctx, collection, nil,
|
|
|
|
storage.WithOffset(2),
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return errors.WithStack(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := 2, len(results); e != g {
|
|
|
|
return errors.Errorf("len(results): expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[2]["name"], results[0]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[0][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
|
|
|
if e, g := docs[3]["name"], results[1]["name"]; e != g {
|
|
|
|
return errors.Errorf("results[1][\"name\"]: expected '%v', got '%v'", e, g)
|
|
|
|
}
|
|
|
|
|
2023-02-17 22:26:12 +01:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-09-13 06:03:25 +02:00
|
|
|
func testDocumentStoreOps(ctx context.Context, t *testing.T, store storage.DocumentStore) {
|
2023-02-17 22:26:12 +01:00
|
|
|
for _, tc := range documentStoreOpsTestCases {
|
|
|
|
func(tc documentStoreOpsTestCase) {
|
|
|
|
t.Run(tc.Name, func(t *testing.T) {
|
2023-09-13 06:03:25 +02:00
|
|
|
if err := tc.Run(ctx, store); err != nil {
|
2023-02-17 22:26:12 +01:00
|
|
|
t.Errorf("%+v", errors.WithStack(err))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}(tc)
|
|
|
|
}
|
|
|
|
}
|