package testsuite import ( "context" "reflect" "sync" "testing" "forge.cadoles.com/cadoles/bouncer/internal/store" "github.com/pkg/errors" ) type layerRepositoryTestCase struct { Name string Do func(repo store.LayerRepository) error } const layerType store.LayerType = "test" var layerRepositoryTestCases = []layerRepositoryTestCase{ { Name: "Create layer", Do: func(repo store.LayerRepository) error { ctx := context.Background() options := map[string]any{} layer, err := repo.CreateLayer(ctx, "create_layer_proxy", "create_layer", layerType, options) if err != nil { return errors.WithStack(err) } if layer == nil { return errors.Errorf("layer should not be nil") } if layer.Name == "" { return errors.Errorf("layer.Name should not be empty") } if layer.Proxy == "" { return errors.Errorf("layer.Proxy should not be empty") } if layer.CreatedAt.IsZero() { return errors.Errorf("layer.CreatedAt should not be zero value") } if layer.UpdatedAt.IsZero() { return errors.Errorf("layer.UpdatedAt should not be zero value") } if layer.Revision != 0 { return errors.Errorf("layer.Revision should be zero") } return nil }, }, { Name: "Create then get layer", Do: func(repo store.LayerRepository) error { ctx := context.Background() var proxyName store.ProxyName = "create_then_get_layer_proxy" var layerName store.LayerName = "create_then_get_layer" var layerType store.LayerType = "dummy" var layerOptions store.LayerOptions = store.LayerOptions{ "foo": "bar", "test": struct { Items []int `json:"items"` }{ Items: []int{1, 2, 3}, }, } createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions) if err != nil { return errors.WithStack(err) } foundLayer, err := repo.GetLayer(ctx, proxyName, layerName) if err != nil { return errors.WithStack(err) } if e, g := createdLayer.Name, foundLayer.Name; e != g { return errors.Errorf("foundLayer.Name: expected '%v', got '%v'", createdLayer.Name, foundLayer.Name) } if e, g := createdLayer.CreatedAt, foundLayer.CreatedAt; !reflect.DeepEqual(e, g) { return errors.Errorf("foundLayer.CreatedAt: expected '%v', got '%v'", createdLayer.CreatedAt, foundLayer.CreatedAt) } if e, g := createdLayer.UpdatedAt, foundLayer.UpdatedAt; !reflect.DeepEqual(e, g) { return errors.Errorf("foundLayer.UpdatedAt: expected '%v', got '%v'", createdLayer.UpdatedAt, foundLayer.UpdatedAt) } if e, g := createdLayer.Enabled, foundLayer.Enabled; !reflect.DeepEqual(e, g) { return errors.Errorf("foundLayer.Enabled: expected '%v', got '%v'", createdLayer.Enabled, foundLayer.Enabled) } if e, g := createdLayer.Weight, foundLayer.Weight; !reflect.DeepEqual(e, g) { return errors.Errorf("foundLayer.Weight: expected '%v', got '%v'", createdLayer.Weight, foundLayer.Weight) } if e, g := createdLayer.Proxy, foundLayer.Proxy; !reflect.DeepEqual(e, g) { return errors.Errorf("foundLayer.Proxy: expected '%v', got '%v'", createdLayer.Proxy, foundLayer.Proxy) } if e, g := createdLayer.Options, foundLayer.Options; !reflect.DeepEqual(e, g) { return errors.Errorf("foundLayer.Options: expected '%v', got '%v'", createdLayer.Options, foundLayer.Options) } return nil }, }, { Name: "Create then delete layer", Do: func(repo store.LayerRepository) error { ctx := context.Background() var layerName store.LayerName = "create_then_delete_layer" var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy") var layerType store.LayerType = "dummy" var layerOptions store.LayerOptions = store.LayerOptions{ "foo": "bar", "test": struct { Items []int `json:"items"` }{ Items: []int{1, 2, 3}, }, } createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions) if err != nil { return errors.WithStack(err) } if err := repo.DeleteLayer(ctx, createdLayer.Proxy, createdLayer.Name); err != nil { return errors.WithStack(err) } foundLayer, err := repo.GetLayer(ctx, createdLayer.Proxy, createdLayer.Name) if err == nil { return errors.New("err should not be nil") } if !errors.Is(err, store.ErrNotFound) { return errors.Errorf("err should be store.ErrNotFound, got '%+v'", err) } if foundLayer != nil { return errors.Errorf("foundLayer should be nil, got '%v'", foundLayer) } return nil }, }, { Name: "Create already existing layer", Do: func(repo store.LayerRepository) error { ctx := context.Background() var layerName store.LayerName = "create_already_existing_layer" var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy") var layerType store.LayerType = "dummy" var layerOptions store.LayerOptions = store.LayerOptions{ "foo": "bar", "test": struct { Items []int `json:"items"` }{ Items: []int{1, 2, 3}, }, } _, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions) if err != nil { return errors.WithStack(err) } _, err = repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions) if err == nil { return errors.New("err should not be nil") } if !errors.Is(err, store.ErrAlreadyExist) { return errors.Errorf("err: expected store.ErrAlreadyExists, got '%+v'", err) } return nil }, }, { Name: "Create then query layer", Do: func(repo store.LayerRepository) error { ctx := context.Background() var layerName store.LayerName = "create_then_query_layer" var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy") var layerType store.LayerType = "dummy" var layerOptions store.LayerOptions = store.LayerOptions{ "foo": "bar", "test": struct { Items []int `json:"items"` }{ Items: []int{1, 2, 3}, }, } createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions) if err != nil { return errors.WithStack(err) } headers, err := repo.QueryLayers(ctx, createdLayer.Proxy) if err != nil { return errors.WithStack(err) } if len(headers) < 1 { return errors.Errorf("len(headers): expected value > 1, got '%v'", len(headers)) } found := false for _, h := range headers { if h.Name == createdLayer.Name { found = true break } } if !found { return errors.New("could not find created layer in query results") } return nil }, }, { Name: "Create then update layer", Do: func(repo store.LayerRepository) error { ctx := context.Background() var layerName store.LayerName = "create_then_update_layer" var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy") var layerType store.LayerType = "dummy" var layerOptions store.LayerOptions = store.LayerOptions{} createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions) if err != nil { return errors.WithStack(err) } if e, g := 0, createdLayer.Revision; e != g { return errors.Errorf("createdLayer.Revision: expected '%v', got '%v'", e, g) } updatedLayer, err := repo.UpdateLayer(ctx, proxyName, layerName) if err != nil { return errors.Wrap(err, "err should be nil") } if e, g := 1, updatedLayer.Revision; e != g { return errors.Errorf("updatedLayer.Revision: expected '%v', got '%v'", e, g) } return nil }, }, { Name: "Update layer concurrently", Do: func(repo store.LayerRepository) error { ctx := context.Background() var layerName store.LayerName = "update_layer_concurrently" var proxyName store.ProxyName = store.ProxyName(string(layerName) + "_proxy") var layerType store.LayerType = "dummy" var layerOptions store.LayerOptions = store.LayerOptions{} createdLayer, err := repo.CreateLayer(ctx, proxyName, layerName, layerType, layerOptions) if err != nil { return errors.WithStack(err) } if createdLayer.Revision != 0 { return errors.Errorf("createdLayer.Revision should be zero") } var wg sync.WaitGroup total := 100 wg.Add(total) for i := 0; i < total; i++ { go func(i int) { defer wg.Done() if _, err := repo.UpdateLayer(ctx, createdLayer.Proxy, createdLayer.Name); err != nil { panic(errors.Wrap(err, "err should be nil")) } }(i) } wg.Wait() layer, err := repo.GetLayer(ctx, createdLayer.Proxy, createdLayer.Name) if err != nil { return errors.Wrap(err, "err should be nil") } if e, g := total, layer.Revision; e != g { return errors.Errorf("layer.Revision: expected '%v', got '%v'", e, g) } return nil }, }, } func TestLayerRepository(t *testing.T, repo store.LayerRepository) { for _, tc := range layerRepositoryTestCases { func(tc layerRepositoryTestCase) { t.Run(tc.Name, func(t *testing.T) { if err := tc.Do(repo); err != nil { t.Fatalf("%+v", errors.WithStack(err)) } }) }(tc) } }