From f534e41b6c7907e2d4e3d7c9ca2ab9a064580a06 Mon Sep 17 00:00:00 2001 From: ameersohel45 Date: Fri, 14 Nov 2025 01:04:32 +0530 Subject: [PATCH] 543 - add: test cases --- .../schemav2validator/cmd/plugin_test.go | 157 +++++++++ .../schemav2validator_test.go | 298 ++++++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 pkg/plugin/implementation/schemav2validator/cmd/plugin_test.go create mode 100644 pkg/plugin/implementation/schemav2validator/schemav2validator_test.go diff --git a/pkg/plugin/implementation/schemav2validator/cmd/plugin_test.go b/pkg/plugin/implementation/schemav2validator/cmd/plugin_test.go new file mode 100644 index 0000000..af9c10d --- /dev/null +++ b/pkg/plugin/implementation/schemav2validator/cmd/plugin_test.go @@ -0,0 +1,157 @@ +package main + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" +) + +const testSpec = `openapi: 3.1.0 +info: + title: Test API + version: 1.0.0 +paths: + /test: + post: + requestBody: + content: + application/json: + schema: + type: object + properties: + context: + type: object + properties: + action: + const: test +` + +func TestProvider_New(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(testSpec)) + })) + defer server.Close() + + tests := []struct { + name string + ctx context.Context + config map[string]string + wantErr bool + errMsg string + }{ + { + name: "nil context", + ctx: nil, + config: map[string]string{"url": server.URL}, + wantErr: true, + errMsg: "context cannot be nil", + }, + { + name: "missing url", + ctx: context.Background(), + config: map[string]string{}, + wantErr: true, + errMsg: "url not configured", + }, + { + name: "empty url", + ctx: context.Background(), + config: map[string]string{"url": ""}, + wantErr: true, + errMsg: "url not configured", + }, + { + name: "valid config with default TTL", + ctx: context.Background(), + config: map[string]string{"url": server.URL}, + wantErr: false, + }, + { + name: "valid config with custom TTL", + ctx: context.Background(), + config: map[string]string{ + "url": server.URL, + "cacheTTL": "7200", + }, + wantErr: false, + }, + { + name: "invalid TTL falls back to default", + ctx: context.Background(), + config: map[string]string{ + "url": server.URL, + "cacheTTL": "invalid", + }, + wantErr: false, + }, + { + name: "negative TTL falls back to default", + ctx: context.Background(), + config: map[string]string{ + "url": server.URL, + "cacheTTL": "-100", + }, + wantErr: false, + }, + { + name: "zero TTL falls back to default", + ctx: context.Background(), + config: map[string]string{ + "url": server.URL, + "cacheTTL": "0", + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + provider := schemav2ValidatorProvider{} + validator, cleanup, err := provider.New(tt.ctx, tt.config) + + if (err != nil) != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if tt.wantErr && tt.errMsg != "" && err != nil { + if !contains(err.Error(), tt.errMsg) { + t.Errorf("New() error = %v, want error containing %v", err, tt.errMsg) + } + } + + if !tt.wantErr { + if validator == nil { + t.Error("Expected validator instance, got nil") + } + if cleanup != nil { + t.Error("Expected nil cleanup function, got non-nil") + } + } + }) + } +} + +func TestProvider_ExportedVariable(t *testing.T) { + if Provider == (schemav2ValidatorProvider{}) { + t.Log("Provider variable is properly exported") + } else { + t.Error("Provider variable has unexpected value") + } +} + +func contains(s, substr string) bool { + if len(substr) == 0 { + return true + } + if len(s) < len(substr) { + return false + } + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +} diff --git a/pkg/plugin/implementation/schemav2validator/schemav2validator_test.go b/pkg/plugin/implementation/schemav2validator/schemav2validator_test.go new file mode 100644 index 0000000..6f5a71f --- /dev/null +++ b/pkg/plugin/implementation/schemav2validator/schemav2validator_test.go @@ -0,0 +1,298 @@ +package schemav2validator + +import ( + "context" + "net/http" + "net/http/httptest" + "os" + "testing" +) + +const testSpec = `openapi: 3.1.0 +info: + title: Test API + version: 1.0.0 +paths: + /search: + post: + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [context, message] + properties: + context: + type: object + required: [action] + properties: + action: + const: search + domain: + type: string + message: + type: object + /select: + post: + requestBody: + content: + application/json: + schema: + type: object + required: [context, message] + properties: + context: + allOf: + - type: object + properties: + action: + enum: [select] + message: + type: object + required: [order] + properties: + order: + type: object +` + +func TestNew(t *testing.T) { + tests := []struct { + name string + config *Config + wantErr bool + }{ + {"nil config", nil, true}, + {"empty URL", &Config{URL: ""}, true}, + {"invalid URL", &Config{URL: "http://invalid-domain-12345.com/spec.yaml"}, true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, _, err := New(context.Background(), tt.config) + if (err != nil) != tt.wantErr { + t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestValidate_ActionExtraction(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(testSpec)) + })) + defer server.Close() + + validator, _, err := New(context.Background(), &Config{URL: server.URL, CacheTTL: 3600}) + if err != nil { + t.Fatalf("Failed to create validator: %v", err) + } + + tests := []struct { + name string + payload string + wantErr bool + errMsg string + }{ + { + name: "valid search action", + payload: `{"context":{"action":"search","domain":"retail"},"message":{}}`, + wantErr: false, + }, + { + name: "valid select action with allOf", + payload: `{"context":{"action":"select"},"message":{"order":{}}}`, + wantErr: false, + }, + { + name: "missing action", + payload: `{"context":{},"message":{}}`, + wantErr: true, + errMsg: "missing field Action", + }, + { + name: "unsupported action", + payload: `{"context":{"action":"unknown"},"message":{}}`, + wantErr: true, + errMsg: "unsupported action: unknown", + }, + { + name: "action as number", + payload: `{"context":{"action":123},"message":{}}`, + wantErr: true, + errMsg: "failed to parse JSON payload", + }, + { + name: "invalid JSON", + payload: `{invalid json}`, + wantErr: true, + errMsg: "failed to parse JSON payload", + }, + { + name: "missing required field", + payload: `{"context":{"action":"search"}}`, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validator.Validate(context.Background(), nil, []byte(tt.payload)) + if (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.wantErr && tt.errMsg != "" && err != nil { + if !contains(err.Error(), tt.errMsg) { + t.Errorf("Validate() error = %v, want error containing %v", err, tt.errMsg) + } + } + }) + } +} + +func TestValidate_NestedValidation(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(testSpec)) + })) + defer server.Close() + + validator, _, err := New(context.Background(), &Config{URL: server.URL, CacheTTL: 3600}) + if err != nil { + t.Fatalf("Failed to create validator: %v", err) + } + + tests := []struct { + name string + payload string + wantErr bool + }{ + { + name: "select missing required order", + payload: `{"context":{"action":"select"},"message":{}}`, + wantErr: true, + }, + { + name: "select with order", + payload: `{"context":{"action":"select"},"message":{"order":{}}}`, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validator.Validate(context.Background(), nil, []byte(tt.payload)) + if (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestLoadSpec_LocalFile(t *testing.T) { + tmpFile, err := os.CreateTemp("", "test-spec-*.yaml") + if err != nil { + t.Fatalf("Failed to create temp file: %v", err) + } + defer os.Remove(tmpFile.Name()) + + if _, err := tmpFile.Write([]byte(testSpec)); err != nil { + t.Fatalf("Failed to write temp file: %v", err) + } + tmpFile.Close() + + validator, _, err := New(context.Background(), &Config{URL: tmpFile.Name(), CacheTTL: 3600}) + if err != nil { + t.Fatalf("Failed to load local spec: %v", err) + } + + validator.specMutex.RLock() + defer validator.specMutex.RUnlock() + + if validator.spec == nil || validator.spec.doc == nil { + t.Error("Spec not loaded from local file") + } +} + +func TestCacheTTL_DefaultValue(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(testSpec)) + })) + defer server.Close() + + validator, _, err := New(context.Background(), &Config{URL: server.URL}) + if err != nil { + t.Fatalf("Failed to create validator: %v", err) + } + + if validator.config.CacheTTL != 3600 { + t.Errorf("Expected default CacheTTL 3600, got %d", validator.config.CacheTTL) + } +} + +func TestValidate_EdgeCases(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(testSpec)) + })) + defer server.Close() + + validator, _, err := New(context.Background(), &Config{URL: server.URL, CacheTTL: 3600}) + if err != nil { + t.Fatalf("Failed to create validator: %v", err) + } + + tests := []struct { + name string + payload string + wantErr bool + }{ + { + name: "empty payload", + payload: `{}`, + wantErr: true, + }, + { + name: "null context", + payload: `{"context":null,"message":{}}`, + wantErr: true, + }, + { + name: "empty string action", + payload: `{"context":{"action":""},"message":{}}`, + wantErr: true, + }, + { + name: "action with whitespace", + payload: `{"context":{"action":" search "},"message":{}}`, + wantErr: true, + }, + { + name: "case sensitive action", + payload: `{"context":{"action":"Search"},"message":{}}`, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validator.Validate(context.Background(), nil, []byte(tt.payload)) + if (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func contains(s, substr string) bool { + if len(substr) == 0 { + return true + } + if len(s) < len(substr) { + return false + } + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +}