fix: resolve comments
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -129,3 +129,13 @@ dist
|
|||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# Ignore compiled shared object files
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Ignore coverage output files
|
||||||
|
coverage.out
|
||||||
|
coverage.html
|
||||||
|
|
||||||
|
# Ignore the schema directory used for testing
|
||||||
|
/schemas/
|
||||||
9
go.mod
9
go.mod
@@ -1,16 +1,23 @@
|
|||||||
module github.com/beckn/beckn-onix
|
module github.com/beckn/beckn-onix
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
|
||||||
|
golang.org/x/crypto v0.36.0
|
||||||
|
)
|
||||||
|
|
||||||
toolchain go1.23.7
|
toolchain go1.23.7
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03
|
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
golang.org/x/crypto v0.36.0
|
golang.org/x/crypto v0.36.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.31.0 // indirect
|
||||||
require (
|
require (
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -1,3 +1,7 @@
|
|||||||
|
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||||
|
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||||
|
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
@@ -18,5 +22,7 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
|
|||||||
@@ -16,30 +16,30 @@ import (
|
|||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Level string
|
type level string
|
||||||
|
|
||||||
type DestinationType string
|
type destinationType string
|
||||||
|
|
||||||
type Destination struct {
|
type destination struct {
|
||||||
Type DestinationType `yaml:"type"`
|
Type destinationType `yaml:"type"`
|
||||||
Config map[string]string `yaml:"config"`
|
Config map[string]string `yaml:"config"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Stdout DestinationType = "stdout"
|
Stdout destinationType = "stdout"
|
||||||
File DestinationType = "file"
|
File destinationType = "file"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DebugLevel Level = "debug"
|
DebugLevel level = "debug"
|
||||||
InfoLevel Level = "info"
|
InfoLevel level = "info"
|
||||||
WarnLevel Level = "warn"
|
WarnLevel level = "warn"
|
||||||
ErrorLevel Level = "error"
|
ErrorLevel level = "error"
|
||||||
FatalLevel Level = "fatal"
|
FatalLevel level = "fatal"
|
||||||
PanicLevel Level = "panic"
|
PanicLevel level = "panic"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logLevels = map[Level]zerolog.Level{
|
var logLevels = map[level]zerolog.Level{
|
||||||
DebugLevel: zerolog.DebugLevel,
|
DebugLevel: zerolog.DebugLevel,
|
||||||
InfoLevel: zerolog.InfoLevel,
|
InfoLevel: zerolog.InfoLevel,
|
||||||
WarnLevel: zerolog.WarnLevel,
|
WarnLevel: zerolog.WarnLevel,
|
||||||
@@ -49,8 +49,8 @@ var logLevels = map[Level]zerolog.Level{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Level Level `yaml:"level"`
|
Level level `yaml:"level"`
|
||||||
Destinations []Destination `yaml:"destinations"`
|
Destinations []destination `yaml:"destinations"`
|
||||||
ContextKeys []string `yaml:"contextKeys"`
|
ContextKeys []string `yaml:"contextKeys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ func (config *Config) validate() error {
|
|||||||
|
|
||||||
var defaultConfig = Config{
|
var defaultConfig = Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{Type: Stdout},
|
{Type: Stdout},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -22,7 +21,7 @@ type ctxKey any
|
|||||||
var requestID ctxKey = "requestID"
|
var requestID ctxKey = "requestID"
|
||||||
var userID ctxKey = "userID"
|
var userID ctxKey = "userID"
|
||||||
|
|
||||||
func setupLogger(t *testing.T, l Level) string {
|
func setupLogger(t *testing.T, l level) string {
|
||||||
dir := filepath.Dir(testLogFilePath)
|
dir := filepath.Dir(testLogFilePath)
|
||||||
err := os.MkdirAll(dir, os.ModePerm)
|
err := os.MkdirAll(dir, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -31,7 +30,7 @@ func setupLogger(t *testing.T, l Level) string {
|
|||||||
|
|
||||||
config := Config{
|
config := Config{
|
||||||
Level: l,
|
Level: l,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{
|
{
|
||||||
Type: File,
|
Type: File,
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
@@ -53,21 +52,11 @@ func setupLogger(t *testing.T, l Level) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readLogFile(t *testing.T, logPath string) []string {
|
func readLogFile(t *testing.T, logPath string) []string {
|
||||||
file, err := os.Open(logPath)
|
b, err := os.ReadFile(logPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to open log file: %v", err)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
var lines []string
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
lines = append(lines, scanner.Text())
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
t.Fatalf("failed to read log file: %v", err)
|
t.Fatalf("failed to read log file: %v", err)
|
||||||
}
|
}
|
||||||
|
return strings.Split(string(b), "\n")
|
||||||
return lines
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLogLine(t *testing.T, line string) map[string]interface{} {
|
func parseLogLine(t *testing.T, line string) map[string]interface{} {
|
||||||
@@ -404,7 +393,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Valid config with Stdout",
|
name: "Valid config with Stdout",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{Type: Stdout},
|
{Type: Stdout},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -414,7 +403,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Valid config with File destination and valid path",
|
name: "Valid config with File destination and valid path",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{
|
{
|
||||||
Type: File,
|
Type: File,
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
@@ -432,7 +421,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Error: Invalid log level",
|
name: "Error: Invalid log level",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: "invalid",
|
Level: "invalid",
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{Type: Stdout},
|
{Type: Stdout},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -442,7 +431,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Error: No destinations provided",
|
name: "Error: No destinations provided",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{},
|
Destinations: []destination{},
|
||||||
},
|
},
|
||||||
wantErr: ErrLogDestinationNil,
|
wantErr: ErrLogDestinationNil,
|
||||||
},
|
},
|
||||||
@@ -450,7 +439,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Error: Invalid destination type",
|
name: "Error: Invalid destination type",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{Type: "unknown"},
|
{Type: "unknown"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -460,7 +449,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Error: Missing file path for file destination",
|
name: "Error: Missing file path for file destination",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{
|
{
|
||||||
Type: File,
|
Type: File,
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
@@ -475,7 +464,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Error: Invalid maxSize value in file destination",
|
name: "Error: Invalid maxSize value in file destination",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{
|
{
|
||||||
Type: File,
|
Type: File,
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
@@ -491,7 +480,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Error: Invalid maxBackups value in file destination",
|
name: "Error: Invalid maxBackups value in file destination",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{
|
{
|
||||||
Type: File,
|
Type: File,
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
@@ -507,7 +496,7 @@ func TestValidateConfig(t *testing.T) {
|
|||||||
name: "Error: Invalid maxAge value in file destination",
|
name: "Error: Invalid maxAge value in file destination",
|
||||||
config: Config{
|
config: Config{
|
||||||
Level: InfoLevel,
|
Level: InfoLevel,
|
||||||
Destinations: []Destination{
|
Destinations: []destination{
|
||||||
{
|
{
|
||||||
Type: File,
|
Type: File,
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
|
|||||||
16
pkg/plugin/definition/schemaValidator.go
Normal file
16
pkg/plugin/definition/schemaValidator.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package definition
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SchemaValidator interface for schema validation.
|
||||||
|
type SchemaValidator interface {
|
||||||
|
Validate(ctx context.Context, url *url.URL, payload []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaValidatorProvider interface for creating validators.
|
||||||
|
type SchemaValidatorProvider interface {
|
||||||
|
New(ctx context.Context, config map[string]string) (SchemaValidator, func() error, error)
|
||||||
|
}
|
||||||
33
pkg/plugin/implementation/schemaValidator/cmd/plugin.go
Normal file
33
pkg/plugin/implementation/schemaValidator/cmd/plugin.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
definition "github.com/beckn/beckn-onix/pkg/plugin/definition"
|
||||||
|
schemaValidator "github.com/beckn/beckn-onix/pkg/plugin/implementation/schemaValidator"
|
||||||
|
)
|
||||||
|
|
||||||
|
// schemaValidatorProvider provides instances of schemaValidator.
|
||||||
|
type schemaValidatorProvider struct{}
|
||||||
|
|
||||||
|
// New initializes a new Verifier instance.
|
||||||
|
func (vp schemaValidatorProvider) New(ctx context.Context, config map[string]string) (definition.SchemaValidator, func() error, error) {
|
||||||
|
if ctx == nil {
|
||||||
|
return nil, nil, errors.New("context cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract schemaDir from the config map
|
||||||
|
schemaDir, ok := config["schemaDir"]
|
||||||
|
if !ok || schemaDir == "" {
|
||||||
|
return nil, nil, errors.New("config must contain 'schemaDir'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new schemaValidator instance with the provided configuration
|
||||||
|
return schemaValidator.New(ctx, &schemaValidator.Config{
|
||||||
|
SchemaDir: schemaDir,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provider is the exported symbol that the plugin manager will look for.
|
||||||
|
var Provider definition.SchemaValidatorProvider = schemaValidatorProvider{}
|
||||||
150
pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go
Normal file
150
pkg/plugin/implementation/schemaValidator/cmd/plugin_test.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupTestSchema creates a temporary directory and writes a sample schema file.
|
||||||
|
func setupTestSchema(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// Create a temporary directory for the schema
|
||||||
|
schemaDir, err := os.MkdirTemp("", "schemas")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create temp directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the directory structure for the schema file
|
||||||
|
schemaFilePath := filepath.Join(schemaDir, "example", "1.0", "test_schema.json")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(schemaFilePath), 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create schema directory structure: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a sample schema
|
||||||
|
schemaContent := `{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"context": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {"type": "string"},
|
||||||
|
"version": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["domain", "version"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["context"]
|
||||||
|
}`
|
||||||
|
|
||||||
|
// Write the schema to the file
|
||||||
|
if err := os.WriteFile(schemaFilePath, []byte(schemaContent), 0644); err != nil {
|
||||||
|
t.Fatalf("Failed to write schema file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return schemaDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidatorProviderSuccess tests successful ValidatorProvider implementation.
|
||||||
|
func TestValidatorProviderSuccess(t *testing.T) {
|
||||||
|
schemaDir := setupTestSchema(t)
|
||||||
|
defer os.RemoveAll(schemaDir)
|
||||||
|
|
||||||
|
// Define test cases.
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ctx context.Context
|
||||||
|
config map[string]string
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid schema directory",
|
||||||
|
ctx: context.Background(), // Valid context
|
||||||
|
config: map[string]string{"schemaDir": schemaDir},
|
||||||
|
expectedError: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test using table-driven tests
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
vp := schemaValidatorProvider{}
|
||||||
|
schemaValidator, _, err := vp.New(tt.ctx, tt.config)
|
||||||
|
|
||||||
|
// Ensure no error occurred
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the schemaValidator is not nil
|
||||||
|
if schemaValidator == nil {
|
||||||
|
t.Error("expected a non-nil schemaValidator, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidatorProviderSuccess tests cases where ValidatorProvider creation should fail.
|
||||||
|
func TestValidatorProviderFailure(t *testing.T) {
|
||||||
|
schemaDir := setupTestSchema(t)
|
||||||
|
defer os.RemoveAll(schemaDir)
|
||||||
|
|
||||||
|
// Define test cases.
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ctx context.Context
|
||||||
|
config map[string]string
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Config is empty",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{},
|
||||||
|
expectedError: "config must contain 'schemaDir'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "schemaDir is empty",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{"schemaDir": ""},
|
||||||
|
expectedError: "config must contain 'schemaDir'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid schema directory",
|
||||||
|
ctx: context.Background(), // Valid context
|
||||||
|
config: map[string]string{"schemaDir": "/invalid/dir"},
|
||||||
|
expectedError: "failed to initialise schemaValidator: schema directory does not exist: /invalid/dir",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Nil context",
|
||||||
|
ctx: nil, // Nil context
|
||||||
|
config: map[string]string{"schemaDir": schemaDir},
|
||||||
|
expectedError: "context cannot be nil",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test using table-driven tests
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
vp := schemaValidatorProvider{}
|
||||||
|
_, _, err := vp.New(tt.ctx, tt.config)
|
||||||
|
|
||||||
|
// Check for expected error
|
||||||
|
if tt.expectedError != "" {
|
||||||
|
if err == nil || !strings.Contains(err.Error(), tt.expectedError) {
|
||||||
|
t.Errorf("expected error %q, got %v", tt.expectedError, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure no error occurred
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
197
pkg/plugin/implementation/schemaValidator/schemaValidator.go
Normal file
197
pkg/plugin/implementation/schemaValidator/schemaValidator.go
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
package schemaValidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
response "github.com/beckn/beckn-onix/pkg/response"
|
||||||
|
|
||||||
|
"github.com/santhosh-tekuri/jsonschema/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Payload represents the structure of the data payload with context information.
|
||||||
|
type payload struct {
|
||||||
|
Context struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
} `json:"context"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaValidator implements the Validator interface.
|
||||||
|
type SchemaValidator struct {
|
||||||
|
config *Config
|
||||||
|
schemaCache map[string]*jsonschema.Schema
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config struct for SchemaValidator.
|
||||||
|
type Config struct {
|
||||||
|
SchemaDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new ValidatorProvider instance.
|
||||||
|
func New(ctx context.Context, config *Config) (*SchemaValidator, func() error, error) {
|
||||||
|
// Check if config is nil
|
||||||
|
if config == nil {
|
||||||
|
return nil, nil, fmt.Errorf("config cannot be nil")
|
||||||
|
}
|
||||||
|
v := &SchemaValidator{
|
||||||
|
config: config,
|
||||||
|
schemaCache: make(map[string]*jsonschema.Schema),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call Initialise function to load schemas and get validators
|
||||||
|
if err := v.initialise(); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to initialise schemaValidator: %v", err)
|
||||||
|
}
|
||||||
|
return v, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates the given data against the schema.
|
||||||
|
func (v *SchemaValidator) Validate(ctx context.Context, url *url.URL, data []byte) error {
|
||||||
|
var payloadData payload
|
||||||
|
err := json.Unmarshal(data, &payloadData)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse JSON payload: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract domain, version, and endpoint from the payload and uri.
|
||||||
|
cxt_domain := payloadData.Context.Domain
|
||||||
|
version := payloadData.Context.Version
|
||||||
|
version = fmt.Sprintf("v%s", version)
|
||||||
|
|
||||||
|
endpoint := path.Base(url.String())
|
||||||
|
// ToDo Add debug log here
|
||||||
|
fmt.Println("Handling request for endpoint:", endpoint)
|
||||||
|
domain := strings.ToLower(cxt_domain)
|
||||||
|
domain = strings.ReplaceAll(domain, ":", "_")
|
||||||
|
|
||||||
|
// Construct the schema file name.
|
||||||
|
schemaFileName := fmt.Sprintf("%s_%s_%s", domain, version, endpoint)
|
||||||
|
|
||||||
|
// Retrieve the schema from the cache.
|
||||||
|
schema, exists := v.schemaCache[schemaFileName]
|
||||||
|
if !exists {
|
||||||
|
return fmt.Errorf("schema not found for domain: %s", schemaFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonData any
|
||||||
|
if err := json.Unmarshal(data, &jsonData); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse JSON data: %v", err)
|
||||||
|
}
|
||||||
|
err = schema.Validate(jsonData)
|
||||||
|
if err != nil {
|
||||||
|
// Handle schema validation errors
|
||||||
|
if validationErr, ok := err.(*jsonschema.ValidationError); ok {
|
||||||
|
// Convert validation errors into an array of SchemaValError
|
||||||
|
var schemaErrors []response.Error
|
||||||
|
for _, cause := range validationErr.Causes {
|
||||||
|
// Extract the path and message from the validation error
|
||||||
|
path := strings.Join(cause.InstanceLocation, ".") // JSON path to the invalid field
|
||||||
|
message := cause.Error() // Validation error message
|
||||||
|
|
||||||
|
// Append the error to the schemaErrors array
|
||||||
|
schemaErrors = append(schemaErrors, response.Error{
|
||||||
|
Paths: path,
|
||||||
|
Message: message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Return the array of schema validation errors
|
||||||
|
return &response.SchemaValidationErr{Errors: schemaErrors}
|
||||||
|
}
|
||||||
|
// Return a generic error for non-validation errors
|
||||||
|
return fmt.Errorf("validation failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return nil if validation succeeds
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatorProvider provides instances of Validator.
|
||||||
|
type ValidatorProvider struct{}
|
||||||
|
|
||||||
|
// Initialise initialises the validator provider by compiling all the JSON schema files
|
||||||
|
// from the specified directory and storing them in a cache indexed by their schema filenames.
|
||||||
|
func (v *SchemaValidator) initialise() error {
|
||||||
|
schemaDir := v.config.SchemaDir
|
||||||
|
// Check if the directory exists and is accessible.
|
||||||
|
info, err := os.Stat(schemaDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("schema directory does not exist: %s", schemaDir)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("failed to access schema directory: %v", err)
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
return fmt.Errorf("provided schema path is not a directory: %s", schemaDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler := jsonschema.NewCompiler()
|
||||||
|
|
||||||
|
// Helper function to process directories recursively.
|
||||||
|
var processDir func(dir string) error
|
||||||
|
processDir = func(dir string) error {
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
path := filepath.Join(dir, entry.Name())
|
||||||
|
if entry.IsDir() {
|
||||||
|
// Recursively process subdirectories.
|
||||||
|
if err := processDir(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if filepath.Ext(entry.Name()) == ".json" {
|
||||||
|
// Process JSON files.
|
||||||
|
compiledSchema, err := compiler.Compile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to compile JSON schema from file %s: %v", entry.Name(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use relative path from schemaDir to avoid absolute paths and make schema keys domain/version specific.
|
||||||
|
relativePath, err := filepath.Rel(schemaDir, path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get relative path for file %s: %v", entry.Name(), err)
|
||||||
|
}
|
||||||
|
// Split the relative path to get domain, version, and schema.
|
||||||
|
parts := strings.Split(relativePath, string(os.PathSeparator))
|
||||||
|
|
||||||
|
// Ensure that the file path has at least 3 parts: domain, version, and schema file.
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return fmt.Errorf("invalid schema file structure, expected domain/version/schema.json but got: %s", relativePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract domain, version, and schema filename from the parts.
|
||||||
|
// Validate that the extracted parts are non-empty.
|
||||||
|
domain := strings.TrimSpace(parts[0])
|
||||||
|
version := strings.TrimSpace(parts[1])
|
||||||
|
schemaFileName := strings.TrimSpace(parts[2])
|
||||||
|
schemaFileName = strings.TrimSuffix(schemaFileName, ".json")
|
||||||
|
|
||||||
|
if domain == "" || version == "" || schemaFileName == "" {
|
||||||
|
return fmt.Errorf("invalid schema file structure, one or more components are empty. Relative path: %s", relativePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a unique key combining domain, version, and schema name (e.g., ondc_trv10_v2.0.0_schema).
|
||||||
|
uniqueKey := fmt.Sprintf("%s_%s_%s", domain, version, schemaFileName)
|
||||||
|
// Store the compiled schema in the SchemaCache using the unique key.
|
||||||
|
v.schemaCache[uniqueKey] = compiledSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start processing from the root schema directory.
|
||||||
|
if err := processDir(schemaDir); err != nil {
|
||||||
|
return fmt.Errorf("failed to read schema directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,353 @@
|
|||||||
|
package schemaValidator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/santhosh-tekuri/jsonschema/v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupTestSchema creates a temporary directory and writes a sample schema file.
|
||||||
|
func setupTestSchema(t *testing.T) string {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
// Create a temporary directory for the schema
|
||||||
|
schemaDir, err := os.MkdirTemp("", "schemas")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create temp directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the directory structure for the schema file
|
||||||
|
schemaFilePath := filepath.Join(schemaDir, "example", "v1.0", "endpoint.json")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(schemaFilePath), 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create schema directory structure: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a sample schema
|
||||||
|
schemaContent := `{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"context": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {"type": "string"},
|
||||||
|
"version": {"type": "string"},
|
||||||
|
"action": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["domain", "version", "action"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["context"]
|
||||||
|
}`
|
||||||
|
|
||||||
|
// Write the schema to the file
|
||||||
|
if err := os.WriteFile(schemaFilePath, []byte(schemaContent), 0644); err != nil {
|
||||||
|
t.Fatalf("Failed to write schema file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return schemaDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidator_Validate_Success(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
payload string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid payload",
|
||||||
|
url: "http://example.com/endpoint",
|
||||||
|
payload: `{"context": {"domain": "example", "version": "1.0", "action": "endpoint"}}`,
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a temporary schema directory for testing
|
||||||
|
schemaDir := setupTestSchema(t)
|
||||||
|
defer os.RemoveAll(schemaDir)
|
||||||
|
|
||||||
|
config := &Config{SchemaDir: schemaDir}
|
||||||
|
v, _, err := New(context.Background(), config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create validator: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
u, _ := url.Parse(tt.url)
|
||||||
|
err := v.Validate(context.Background(), u, []byte(tt.payload))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Logf("Test %s passed with no errors", tt.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidator_Validate_Failure(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
url string
|
||||||
|
payload string
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Invalid JSON payload",
|
||||||
|
url: "http://example.com/endpoint",
|
||||||
|
payload: `{"context": {"domain": "example", "version": "1.0"`,
|
||||||
|
wantErr: "failed to parse JSON payload",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Schema validation failure",
|
||||||
|
url: "http://example.com/endpoint",
|
||||||
|
payload: `{"context": {"domain": "example", "version": "1.0"}}`,
|
||||||
|
wantErr: "context: at '/context': missing property 'action'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Schema not found",
|
||||||
|
url: "http://example.com/unknown_endpoint",
|
||||||
|
payload: `{"context": {"domain": "example", "version": "1.0"}}`,
|
||||||
|
wantErr: "schema not found for domain",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a temporary schema directory for testing
|
||||||
|
schemaDir := setupTestSchema(t)
|
||||||
|
defer os.RemoveAll(schemaDir)
|
||||||
|
|
||||||
|
config := &Config{SchemaDir: schemaDir}
|
||||||
|
v, _, err := New(context.Background(), config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create validator: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
u, _ := url.Parse(tt.url)
|
||||||
|
err := v.Validate(context.Background(), u, []byte(tt.payload))
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected error containing '%s', but got nil", tt.wantErr)
|
||||||
|
} else if !strings.Contains(err.Error(), tt.wantErr) {
|
||||||
|
t.Errorf("Expected error containing '%s', but got '%v'", tt.wantErr, err)
|
||||||
|
} else {
|
||||||
|
t.Logf("Test %s passed with expected error: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Logf("Test %s passed with no errors", tt.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidator_Initialise(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setupFunc func(schemaDir string) error
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Schema directory does not exist",
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Do not create the schema directory
|
||||||
|
return nil
|
||||||
|
|
||||||
|
},
|
||||||
|
wantErr: "schema directory does not exist",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Schema path is not a directory",
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Create a file instead of a directory
|
||||||
|
return os.WriteFile(schemaDir, []byte{}, 0644)
|
||||||
|
},
|
||||||
|
wantErr: "provided schema path is not a directory",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid schema file structure",
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Create an invalid schema file structure
|
||||||
|
invalidSchemaFile := filepath.Join(schemaDir, "invalid_schema.json")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create directory: %v", err)
|
||||||
|
}
|
||||||
|
return os.WriteFile(invalidSchemaFile, []byte(`{}`), 0644)
|
||||||
|
},
|
||||||
|
wantErr: "invalid schema file structure",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Failed to compile JSON schema",
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Create a schema file with invalid JSON
|
||||||
|
invalidSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create directory: %v", err)
|
||||||
|
}
|
||||||
|
return os.WriteFile(invalidSchemaFile, []byte(`{invalid json}`), 0644)
|
||||||
|
},
|
||||||
|
wantErr: "failed to compile JSON schema",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid schema file structure with empty components",
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Create a schema file with empty domain, version, or schema name
|
||||||
|
invalidSchemaFile := filepath.Join(schemaDir, "", "1.0", "endpoint.json")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create directory: %v", err)
|
||||||
|
}
|
||||||
|
return os.WriteFile(invalidSchemaFile, []byte(`{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"context": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {"type": "string"},
|
||||||
|
"version": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["domain", "version"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["context"]
|
||||||
|
}`), 0644)
|
||||||
|
},
|
||||||
|
wantErr: "failed to read schema directory: invalid schema file structure, expected domain/version/schema.json but got: 1.0/endpoint.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Failed to read directory",
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Create a directory and remove read permissions
|
||||||
|
if err := os.MkdirAll(schemaDir, 0000); err != nil {
|
||||||
|
t.Fatalf("Failed to create directory: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
wantErr: "failed to read directory",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Valid schema directory",
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Create a valid schema file
|
||||||
|
validSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json")
|
||||||
|
if err := os.MkdirAll(filepath.Dir(validSchemaFile), 0755); err != nil {
|
||||||
|
t.Fatalf("Failed to create directory: %v", err)
|
||||||
|
}
|
||||||
|
return os.WriteFile(validSchemaFile, []byte(`{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"context": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"domain": {"type": "string"},
|
||||||
|
"version": {"type": "string"}
|
||||||
|
},
|
||||||
|
"required": ["domain", "version"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["context"]
|
||||||
|
}`), 0644)
|
||||||
|
},
|
||||||
|
wantErr: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Setup a temporary schema directory for testing
|
||||||
|
schemaDir := filepath.Join(os.TempDir(), "schemas")
|
||||||
|
defer os.RemoveAll(schemaDir)
|
||||||
|
|
||||||
|
// Run the setup function to prepare the test case
|
||||||
|
if err := tt.setupFunc(schemaDir); err != nil {
|
||||||
|
t.Fatalf("setupFunc() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &Config{SchemaDir: schemaDir}
|
||||||
|
v := &SchemaValidator{
|
||||||
|
config: config,
|
||||||
|
schemaCache: make(map[string]*jsonschema.Schema),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := v.initialise()
|
||||||
|
if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") {
|
||||||
|
t.Errorf("Error: initialise() returned error = %v, expected error = %v", err, tt.wantErr)
|
||||||
|
} else if err == nil {
|
||||||
|
t.Logf("Test %s passed: validator initialized successfully", tt.name)
|
||||||
|
} else {
|
||||||
|
t.Logf("Test %s passed with expected error: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidatorNew_Success(t *testing.T) {
|
||||||
|
schemaDir := setupTestSchema(t)
|
||||||
|
defer os.RemoveAll(schemaDir)
|
||||||
|
|
||||||
|
config := &Config{SchemaDir: schemaDir}
|
||||||
|
_, _, err := New(context.Background(), config)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidatorNewFailure(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
config *Config
|
||||||
|
setupFunc func(schemaDir string) error
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Config is nil",
|
||||||
|
config: nil,
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
wantErr: "config cannot be nil",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Failed to initialise validators",
|
||||||
|
config: &Config{
|
||||||
|
SchemaDir: "/invalid/path",
|
||||||
|
},
|
||||||
|
setupFunc: func(schemaDir string) error {
|
||||||
|
// Do not create the schema directory
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
wantErr: "ailed to initialise schemaValidator: schema directory does not exist: /invalid/path",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// Run the setup function if provided
|
||||||
|
if tt.setupFunc != nil {
|
||||||
|
schemaDir := ""
|
||||||
|
if tt.config != nil {
|
||||||
|
schemaDir = tt.config.SchemaDir
|
||||||
|
}
|
||||||
|
if err := tt.setupFunc(schemaDir); err != nil {
|
||||||
|
t.Fatalf("Setup function failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the New function with the test config
|
||||||
|
_, _, err := New(context.Background(), tt.config)
|
||||||
|
if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") {
|
||||||
|
t.Errorf("Error: New() returned error = %v, expected error = %v", err, tt.wantErr)
|
||||||
|
} else {
|
||||||
|
t.Logf("Test %s passed with expected error: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrorType string
|
type ErrorType string
|
||||||
@@ -23,6 +24,20 @@ type Error struct {
|
|||||||
Paths string `json:"paths,omitempty"`
|
Paths string `json:"paths,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SchemaValidationErr represents a collection of schema validation failures.
|
||||||
|
type SchemaValidationErr struct {
|
||||||
|
Errors []Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface for SchemaValidationErr.
|
||||||
|
func (e *SchemaValidationErr) Error() string {
|
||||||
|
var errorMessages []string
|
||||||
|
for _, err := range e.Errors {
|
||||||
|
errorMessages = append(errorMessages, fmt.Sprintf("%s: %s", err.Paths, err.Message))
|
||||||
|
}
|
||||||
|
return strings.Join(errorMessages, "; ")
|
||||||
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Ack struct {
|
Ack struct {
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
|
|||||||
BIN
schemas.zip
Normal file
BIN
schemas.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user