345 lines
8.9 KiB
Go
345 lines
8.9 KiB
Go
package testsuite
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"forge.cadoles.com/arcad/edge/pkg/app"
|
|
"forge.cadoles.com/arcad/edge/pkg/storage/share"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type repositoryTestCase struct {
|
|
Name string
|
|
Skip bool
|
|
Run func(ctx context.Context, t *testing.T, store share.Store) error
|
|
}
|
|
|
|
var repositoryTestCases = []repositoryTestCase{
|
|
{
|
|
Name: "Update resource attributes",
|
|
Skip: false,
|
|
Run: func(ctx context.Context, t *testing.T, store share.Store) error {
|
|
origin := app.ID("test")
|
|
resourceID := share.ResourceID("test")
|
|
|
|
// Try to create resource without attributes
|
|
_, err := store.UpdateAttributes(ctx, origin, resourceID)
|
|
if err == nil {
|
|
return errors.New("err should not be nil")
|
|
}
|
|
|
|
if !errors.Is(err, share.ErrAttributeRequired) {
|
|
return errors.Errorf("err: expected share.ErrAttributeRequired, got '%+v'", err)
|
|
}
|
|
|
|
attributes := []share.Attribute{
|
|
share.NewBaseAttribute("my_text_attr", share.TypeText, "foo"),
|
|
share.NewBaseAttribute("my_number_attr", share.TypeNumber, 5),
|
|
share.NewBaseAttribute("my_path_attr", share.TypePath, "/my/path"),
|
|
share.NewBaseAttribute("my_bool_attr", share.TypeBool, true),
|
|
}
|
|
|
|
resource, err := store.UpdateAttributes(ctx, origin, resourceID, attributes...)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
isNil := reflect.ValueOf(resource).IsNil()
|
|
if isNil {
|
|
return errors.New("resource should not be nil")
|
|
}
|
|
|
|
if e, g := resourceID, resource.ID(); e != g {
|
|
return errors.Errorf("resource.ID(): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
if e, g := origin, resource.Origin(); e != g {
|
|
return errors.Errorf("resource.Origin(): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
if e, g := 4, len(resource.Attributes()); e != g {
|
|
return errors.Errorf("len(resource.Attributes()): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "Find resources by attribute name",
|
|
Skip: false,
|
|
Run: func(ctx context.Context, t *testing.T, store share.Store) error {
|
|
if err := loadTestData(ctx, "testdata/find_resources_by_attribute_name.json", store); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
resources, err := store.FindResources(ctx, share.WithName("my_number"))
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
isNil := reflect.ValueOf(resources).IsNil()
|
|
if isNil {
|
|
return errors.New("resources should not be nil")
|
|
}
|
|
|
|
if e, g := 2, len(resources); e != g {
|
|
return errors.Errorf("len(resources): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "Find resources by attribute type",
|
|
Skip: false,
|
|
Run: func(ctx context.Context, t *testing.T, store share.Store) error {
|
|
if err := loadTestData(ctx, "testdata/find_resources_by_attribute_type.json", store); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
resources, err := store.FindResources(ctx, share.WithType(share.TypePath))
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
isNil := reflect.ValueOf(resources).IsNil()
|
|
if isNil {
|
|
return errors.New("resources should not be nil")
|
|
}
|
|
|
|
if e, g := 1, len(resources); e != g {
|
|
return errors.Errorf("len(resources): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "Find resources by attribute type and name",
|
|
Skip: false,
|
|
Run: func(ctx context.Context, t *testing.T, store share.Store) error {
|
|
if err := loadTestData(ctx, "testdata/find_resources_by_attribute_type_and_name.json", store); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
resources, err := store.FindResources(ctx, share.WithType(share.TypeText), share.WithName("my_attr"))
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
isNil := reflect.ValueOf(resources).IsNil()
|
|
if isNil {
|
|
return errors.New("resources should not be nil")
|
|
}
|
|
|
|
if e, g := 1, len(resources); e != g {
|
|
return errors.Errorf("len(resources): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "Get resource",
|
|
Skip: false,
|
|
Run: func(ctx context.Context, t *testing.T, store share.Store) error {
|
|
if err := loadTestData(ctx, "testdata/get_resource.json", store); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
origin := app.ID("app1.edge.app")
|
|
resourceID := share.ResourceID("res-1")
|
|
|
|
resource, err := store.GetResource(ctx, origin, resourceID)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
isNil := reflect.ValueOf(resource).IsNil()
|
|
if isNil {
|
|
return errors.New("resources should not be nil")
|
|
}
|
|
|
|
if e, g := origin, resource.Origin(); e != g {
|
|
return errors.Errorf("resource.Origin(): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
if e, g := resourceID, resource.ID(); e != g {
|
|
return errors.Errorf("resource.ID(): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
resource, err = store.GetResource(ctx, origin, "unexistant-id")
|
|
if err == nil {
|
|
return errors.New("err should not be nil")
|
|
}
|
|
|
|
if !errors.Is(err, share.ErrNotFound) {
|
|
return errors.Errorf("err: expected share.ErrNotFound, got '%+v'", err)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "Delete resource",
|
|
Skip: false,
|
|
Run: func(ctx context.Context, t *testing.T, store share.Store) error {
|
|
if err := loadTestData(ctx, "testdata/delete_resource.json", store); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
origin := app.ID("app1.edge.app")
|
|
resourceID := share.ResourceID("res-1")
|
|
|
|
// It should delete an existing resource
|
|
if err := store.DeleteResource(ctx, origin, resourceID); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
_, err := store.GetResource(ctx, origin, resourceID)
|
|
if err == nil {
|
|
return errors.New("err should not be nil")
|
|
}
|
|
|
|
// The resource should be deleted
|
|
if !errors.Is(err, share.ErrNotFound) {
|
|
return errors.Errorf("err: expected share.ErrNotFound, got '%+v'", err)
|
|
}
|
|
|
|
// It should not delete an unexistant resource
|
|
err = store.DeleteResource(ctx, origin, resourceID)
|
|
if err == nil {
|
|
return errors.New("err should not be nil")
|
|
}
|
|
|
|
if !errors.Is(err, share.ErrNotFound) {
|
|
return errors.Errorf("err: expected share.ErrNotFound, got '%+v'", err)
|
|
}
|
|
|
|
otherOrigin := app.ID("app2.edge.app")
|
|
|
|
// It should not delete a resource with the same id and another origin
|
|
resource, err := store.GetResource(ctx, otherOrigin, resourceID)
|
|
if err != nil {
|
|
return errors.New("err should not be nil")
|
|
}
|
|
|
|
if e, g := otherOrigin, resource.Origin(); e != g {
|
|
return errors.Errorf("resource.Origin(): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
{
|
|
Name: "Delete attributes",
|
|
Skip: false,
|
|
Run: func(ctx context.Context, t *testing.T, store share.Store) error {
|
|
if err := loadTestData(ctx, "testdata/delete_attributes.json", store); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
origin := app.ID("app1.edge.app")
|
|
resourceID := share.ResourceID("res-1")
|
|
|
|
// It should delete specified attributes
|
|
if err := store.DeleteAttributes(ctx, origin, resourceID, "my_text", "my_bool"); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
resource, err := store.GetResource(ctx, origin, resourceID)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if e, g := 1, len(resource.Attributes()); e != g {
|
|
return errors.Errorf("len(resource.Attributes()): expected '%v', got '%v'", e, g)
|
|
}
|
|
|
|
attr := share.GetAttribute(resource, "my_number", share.TypeNumber)
|
|
if attr == nil {
|
|
return errors.New("attr shoudl not be nil")
|
|
}
|
|
|
|
return nil
|
|
},
|
|
},
|
|
}
|
|
|
|
func runRepositoryTests(t *testing.T, newRepo NewTestStoreFunc) {
|
|
for _, tc := range repositoryTestCases {
|
|
func(tc repositoryTestCase) {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if tc.Skip {
|
|
t.SkipNow()
|
|
|
|
return
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
repo, err := newRepo(tc.Name)
|
|
if err != nil {
|
|
t.Fatalf("%+v", errors.WithStack(err))
|
|
}
|
|
|
|
if err := tc.Run(ctx, t, repo); err != nil {
|
|
t.Errorf("%+v", errors.WithStack(err))
|
|
}
|
|
})
|
|
}(tc)
|
|
}
|
|
}
|
|
|
|
type jsonResource struct {
|
|
ID string `json:"id"`
|
|
Origin string `json:"origin"`
|
|
Attributes []jsonAttribute `json:"attributes"`
|
|
}
|
|
|
|
type jsonAttribute struct {
|
|
Name string `json:"name"`
|
|
Type share.ValueType `json:"type"`
|
|
Value any `json:"value"`
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
UpdatedAt time.Time `json:"updatedAt"`
|
|
}
|
|
|
|
func loadTestData(ctx context.Context, jsonFile string, store share.Store) error {
|
|
data, err := testData.ReadFile(jsonFile)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
var resources []jsonResource
|
|
|
|
if err := json.Unmarshal(data, &resources); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
for _, res := range resources {
|
|
attributes := make([]share.Attribute, len(res.Attributes))
|
|
|
|
for idx, attr := range res.Attributes {
|
|
attributes[idx] = share.NewBaseAttribute(
|
|
attr.Name,
|
|
attr.Type,
|
|
attr.Value,
|
|
)
|
|
}
|
|
|
|
_, err := store.UpdateAttributes(ctx, app.ID(res.Origin), share.ResourceID(res.ID), attributes...)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|