From 6484e6b1587d4af028c04807afef24f1fa836b0f Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 21:39:18 +0200 Subject: [PATCH 1/9] chore(validator): fixed issue with wrong argument type for duration parsing and validating --- internal/core/manifests/kinds/tests/base.go | 6 +++--- internal/validate/validator.go | 4 +++- internal/validate/validators_funcs.go | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/core/manifests/kinds/tests/base.go b/internal/core/manifests/kinds/tests/base.go index c74e4fd..6fe55dc 100644 --- a/internal/core/manifests/kinds/tests/base.go +++ b/internal/core/manifests/kinds/tests/base.go @@ -21,10 +21,10 @@ type HttpCase struct { type Assert struct { Target string `yaml:"target,omitempty" json:"target,omitempty" validate:"required,oneof=status body headers"` - Equals any `yaml:"equals,omitempty" json:"equals,omitempty" validate:"omitempty,min=1,max=100"` - Contains string `yaml:"contains,omitempty" json:"contains,omitempty" validate:"omitempty,min=1,max=100"` + Equals any `yaml:"equals,omitempty" json:"equals,omitempty" validate:"omitempty"` + Contains string `yaml:"contains,omitempty" json:"contains,omitempty" validate:"omitempty,min=1"` Exists bool `yaml:"exists,omitempty" json:"exists,omitempty" validate:"omitempty,boolean"` - Template string `yaml:"template,omitempty" json:"template,omitempty" validate:"omitempty,min=1,max=100,contains_template"` + Template string `yaml:"template,omitempty" json:"template,omitempty" validate:"omitempty,min=1,contains_template"` } type Save struct { diff --git a/internal/validate/validator.go b/internal/validate/validator.go index f4168a5..8686960 100644 --- a/internal/validate/validator.go +++ b/internal/validate/validator.go @@ -90,7 +90,7 @@ func (e *ValidationError) Error() string { } func buildErrorMessage(fieldErr validator.FieldError) string { - fieldName := fieldErr.Field() + fieldName := strings.ToLower(fieldErr.Field()) switch fieldErr.Tag() { case "required": @@ -107,6 +107,8 @@ func buildErrorMessage(fieldErr validator.FieldError) string { return fmt.Sprintf("field '%s' must be exclusive between: %s and %s", fieldName, fieldName, strings.Join(strings.Split(strings.TrimSpace(fieldErr.Param()), " "), " | ")) case "eq": return fmt.Sprintf("field '%s' must be equal to %s", fieldName, fieldErr.Param()) + case "duration": + return fmt.Sprintf("field '%s' has wrong format '%s' must be duration", fieldName, fieldErr.Value()) default: return fmt.Sprintf("field '%s' failed validation '%s'", fieldName, fieldErr.Tag()) } diff --git a/internal/validate/validators_funcs.go b/internal/validate/validators_funcs.go index b44b197..a72830a 100644 --- a/internal/validate/validators_funcs.go +++ b/internal/validate/validators_funcs.go @@ -1,6 +1,7 @@ package validate import ( + "fmt" "strings" "time" @@ -11,7 +12,7 @@ import ( var ( validationFuncs = map[string]func(fl validator.FieldLevel) bool{ "duration": func(fl validator.FieldLevel) bool { - _, err := time.ParseDuration(fl.Field().String()) + _, err := time.ParseDuration(fmt.Sprint(fl.Field())) return err == nil }, "contains_template": func(fl validator.FieldLevel) bool { From e92a1cee03d5aebb128e9248590a408541d5582a Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 21:40:07 +0200 Subject: [PATCH 2/9] chore(cmd): apply cmd adjusted --- cmd/cli/apply/apply.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/cli/apply/apply.go b/cmd/cli/apply/apply.go index 2eb508b..e924120 100644 --- a/cmd/cli/apply/apply.go +++ b/cmd/cli/apply/apply.go @@ -39,6 +39,8 @@ var Cmd = &cobra.Command{ return } + printManifestsLoadResult(loadedMans, cachedMans) + cli.Info("Validating manifests...") validator := validate.NewManifestValidator(validate.NewValidator(), cli.Instance()) @@ -46,12 +48,10 @@ var Cmd = &cobra.Command{ validMans := validator.Valid() if len(validMans) == 0 { - cli.Warning("No valid manifests to apply") + cli.Info("No new valid manifests to apply") return } - printManifestsLoadResult(validMans, cachedMans) - cli.Infof("Saving %d manifests to storage...", len(validMans)) if err := store.Save(validMans...); err != nil { cli.Errorf("Storage error: -\n%s", err.Error()) From 4d6425213ba878dacd3a9a52a4d1ec298602d131 Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 21:41:06 +0200 Subject: [PATCH 3/9] fix(manifests): fixed issues with no adding loaded saved manifests by hash as cached array --- internal/core/io/load.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/core/io/load.go b/internal/core/io/load.go index 421233c..9a12189 100644 --- a/internal/core/io/load.go +++ b/internal/core/io/load.go @@ -100,6 +100,7 @@ func processFile(filePath string, manifestsSet map[string]struct{}) (new []manif if existingManifest != nil { if _, exists := manifestsSet[existingManifest.GetID()]; !exists { manifestsSet[existingManifest.GetID()] = struct{}{} + cachedManifests = append(cachedManifests, existingManifest) } continue } From 3b9b8e8564059a944a8abb7b3c15d213c6aa27e7 Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 21:56:57 +0200 Subject: [PATCH 4/9] chore(examples): simple http test YAML example updated for better readability --- examples/simple-http-tests/http_test.yaml | 28 +++++++++-------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/examples/simple-http-tests/http_test.yaml b/examples/simple-http-tests/http_test.yaml index c06e57c..0e38b8d 100644 --- a/examples/simple-http-tests/http_test.yaml +++ b/examples/simple-http-tests/http_test.yaml @@ -10,7 +10,7 @@ kind: HttpTest metadata: # name: Unique identifier for this test suite name: simple-test-example - + # namespace: Logical grouping for organizational purposes namespace: simple-http-tests @@ -25,9 +25,7 @@ spec: - name: Get All Users Test # Descriptive test name method: GET # HTTP method (GET/POST/PUT/etc) endpoint: /users # Appended to target URL - - # assert: Validation rules - assert: + assert: # Assert: Validation rules - target: status # What to validate (status code) equals: 200 # Expected value (HTTP 200 OK) @@ -35,13 +33,9 @@ spec: - name: Create New User With Body method: POST endpoint: /users - - # headers: Request headers to include - headers: - Content-Type: application/json # Specifies JSON payload - - # body: Request payload (automatically JSON-encoded) - body: + headers: # headers: Request headers to include + Content-Type: application/json # Specifies JSON payload + body: # body: Request payload (automatically JSON-encoded) name: "{{ Fake.name }}" # Generate fake name for request email: "{{ Fake.email }}" # Generate fake email for request age: "{{ Fake.uint.10.100 }}" # Generate fake positive number between 10 and 100 including @@ -50,12 +44,12 @@ spec: number: "{{ Fake.name }}" assert: - target: status - equals: 201 # HTTP 201 Created + equals: 201 # HTTP 201 Created # Test Case 3: Getting and validating a user - name: Get User By ID Test method: GET - endpoint: /users/1 # Endpoint with user ID + endpoint: /users/1 # Endpoint with user ID assert: - target: status equals: 200 @@ -63,13 +57,13 @@ spec: # Test Case 4: Absolute URL test - name: Always Fail Endpoint Test method: GET - url: http://127.0.0.1:8081/fail # Overrides spec.target + url: http://127.0.0.1:8081/fail # Overrides spec.target assert: - target: status - equals: 500 # Expecting server error + equals: 500 # Expecting server error # Test Case 5: Performance testing - name: Slow Endpoint Response Test method: GET - endpoint: /slow?delay=2s # Test endpoint with artificial delay - timeout: 3s # Fail if response > 3 seconds \ No newline at end of file + endpoint: /slow?delay=2s # Test endpoint with artificial delay + timeout: 3s # Fail if response > 3 seconds \ No newline at end of file From 0fc78e0d097151098f3826580e77a71cee33414a Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 21:57:39 +0200 Subject: [PATCH 5/9] fix(runner): fixed issues with tests response status assertion (missed type) --- cmd/cli/run/run.go | 15 +++++++++++++++ internal/core/runner/assert/runner.go | 24 ++++++++++++------------ 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/cmd/cli/run/run.go b/cmd/cli/run/run.go index 5a2f532..1c769c9 100644 --- a/cmd/cli/run/run.go +++ b/cmd/cli/run/run.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/apiqube/cli/internal/validate" + "github.com/apiqube/cli/internal/core/io" "github.com/apiqube/cli/internal/core/manifests" "github.com/apiqube/cli/internal/core/runner/context" @@ -35,6 +37,19 @@ var Cmd = &cobra.Command{ } cli.Infof("Loaded %d manifests", len(loadedManifests)) + + cli.Info("Validating manifests...") + validator := validate.NewManifestValidator(validate.NewValidator(), cli.Instance()) + + validator.Validate(loadedManifests...) + + validMans := validator.Valid() + if len(validMans) == 0 { + cli.Warning("No valid manifests to run tests") + return + } + + cli.Success("All manifests valid") cli.Info("Generating plan...") manager := runner.NewPlanManagerBuilder(). diff --git a/internal/core/runner/assert/runner.go b/internal/core/runner/assert/runner.go index f7bc2c4..7fdefa8 100644 --- a/internal/core/runner/assert/runner.go +++ b/internal/core/runner/assert/runner.go @@ -54,19 +54,19 @@ func (r *Runner) Assert(ctx interfaces.ExecutionContext, asserts []*tests.Assert func (r *Runner) assertStatus(_ interfaces.ExecutionContext, assert *tests.Assert, resp *http.Response) error { if assert.Equals != nil { - expectedCode, ok := assert.Equals.(int) + expectedCode, ok := assert.Equals.(float64) if !ok { - return fmt.Errorf("assertion failed, expected status correct value got %v", assert.Equals) + return fmt.Errorf("expected status correct value got %v", assert.Equals) } - if resp.StatusCode != expectedCode { - return fmt.Errorf("assertion failed, expected status code %v, got %v", expectedCode, resp.StatusCode) + if resp.StatusCode != int(expectedCode) { + return fmt.Errorf("expected status code %v, got %v", expectedCode, resp.StatusCode) } } if assert.Contains != "" { if !strings.Contains(resp.Status, assert.Contains) { - return fmt.Errorf("assertion failed, expected %v to contain %q", resp.Status, assert.Contains) + return fmt.Errorf("expected %v to contain %q", resp.Status, assert.Contains) } } @@ -76,7 +76,7 @@ func (r *Runner) assertStatus(_ interfaces.ExecutionContext, assert *tests.Asser func (r *Runner) assertBody(_ interfaces.ExecutionContext, assert *tests.Assert, _ *http.Response, body []byte) error { if assert.Exists { if len(body) == 0 { - return fmt.Errorf("assertion failed, expected not null body") + return fmt.Errorf("expected not null body") } return nil @@ -107,14 +107,14 @@ func (r *Runner) assertBody(_ interfaces.ExecutionContext, assert *tests.Assert, } if !reflect.DeepEqual(body, expected) { - return fmt.Errorf("assert failed, expected body %v to equal %v", string(body), expected) + return fmt.Errorf("expected body %v to equal %v", string(body), expected) } return nil } if assert.Contains != "" { if !bytes.Contains(body, []byte(assert.Contains)) { - return fmt.Errorf("assertion failed, expected %v in body", assert.Contains) + return fmt.Errorf("expected %v in body", assert.Contains) } return nil @@ -126,12 +126,12 @@ func (r *Runner) assertBody(_ interfaces.ExecutionContext, assert *tests.Assert, func (r *Runner) assertHeaders(_ interfaces.ExecutionContext, assert *tests.Assert, resp *http.Response) error { if assert.Equals != nil { if equals, ok := assert.Equals.(map[string]any); ok { - return fmt.Errorf("assertion failed, expected map assertion got %v", assert.Equals) + return fmt.Errorf("expected map assertion got %v", assert.Equals) } else { for key, expectedVal := range equals { actualVal := resp.Header.Get(key) if fmt.Sprintf("%v", expectedVal) != actualVal { - return fmt.Errorf("assertion failed, expected header value %v, got %v", expectedVal, actualVal) + return fmt.Errorf("expected header value %v, got %v", expectedVal, actualVal) } } } @@ -152,13 +152,13 @@ func (r *Runner) assertHeaders(_ interfaces.ExecutionContext, assert *tests.Asse } if !found { - return fmt.Errorf("assertion failed, expected header %v but not found", assert.Contains) + return fmt.Errorf("expected header %v but not found", assert.Contains) } } if assert.Exists { if len(resp.Header) == 0 { - return fmt.Errorf("assertion failed, expected some headers in response") + return fmt.Errorf("expected some headers in response") } } From 9dd39fb3626d2f50a5baf8c3c32d4ac20fb84209 Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 22:06:13 +0200 Subject: [PATCH 6/9] chore(runner): removed redundant output logf call on plan execution finishing --- examples/simple-http-tests/http_test.yaml | 12 ++++++------ internal/core/runner/executor/plan.go | 2 -- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/simple-http-tests/http_test.yaml b/examples/simple-http-tests/http_test.yaml index 0e38b8d..3217550 100644 --- a/examples/simple-http-tests/http_test.yaml +++ b/examples/simple-http-tests/http_test.yaml @@ -22,12 +22,12 @@ spec: # cases: List of test scenarios to execute cases: # Test Case 1: Basic GET request validation - - name: Get All Users Test # Descriptive test name - method: GET # HTTP method (GET/POST/PUT/etc) - endpoint: /users # Appended to target URL - assert: # Assert: Validation rules - - target: status # What to validate (status code) - equals: 200 # Expected value (HTTP 200 OK) + - name: Get All Users Test # Descriptive test name + method: GET # HTTP method (GET/POST/PUT/etc) + endpoint: /users # Appended to target URL + assert: # Assert: Validation rules + - target: status # What to validate (status code) + equals: 200 # Expected value (HTTP 200 OK) # Test Case 2: POST request with payload - name: Create New User With Body diff --git a/internal/core/runner/executor/plan.go b/internal/core/runner/executor/plan.go index 958c524..37b105c 100644 --- a/internal/core/runner/executor/plan.go +++ b/internal/core/runner/executor/plan.go @@ -131,8 +131,6 @@ func (r *DefaultPlanRunner) RunPlan(ctx interfaces.ExecutionContext, manifest ma } } - output.Logf(interfaces.InfoLevel, "%s plan finished", planID) - return nil } From b7e7535d678b8e8f9ccc33a5fd33d2519064222d Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 23:02:50 +0200 Subject: [PATCH 7/9] chore(manifests): adjusted manifests constant kinds, a cleaning, tiding, linting --- .golanci.yml | 3 +- cmd/cli/apply/apply.go | 51 ++++++++++--------- cmd/cli/check/check.go | 2 +- go.mod | 3 ++ internal/core/manifests/interface.go | 26 +++++----- internal/core/manifests/kinds/plan/plan.go | 12 ++--- .../core/manifests/kinds/servers/server.go | 2 +- .../core/runner/executor/base_registry.go | 6 +-- .../core/runner/executor/executors/http.go | 2 +- .../core/runner/executor/executors/server.go | 2 +- .../core/runner/executor/executors/values.go | 2 +- internal/core/runner/plan/manager.go | 2 +- internal/operations/parse.go | 12 ++--- internal/validate/cli.go | 6 +-- internal/validate/validators_funcs.go | 2 +- 15 files changed, 71 insertions(+), 62 deletions(-) diff --git a/.golanci.yml b/.golanci.yml index 3c54082..4c6a178 100644 --- a/.golanci.yml +++ b/.golanci.yml @@ -26,4 +26,5 @@ run: - "gen" - "tests" skip-files: - - ".*\\.pb\\.go \ No newline at end of file + - ".*\\.pb\\.go + - ".*\\_test.go" \ No newline at end of file diff --git a/cmd/cli/apply/apply.go b/cmd/cli/apply/apply.go index e924120..2034c9b 100644 --- a/cmd/cli/apply/apply.go +++ b/cmd/cli/apply/apply.go @@ -32,35 +32,40 @@ var Cmd = &cobra.Command{ return } - cli.Infof("Loading manifests from: %s", file) - loadedMans, cachedMans, err := io.LoadManifests(file) - if err != nil { - cli.Errorf("Critical load error:\n%s", formatLoadError(err, file)) - return - } + _ = applyManifests(file) + }, +} - printManifestsLoadResult(loadedMans, cachedMans) +func applyManifests(filePath string) error { + cli.Infof("Loading manifests from: %s", filePath) + loadedMans, cachedMans, err := io.LoadManifests(filePath) + if err != nil { + cli.Errorf("Critical load error:\n%s", formatLoadError(err, filePath)) + return err + } - cli.Info("Validating manifests...") - validator := validate.NewManifestValidator(validate.NewValidator(), cli.Instance()) + printManifestsLoadResult(loadedMans, cachedMans) - validator.Validate(loadedMans...) + cli.Info("Validating manifests...") + validator := validate.NewManifestValidator(validate.NewValidator(), cli.Instance()) - validMans := validator.Valid() - if len(validMans) == 0 { - cli.Info("No new valid manifests to apply") - return - } + validator.Validate(loadedMans...) - cli.Infof("Saving %d manifests to storage...", len(validMans)) - if err := store.Save(validMans...); err != nil { - cli.Errorf("Storage error: -\n%s", err.Error()) - return - } + validMans := validator.Valid() + if len(validMans) == 0 { + cli.Info("No new valid manifests to apply") + return errors.New("no new valid manifests to apply") + } - printPostApplySummary(validMans) - cli.Successf("Successfully applied %d manifests", len(validMans)) - }, + cli.Infof("Saving %d manifests to storage...", len(validMans)) + if err = store.Save(validMans...); err != nil { + cli.Errorf("Storage error: -\n%s", err.Error()) + return err + } + + printPostApplySummary(validMans) + cli.Successf("Successfully applied %d manifests", len(validMans)) + return nil } func formatLoadError(err error, file string) string { diff --git a/cmd/cli/check/check.go b/cmd/cli/check/check.go index 2faa8dd..22f1353 100644 --- a/cmd/cli/check/check.go +++ b/cmd/cli/check/check.go @@ -139,7 +139,7 @@ func loadManifests(opts *checkPlanOptions) ([]manifests.Manifest, error) { } func extractPlanManifest(mans []manifests.Manifest) (*plan.Plan, error) { - man, err := findManifestWithKind(manifests.PlanManifestKind, mans) + man, err := findManifestWithKind(manifests.PlanKind, mans) if err != nil { return nil, err } diff --git a/go.mod b/go.mod index 780b7e5..28bf222 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/pterm/pterm v0.12.80 github.com/spf13/cobra v1.9.1 github.com/spf13/viper v1.20.1 + github.com/stretchr/testify v1.10.0 github.com/tidwall/gjson v1.18.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -47,6 +48,7 @@ require ( github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/containerd/console v1.0.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect @@ -71,6 +73,7 @@ require ( github.com/mschoch/smat v0.2.0 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect diff --git a/internal/core/manifests/interface.go b/internal/core/manifests/interface.go index 28bfa7b..ee9b8d7 100644 --- a/internal/core/manifests/interface.go +++ b/internal/core/manifests/interface.go @@ -9,22 +9,22 @@ const ( ) const ( - V1Version = "v1" + V1 = "v1" ) const ( - PlanManifestKind = "Plan" - ValuesManifestKind = "Values" - ServerManifestKind = "Server" - ServiceManifestKind = "Service" - HttpTestManifestKind = "HttpTest" - HttpLoadTestManifestKind = "HttpLoadTest" - GRPCTestManifestKind = "GRPCTest" - GRPCLoadTestManifestKind = "GRPCLoadTest" - WSTestManifestKind = "WSTest" - WSLoadTestManifestKind = "WSLoadTest" - GRAPHQLTestManifestKind = "GraphQLTest" - GRAPHQLLoadTestManifestKind = "GraphQLLoadTest" + PlanKind = "Plan" + ValuesKind = "Values" + ServerKind = "Server" + ServiceKind = "Service" + HttpTestKind = "HttpTest" + HttpLoadTestKind = "HttpLoadTest" + GRPCTestKind = "GRPCTest" + GRPCLoadTestKind = "GRPCLoadTest" + WSTestKind = "WSTest" + WSLoadTestKind = "WSLoadTest" + GRAPHQLTestKind = "GraphQLTest" + GRAPHQLLoadTestKind = "GraphQLLoadTest" ) type Manifest interface { diff --git a/internal/core/manifests/kinds/plan/plan.go b/internal/core/manifests/kinds/plan/plan.go index 883f0d8..5bf8935 100644 --- a/internal/core/manifests/kinds/plan/plan.go +++ b/internal/core/manifests/kinds/plan/plan.go @@ -24,7 +24,7 @@ type Plan struct { Spec struct { Stages []Stage `yaml:"stages" json:"stages" validate:"required,min=1,dive"` - Hooks *Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty" validate:"omitempty,dive"` + Hooks *Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty" validate:"omitempty"` } `yaml:"spec" json:"spec" validate:"required"` Meta *kinds.Meta `yaml:"-" json:"meta"` @@ -37,7 +37,7 @@ type Stage struct { Parallel bool `yaml:"parallel,omitempty" json:"parallel,omitempty"` Params map[string]any `yaml:"params,omitempty" json:"params,omitempty" validate:"omitempty"` Mode string `yaml:"mode,omitempty" json:"mode,omitempty" validate:"omitempty,oneof=strict parallel"` // (strict|parallel) - Hooks *Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty" validate:"omitempty,dive"` + Hooks *Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty" validate:"omitempty"` } type Hooks struct { @@ -88,8 +88,8 @@ func (p *Plan) GetMeta() manifests.Meta { } func (p *Plan) Default() { - if p.Version != manifests.V1Version { - p.Version = manifests.V1Version + if p.Version != manifests.V1 { + p.Version = manifests.V1 } if p.Name == "" { @@ -97,7 +97,7 @@ func (p *Plan) Default() { } if p.Kind == "" { - p.Kind = manifests.PlanManifestKind + p.Kind = manifests.PlanKind } if p.Namespace == "" { @@ -115,7 +115,7 @@ func (p *Plan) Prepare() { } if p.Kind == "" { - p.Kind = manifests.PlanManifestKind + p.Kind = manifests.PlanKind } if p.Meta == nil { diff --git a/internal/core/manifests/kinds/servers/server.go b/internal/core/manifests/kinds/servers/server.go index 969f9b8..7622cf5 100644 --- a/internal/core/manifests/kinds/servers/server.go +++ b/internal/core/manifests/kinds/servers/server.go @@ -21,7 +21,7 @@ type Server struct { Spec struct { BaseURL string `yaml:"baseUrl" json:"baseUrl" validate:"required,url"` Health string `yaml:"health" json:"health" validate:"omitempty,max=100"` - Headers map[string]string `yaml:"headers,omitempty" json:"headers"` + Headers map[string]string `yaml:"headers,omitempty" json:"headers" validate:"omitempty,max=20"` } `yaml:"spec" json:"spec" validate:"required"` Meta *kinds.Meta `yaml:"-" json:"meta"` diff --git a/internal/core/runner/executor/base_registry.go b/internal/core/runner/executor/base_registry.go index d0927bf..c84ccd2 100644 --- a/internal/core/runner/executor/base_registry.go +++ b/internal/core/runner/executor/base_registry.go @@ -10,9 +10,9 @@ import ( var DefaultRegistry = &DefaultExecutorRegistry{ executors: map[string]interfaces.Executor{ - manifests.ValuesManifestKind: executors.NewValuesExecutor(), - manifests.ServerManifestKind: executors.NewServerExecutor(), - manifests.HttpTestManifestKind: executors.NewHTTPExecutor(), + manifests.ValuesKind: executors.NewValuesExecutor(), + manifests.ServerKind: executors.NewServerExecutor(), + manifests.HttpTestKind: executors.NewHTTPExecutor(), }, } diff --git a/internal/core/runner/executor/executors/http.go b/internal/core/runner/executor/executors/http.go index 6337e9c..86d8b84 100644 --- a/internal/core/runner/executor/executors/http.go +++ b/internal/core/runner/executor/executors/http.go @@ -54,7 +54,7 @@ func (e *HTTPExecutor) Run(ctx interfaces.ExecutionContext, manifest manifests.M httpMan, ok := manifest.(*api.Http) if !ok { - return fmt.Errorf("%s manifest %s is not a %s kind", httpExecutorOutputPrefix, manifest.GetID(), manifests.HttpTestManifestKind) + return fmt.Errorf("%s manifest %s is not a %s kind", httpExecutorOutputPrefix, manifest.GetID(), manifests.HttpTestKind) } var wg sync.WaitGroup diff --git a/internal/core/runner/executor/executors/server.go b/internal/core/runner/executor/executors/server.go index a2c9da2..2fc26be 100644 --- a/internal/core/runner/executor/executors/server.go +++ b/internal/core/runner/executor/executors/server.go @@ -32,7 +32,7 @@ func (e *ServerExecutor) Run(ctx interfaces.ExecutionContext, manifest manifests serverMan, ok := manifest.(*servers.Server) if !ok { - return fmt.Errorf("%s manifest %s is not a %s kind", serverExecutorOutputPrefix, manifest.GetID(), manifests.ServerManifestKind) + return fmt.Errorf("%s manifest %s is not a %s kind", serverExecutorOutputPrefix, manifest.GetID(), manifests.ServerKind) } if serverMan.Spec.Health != "" { diff --git a/internal/core/runner/executor/executors/values.go b/internal/core/runner/executor/executors/values.go index 34763bc..1c6b9b3 100644 --- a/internal/core/runner/executor/executors/values.go +++ b/internal/core/runner/executor/executors/values.go @@ -30,7 +30,7 @@ func (e *ValuesExecutor) Run(ctx interfaces.ExecutionContext, manifest manifests valueMan, ok := manifest.(*values.Values) if !ok { - return fmt.Errorf("%s manifest %s is not a %s kind", valuesExecutorOutputPrefix, manifest.GetID(), manifests.ValuesManifestKind) + return fmt.Errorf("%s manifest %s is not a %s kind", valuesExecutorOutputPrefix, manifest.GetID(), manifests.ValuesKind) } for key, data := range valueMan.Spec.Data { diff --git a/internal/core/runner/plan/manager.go b/internal/core/runner/plan/manager.go index 97cedae..3e086aa 100644 --- a/internal/core/runner/plan/manager.go +++ b/internal/core/runner/plan/manager.go @@ -96,7 +96,7 @@ func (g *basicManager) Generate() (*plan.Plan, error) { graph := newDepGraph() for id, m := range g.manifests { - if m.GetKind() == manifests.PlanManifestKind { + if m.GetKind() == manifests.PlanKind { delete(g.manifests, id) continue } diff --git a/internal/operations/parse.go b/internal/operations/parse.go index 2ea4a03..756caa9 100644 --- a/internal/operations/parse.go +++ b/internal/operations/parse.go @@ -100,17 +100,17 @@ func Parse(format ParseFormat, data []byte) (manifests.Manifest, error) { var manifest manifests.Manifest switch raw.Kind { - case manifests.PlanManifestKind: + case manifests.PlanKind: manifest = &plan.Plan{} - case manifests.ValuesManifestKind: + case manifests.ValuesKind: manifest = &values.Values{} - case manifests.ServerManifestKind: + case manifests.ServerKind: manifest = &servers.Server{} - case manifests.ServiceManifestKind: + case manifests.ServiceKind: manifest = &services.Service{} - case manifests.HttpTestManifestKind: + case manifests.HttpTestKind: manifest = &api.Http{} - case manifests.HttpLoadTestManifestKind: + case manifests.HttpLoadTestKind: manifest = &load.Http{} default: return nil, fmt.Errorf("unknown manifest kind: %s", raw.Kind) diff --git a/internal/validate/cli.go b/internal/validate/cli.go index dc31f61..57b25ac 100644 --- a/internal/validate/cli.go +++ b/internal/validate/cli.go @@ -14,7 +14,7 @@ var _ ManifestsValidator = (*ManifestValidator)(nil) type ManifestValidator struct { validator Validator ui ui.UI - valid, inValid []manifests.Manifest + valid, invalid []manifests.Manifest } func NewManifestValidator(validator Validator, ui ui.UI) *ManifestValidator { @@ -40,7 +40,7 @@ func (v *ManifestValidator) Validate(manifests ...manifests.Manifest) bool { errorBuilder.AddGenericError(err) } - v.inValid = append(v.inValid, man) + v.invalid = append(v.invalid, man) } else { v.valid = append(v.valid, man) } @@ -56,7 +56,7 @@ func (v *ManifestValidator) Valid() []manifests.Manifest { } func (v *ManifestValidator) Invalid() []manifests.Manifest { - return v.inValid + return v.invalid } type ManifestErrorBuilder struct { diff --git a/internal/validate/validators_funcs.go b/internal/validate/validators_funcs.go index a72830a..ab29a62 100644 --- a/internal/validate/validators_funcs.go +++ b/internal/validate/validators_funcs.go @@ -22,7 +22,7 @@ var ( } manifestKinsValidationFuncs = map[string]func(fl validator.FieldLevel) bool{ - manifests.PlanManifestKind: planValidationFunc, + manifests.PlanKind: planValidationFunc, } ) From 9c7ee4289ef5ab876f3b170daa735e41319eb781 Mon Sep 17 00:00:00 2001 From: Nofre Date: Wed, 28 May 2025 23:03:30 +0200 Subject: [PATCH 8/9] test(manifests): added tests on manifest validator --- internal/validate/validator_test.go | 145 ++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 internal/validate/validator_test.go diff --git a/internal/validate/validator_test.go b/internal/validate/validator_test.go new file mode 100644 index 0000000..7d44fe3 --- /dev/null +++ b/internal/validate/validator_test.go @@ -0,0 +1,145 @@ +package validate + +import ( + "testing" + + "github.com/apiqube/cli/internal/core/manifests" + "github.com/apiqube/cli/internal/core/manifests/kinds" + "github.com/apiqube/cli/internal/core/manifests/kinds/plan" + "github.com/apiqube/cli/internal/core/manifests/kinds/servers" + "github.com/apiqube/cli/internal/core/manifests/kinds/values" + "github.com/stretchr/testify/require" +) + +var ( + invalidVersionManifest = &values.Values{ + BaseManifest: kinds.BaseManifest{ + Version: "notV1", + }, + Spec: struct { + Data map[string]any `yaml:",inline" json:",inline" validate:"required,min=1,dive"` + }{Data: map[string]any{}}, + } + + invalidKindManifest = &values.Values{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: "NotValues", + }, + Spec: struct { + Data map[string]any `yaml:",inline" json:",inline" validate:"required,min=1,dive"` + }{Data: map[string]any{}}, + } + + invalidNameManifest = &values.Values{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.ValuesKind, + }, + Spec: struct { + Data map[string]any `yaml:",inline" json:",inline" validate:"required,min=1,dive"` + }{Data: map[string]any{}}, + } + + invalidPlanManifest = &plan.Plan{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.PlanKind, + Metadata: kinds.Metadata{ + Name: "invalid_plan", + }, + }, + Spec: struct { + Stages []plan.Stage `yaml:"stages" json:"stages" validate:"required,min=1,dive"` + Hooks *plan.Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty" validate:"omitempty"` + }{ + Stages: []plan.Stage{}, + Hooks: &plan.Hooks{}, + }, + } + + invalidServerManifest = &servers.Server{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.ServerKind, + Metadata: kinds.Metadata{ + Name: "invalid_server", + }, + }, + Spec: struct { + BaseURL string `yaml:"baseUrl" json:"baseUrl" validate:"required,url"` + Health string `yaml:"health" json:"health" validate:"omitempty,max=100"` + Headers map[string]string `yaml:"headers,omitempty" json:"headers" validate:"omitempty,max=20"` + }{ + BaseURL: "", + Health: "", + Headers: map[string]string{}, + }, + } + + validServerManifest = &servers.Server{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.ServerKind, + Metadata: kinds.Metadata{ + Name: "valid_server", + }, + }, + Spec: struct { + BaseURL string `yaml:"baseUrl" json:"baseUrl" validate:"required,url"` + Health string `yaml:"health" json:"health" validate:"omitempty,max=100"` + Headers map[string]string `yaml:"headers,omitempty" json:"headers" validate:"omitempty,max=20"` + }{ + BaseURL: "http://127.0.0.1:8080", + Health: "", + Headers: map[string]string{}, + }, + } +) + +func TestValidator_Validate(t *testing.T) { + v := NewValidator() + + var err error + + t.Run("InvalidVersionManifest: incorrect version", func(t *testing.T) { + err = v.Validate(invalidVersionManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "version") + }) + + t.Run("InvalidKindManifest: incorrect kind", func(t *testing.T) { + err = v.Validate(invalidKindManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "one of") + }) + + t.Run("InvalidNameManifest: empty name", func(t *testing.T) { + err = v.Validate(invalidNameManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "name") + }) + + t.Run("InvalidSpecDataManifest: spec data less then min", func(t *testing.T) { + err = v.Validate(invalidNameManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "data") + }) + + t.Run("InvalidPlanManifest: empty stages", func(t *testing.T) { + err = v.Validate(invalidPlanManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "stages") + }) + + t.Run("InvalidServerManifest: empty base url", func(t *testing.T) { + err = v.Validate(invalidServerManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "baseurl") + }) + + t.Run("ValidServerManifest", func(t *testing.T) { + err = v.Validate(validServerManifest) + require.NoError(t, err) + }) +} From cefe6e853b6b76f1807a855bcd314ea694dc04db Mon Sep 17 00:00:00 2001 From: Nofre Date: Thu, 29 May 2025 06:59:03 +0200 Subject: [PATCH 9/9] chore(ci): ci updated added case for tests running, to validator tests added new test cases, small adjustments in other places --- .github/workflows/ci.yml | 6 +- Taskfile.yml | 5 + internal/core/manifests/kinds/tests/base.go | 4 +- .../core/manifests/kinds/tests/load/http.go | 2 +- internal/validate/validator_test.go | 321 +++++++++++++++++- 5 files changed, 333 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13c229b..4249790 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,13 +31,17 @@ jobs: gofumpt -l -w . git diff --exit-code - - name: golangci-lint + - name: Run golangci-lint uses: golangci/golangci-lint-action@v3 with: version: latest skip-pkg-cache: true args: --issues-exit-code=0 + - name: Run Units Tests + run: | + go test -v -coverpkg=./... ./... + release: needs: build-and-test runs-on: ubuntu-latest diff --git a/Taskfile.yml b/Taskfile.yml index 914ce21..e0c532a 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -40,6 +40,11 @@ tasks: cmds: - reflex -r '\.go$$' -s -- sh -c "task build && task run" + test: + desc: Run all tests + cmds: + - go test -v -coverpkg=./... -coverprofile=cover.out ./... + fmt: desc: 🧹 Cleaning all go code cmds: diff --git a/internal/core/manifests/kinds/tests/base.go b/internal/core/manifests/kinds/tests/base.go index 6fe55dc..152defa 100644 --- a/internal/core/manifests/kinds/tests/base.go +++ b/internal/core/manifests/kinds/tests/base.go @@ -28,7 +28,7 @@ type Assert struct { } type Save struct { - Json map[string]string `yaml:"json,omitempty" json:"json,omitempty" validate:"omitempty,dive,keys,endkeys,json"` + Json map[string]string `yaml:"json,omitempty" json:"json,omitempty" validate:"omitempty,dive,keys,endkeys"` Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty" validate:"omitempty,dive,keys,endkeys"` Status bool `yaml:"status,omitempty" json:"status,omitempty" validate:"omitempty,boolean"` Body bool `yaml:"body,omitempty" json:"body,omitempty" validate:"omitempty,boolean"` @@ -38,5 +38,5 @@ type Save struct { type Pass struct { From string `yaml:"from" json:"from" validate:"required,min=1,max=100"` - Map map[string]string `yaml:"map,omitempty" json:"map,omitempty" validate:"omitempty,min=1,max=100,keys,endkeys"` + Map map[string]string `yaml:"map,omitempty" json:"map,omitempty" validate:"omitempty,min=1,max=100,dive,keys,endkeys"` } diff --git a/internal/core/manifests/kinds/tests/load/http.go b/internal/core/manifests/kinds/tests/load/http.go index 0231183..fa6a4f9 100644 --- a/internal/core/manifests/kinds/tests/load/http.go +++ b/internal/core/manifests/kinds/tests/load/http.go @@ -40,7 +40,7 @@ type HttpCase struct { Wave *WaveConfig `yaml:"wave,omitempty" json:"wave,omitempty" validate:"omitempty"` Step *StepConfig `yaml:"step,omitempty" json:"step,omitempty" validate:"omitempty"` Duration time.Duration `yaml:"duration,omitempty" json:"duration,omitempty" validate:"omitempty,duration"` - SaveEvery int `yaml:"saveEvery,omitempty" json:"saveEvery,omitempty" validate:"omitempty,min=1,max=1000"` + SaveEntry int `yaml:"saveEntry,omitempty" json:"saveEntry,omitempty" validate:"omitempty,min=1,max=1000"` // Int value or precent of saving } type WaveConfig struct { diff --git a/internal/validate/validator_test.go b/internal/validate/validator_test.go index 7d44fe3..5eec015 100644 --- a/internal/validate/validator_test.go +++ b/internal/validate/validator_test.go @@ -2,6 +2,12 @@ package validate import ( "testing" + "time" + + "github.com/apiqube/cli/internal/core/manifests/kinds/services" + "github.com/apiqube/cli/internal/core/manifests/kinds/tests" + "github.com/apiqube/cli/internal/core/manifests/kinds/tests/api" + "github.com/apiqube/cli/internal/core/manifests/kinds/tests/load" "github.com/apiqube/cli/internal/core/manifests" "github.com/apiqube/cli/internal/core/manifests/kinds" @@ -58,6 +64,28 @@ var ( }, } + invalidServiceManifest = &services.Service{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.ServiceKind, + Metadata: kinds.Metadata{ + Name: "invalid_service", + }, + }, + Spec: struct { + Containers []services.Container `yaml:"containers" validate:"required,min=1,max=25,dive"` + }{ + Containers: []services.Container{ + { + Name: "", + Dockerfile: "Dockerfile", + Image: "Image", + Replicas: 100, + }, + }, + }, + } + invalidServerManifest = &servers.Server{ BaseManifest: kinds.BaseManifest{ Version: manifests.V1, @@ -77,6 +105,139 @@ var ( }, } + invalidHttpTestManifest = &api.Http{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.HttpTestKind, + Metadata: kinds.Metadata{ + Name: "invalid_http_test", + }, + }, + Spec: struct { + Target string `yaml:"target,omitempty" json:"target,omitempty" validate:"required"` + Cases []api.HttpCase `yaml:"cases" json:"cases" validate:"required,min=1,max=100,dive"` + }{ + Target: "", + Cases: []api.HttpCase{}, + }, + } + + invalidHttpTestCaseManifest = &api.Http{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.HttpTestKind, + Metadata: kinds.Metadata{ + Name: "invalid_http_test_case", + }, + }, + Spec: struct { + Target string `yaml:"target,omitempty" json:"target,omitempty" validate:"required"` + Cases []api.HttpCase `yaml:"cases" json:"cases" validate:"required,min=1,max=100,dive"` + }{ + Target: "target", + Cases: []api.HttpCase{ + { + HttpCase: tests.HttpCase{ + Name: "", + Method: "INVALID", + }, + }, + }, + }, + } + + invalidHttpLoadTestManifest = &load.Http{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.HttpLoadTestKind, + Metadata: kinds.Metadata{ + Name: "invalid_http_load_test", + }, + }, + Spec: struct { + Target string `yaml:"target,omitempty" json:"target,omitempty" validate:"required"` + Cases []load.HttpCase `yaml:"cases" json:"cases" validate:"required,min=1,max=100,dive"` + }{ + Target: "", + Cases: []load.HttpCase{}, + }, + } + + invalidHttpLoadTestCaseManifest = &load.Http{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.HttpLoadTestKind, + Metadata: kinds.Metadata{ + Name: "invalid_http_load_test_case", + }, + }, + Spec: struct { + Target string `yaml:"target,omitempty" json:"target,omitempty" validate:"required"` + Cases []load.HttpCase `yaml:"cases" json:"cases" validate:"required,min=1,max=100,dive"` + }{ + Target: "target", + Cases: []load.HttpCase{ + { + HttpCase: tests.HttpCase{ + Name: "", + Method: "INVALID", + }, + Type: "INVALID", + Repeats: -1, + Agents: -1, + RPS: -1, + SaveEntry: -1, + }, + }, + }, + } +) + +var ( + validPlanManifest = &plan.Plan{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.PlanKind, + Metadata: kinds.Metadata{ + Name: "valid_plan", + }, + }, + Spec: struct { + Stages []plan.Stage `yaml:"stages" json:"stages" validate:"required,min=1,dive"` + Hooks *plan.Hooks `yaml:"hooks,omitempty" json:"hooks,omitempty" validate:"omitempty"` + }{ + Stages: []plan.Stage{ + { + Name: "stage_1", + Manifests: []string{ + "id_0", + "id_1", + }, + }, + }, + }, + } + + validServiceManifest = &services.Service{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.ServiceKind, + Metadata: kinds.Metadata{ + Name: "valid_service", + }, + }, + Spec: struct { + Containers []services.Container `yaml:"containers" validate:"required,min=1,max=25,dive"` + }{ + Containers: []services.Container{ + { + Name: "some_name", + Dockerfile: "Dockerfile", + }, + }, + }, + } + validServerManifest = &servers.Server{ BaseManifest: kinds.BaseManifest{ Version: manifests.V1, @@ -92,7 +253,103 @@ var ( }{ BaseURL: "http://127.0.0.1:8080", Health: "", - Headers: map[string]string{}, + }, + } + + validHttpTestCaseManifest = &api.Http{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.HttpTestKind, + Metadata: kinds.Metadata{ + Name: "valid_http_test", + }, + }, + Spec: struct { + Target string `yaml:"target,omitempty" json:"target,omitempty" validate:"required"` + Cases []api.HttpCase `yaml:"cases" json:"cases" validate:"required,min=1,max=100,dive"` + }{ + Target: "target", + Cases: []api.HttpCase{ + { + HttpCase: tests.HttpCase{ + Name: "some_name_0", + Method: "GET", + Endpoint: "/users", + Url: "http://127.0.0.1:8080/users", + Headers: map[string]string{ + "Authorization": "Bearer token", + }, + Body: map[string]any{ + "foo": "bar", + }, + Assert: []*tests.Assert{ + { + Target: "status", + Equals: 200, + Contains: "some_data", + Exists: true, + Template: "{{Status}} OK", + }, + }, + Save: &tests.Save{ + Json: map[string]string{"foo": "bar"}, + Headers: map[string]string{ + "Authorization": "Bearer token", + }, + Status: true, + Body: true, + All: true, + Group: "custom_save_group", + }, + Pass: []tests.Pass{ + { + From: "headers", + Map: map[string]string{"Authorization": "Bearer token"}, + }, + }, + Timeout: time.Second, + Parallel: false, + Details: []string{ + "id_0", + "id_1", + }, + }, + }, + }, + }, + } + + validHttpLoadTestCaseManifest = &load.Http{ + BaseManifest: kinds.BaseManifest{ + Version: manifests.V1, + Kind: manifests.HttpLoadTestKind, + Metadata: kinds.Metadata{ + Name: "valid_http_load_test", + }, + }, + Spec: struct { + Target string `yaml:"target,omitempty" json:"target,omitempty" validate:"required"` + Cases []load.HttpCase `yaml:"cases" json:"cases" validate:"required,min=1,max=100,dive"` + }{ + Target: "target", + Cases: []load.HttpCase{ + { + HttpCase: tests.HttpCase{ + Name: "case_0", + Method: "POST", + }, + Type: "wave", + Wave: &load.WaveConfig{ + Low: 100, + High: 10_000, + Delta: 500, + }, + Repeats: 10, + Agents: 10, + RPS: 1_000, + SaveEntry: 5, + }, + }, }, } ) @@ -102,6 +359,7 @@ func TestValidator_Validate(t *testing.T) { var err error + // INVALID t.Run("InvalidVersionManifest: incorrect version", func(t *testing.T) { err = v.Validate(invalidVersionManifest) require.Error(t, err) @@ -132,14 +390,75 @@ func TestValidator_Validate(t *testing.T) { require.Contains(t, err.Error(), "stages") }) + t.Run("InvalidServiceManifest: empty name, both fields, replicas to large", func(t *testing.T) { + err = v.Validate(invalidServiceManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "name") + require.Contains(t, err.Error(), "dockerfile") + require.Contains(t, err.Error(), "image") + require.Contains(t, err.Error(), "replicas") + }) + t.Run("InvalidServerManifest: empty base url", func(t *testing.T) { err = v.Validate(invalidServerManifest) require.Error(t, err) require.Contains(t, err.Error(), "baseurl") }) + t.Run("InvalidHttpTestManifest: empty target, not cases", func(t *testing.T) { + err = v.Validate(invalidHttpTestManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "target") + require.Contains(t, err.Error(), "cases") + }) + + t.Run("InvalidHttpTestManifest: invalid cases: empty name, invalid HTTP method", func(t *testing.T) { + err = v.Validate(invalidHttpTestCaseManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "name") + }) + + t.Run("InvalidHttpLoadTestManifest: empty target, not cases", func(t *testing.T) { + err = v.Validate(invalidHttpLoadTestManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "target") + require.Contains(t, err.Error(), "cases") + }) + + t.Run("InvalidHttpLoadTestCaseManifest: load case not valid", func(t *testing.T) { + err = v.Validate(invalidHttpLoadTestCaseManifest) + require.Error(t, err) + require.Contains(t, err.Error(), "name") + require.Contains(t, err.Error(), "type") + require.Contains(t, err.Error(), "repeats") + require.Contains(t, err.Error(), "agents") + require.Contains(t, err.Error(), "rps") + require.Contains(t, err.Error(), "saveentry") + }) + + // VALID + t.Run("ValidPlanManifest", func(t *testing.T) { + err = v.Validate(validPlanManifest) + require.NoError(t, err) + }) + + t.Run("ValidServiceManifest", func(t *testing.T) { + err = v.Validate(validServiceManifest) + require.NoError(t, err) + }) + t.Run("ValidServerManifest", func(t *testing.T) { err = v.Validate(validServerManifest) require.NoError(t, err) }) + + t.Run("ValidHttpTestManifest", func(t *testing.T) { + err = v.Validate(validHttpTestCaseManifest) + require.NoError(t, err) + }) + + t.Run("ValidHttpLoadTestManifest", func(t *testing.T) { + err = v.Validate(validHttpLoadTestCaseManifest) + require.NoError(t, err) + }) }