Handling forward routing rules using routing plugin
This commit is contained in:
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -9,8 +10,10 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// setupTestConfig creates a temporary directory and writes a sample routing rules file.
|
||||
func setupTestConfig(t *testing.T) string {
|
||||
//go:embed testData/*
|
||||
var testData embed.FS
|
||||
|
||||
func setupTestConfig(t *testing.T, yamlFileName string) string {
|
||||
t.Helper()
|
||||
|
||||
// Create a temporary directory for the routing rules
|
||||
@@ -19,114 +22,124 @@ func setupTestConfig(t *testing.T) string {
|
||||
t.Fatalf("Failed to create temp directory: %v", err)
|
||||
}
|
||||
|
||||
// Define sample routing rules
|
||||
rulesContent := `
|
||||
routing_rules:
|
||||
- domain: "ONDC:TRV11"
|
||||
version: "2.0.0"
|
||||
routing_type: "url"
|
||||
target:
|
||||
url: "https://services-backend/trv/v1"
|
||||
endpoints:
|
||||
- select
|
||||
- init
|
||||
- confirm
|
||||
- status
|
||||
|
||||
- domain: "ONDC:TRV11"
|
||||
version: "2.0.0"
|
||||
routing_type: "msgq"
|
||||
target:
|
||||
topic_id: "trv_topic_id1"
|
||||
endpoints:
|
||||
- search
|
||||
`
|
||||
// Read the YAML file content
|
||||
yamlContent := readYAMLFile(t, yamlFileName)
|
||||
|
||||
// Write the routing rules to a file
|
||||
rulesFilePath := filepath.Join(configDir, "routing_rules.yaml")
|
||||
if err := os.WriteFile(rulesFilePath, []byte(rulesContent), 0644); err != nil {
|
||||
if err := os.WriteFile(rulesFilePath, []byte(yamlContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to write routing rules file: %v", err)
|
||||
}
|
||||
|
||||
return rulesFilePath
|
||||
}
|
||||
|
||||
func readYAMLFile(t *testing.T, fileName string) string {
|
||||
t.Helper()
|
||||
|
||||
// Read the YAML file
|
||||
content, err := testData.ReadFile("testData/" + fileName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read YAML file: %v", err)
|
||||
}
|
||||
|
||||
return string(content)
|
||||
}
|
||||
|
||||
// setupRouter is a helper function to create router instance.
|
||||
func setupRouter(t *testing.T, configFile string) (*Router, func() error, string) {
|
||||
rulesFilePath := setupTestConfig(t, configFile)
|
||||
config := &Config{
|
||||
RoutingConfig: rulesFilePath,
|
||||
}
|
||||
router, _, err := New(context.Background(), config)
|
||||
if err != nil {
|
||||
t.Fatalf("New failed: %v", err)
|
||||
}
|
||||
return router, nil, rulesFilePath
|
||||
}
|
||||
|
||||
// TestNew tests the New function.
|
||||
func TestNew(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rulesFilePath := setupTestConfig(t)
|
||||
defer os.RemoveAll(filepath.Dir(rulesFilePath))
|
||||
|
||||
// Define test cases
|
||||
tests := []struct {
|
||||
name string
|
||||
config *Config
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Valid configuration",
|
||||
config: &Config{
|
||||
RoutingConfig: rulesFilePath,
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Empty config",
|
||||
config: nil,
|
||||
expectedError: "config cannot be nil",
|
||||
},
|
||||
{
|
||||
name: "Empty routing config path",
|
||||
config: &Config{
|
||||
RoutingConfig: "",
|
||||
},
|
||||
expectedError: "routing_config path is empty",
|
||||
},
|
||||
{
|
||||
name: "Routing config file does not exist",
|
||||
config: &Config{
|
||||
RoutingConfig: "/nonexistent/path/to/rules.yaml",
|
||||
},
|
||||
expectedError: "error reading config file",
|
||||
},
|
||||
// List of YAML files in the testData directory
|
||||
yamlFiles := []string{
|
||||
"bap_caller.yaml",
|
||||
"bap_receiver.yaml",
|
||||
"bpp_caller.yaml",
|
||||
"bpp_receiver.yaml",
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
router, closeFunc, err := New(ctx, tt.config)
|
||||
for _, yamlFile := range yamlFiles {
|
||||
t.Run(yamlFile, func(t *testing.T) {
|
||||
rulesFilePath := setupTestConfig(t, yamlFile)
|
||||
defer os.RemoveAll(filepath.Dir(rulesFilePath))
|
||||
|
||||
// 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
|
||||
// Define test cases
|
||||
tests := []struct {
|
||||
name string
|
||||
config *Config
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Valid configuration",
|
||||
config: &Config{
|
||||
RoutingConfig: rulesFilePath,
|
||||
},
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "Empty config",
|
||||
config: nil,
|
||||
expectedError: "config cannot be nil",
|
||||
},
|
||||
{
|
||||
name: "Empty routing config path",
|
||||
config: &Config{
|
||||
RoutingConfig: "",
|
||||
},
|
||||
expectedError: "routingConfig path is empty",
|
||||
},
|
||||
{
|
||||
name: "Routing config file does not exist",
|
||||
config: &Config{
|
||||
RoutingConfig: "/nonexistent/path/to/rules.yaml",
|
||||
},
|
||||
expectedError: "error reading config file",
|
||||
},
|
||||
}
|
||||
|
||||
// Ensure no error occurred
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
router, _, err := New(ctx, tt.config)
|
||||
|
||||
// Ensure the router and close function are not nil
|
||||
if router == nil {
|
||||
t.Error("expected a non-nil Router instance, got nil")
|
||||
}
|
||||
if closeFunc == nil {
|
||||
t.Error("expected a non-nil close function, got nil")
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
// Test the close function
|
||||
if err := closeFunc(); err != nil {
|
||||
t.Errorf("close function returned an error: %v", err)
|
||||
// Ensure no error occurred
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure the router and close function are not nil
|
||||
if router == nil {
|
||||
t.Error("expected a non-nil Router instance, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateRules_Success tests the validate function for success cases.
|
||||
func TestValidateRules_Success(t *testing.T) {
|
||||
// TestValidateRulesSuccess tests the validate function for success cases.
|
||||
func TestValidateRulesSuccess(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rules []routingRule
|
||||
@@ -135,13 +148,13 @@ func TestValidateRules_Success(t *testing.T) {
|
||||
name: "Valid rules with url routing",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "url",
|
||||
Target: target{
|
||||
URL: "https://example.com/api",
|
||||
},
|
||||
Endpoints: []string{"search", "select"},
|
||||
Endpoints: []string{"on_search", "on_select"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -149,13 +162,49 @@ func TestValidateRules_Success(t *testing.T) {
|
||||
name: "Valid rules with msgq routing",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "msgq",
|
||||
Target: target{
|
||||
TopicID: "example_topic",
|
||||
},
|
||||
Endpoints: []string{"search", "select"},
|
||||
Endpoints: []string{"on_search", "on_select"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Valid rules with bpp routing to gateway",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "bpp",
|
||||
Target: target{
|
||||
URL: "https://mock_gateway.com/api",
|
||||
},
|
||||
Endpoints: []string{"search"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Valid rules with bpp routing",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "bpp",
|
||||
Endpoints: []string{"select"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Valid rules with bap routing",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "bap",
|
||||
Endpoints: []string{"select"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -171,8 +220,8 @@ func TestValidateRules_Success(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateRules_Failure tests the validate function for failure cases.
|
||||
func TestValidateRules_Failure(t *testing.T) {
|
||||
// TestValidateRulesFailure tests the validate function for failure cases.
|
||||
func TestValidateRulesFailure(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rules []routingRule
|
||||
@@ -190,13 +239,13 @@ func TestValidateRules_Failure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
expectedErr: "invalid rule: domain, version, and routing_type are required",
|
||||
expectedErr: "invalid rule: domain, version, and routingType are required",
|
||||
},
|
||||
{
|
||||
name: "Missing version",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Domain: "retail",
|
||||
RoutingType: "url",
|
||||
Target: target{
|
||||
URL: "https://example.com/api",
|
||||
@@ -204,13 +253,13 @@ func TestValidateRules_Failure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
expectedErr: "invalid rule: domain, version, and routing_type are required",
|
||||
expectedErr: "invalid rule: domain, version, and routingType are required",
|
||||
},
|
||||
{
|
||||
name: "Missing routing_type",
|
||||
name: "Missing routingType",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
Target: target{
|
||||
URL: "https://example.com/api",
|
||||
@@ -218,28 +267,28 @@ func TestValidateRules_Failure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
expectedErr: "invalid rule: domain, version, and routing_type are required",
|
||||
expectedErr: "invalid rule: domain, version, and routingType are required",
|
||||
},
|
||||
{
|
||||
name: "Invalid routing_type",
|
||||
name: "Invalid routingType",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "invalid_type",
|
||||
RoutingType: "invalid",
|
||||
Target: target{
|
||||
URL: "https://example.com/api",
|
||||
},
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
expectedErr: "invalid rule: unknown routing_type 'invalid_type'",
|
||||
expectedErr: "invalid rule: unknown routingType 'invalid'",
|
||||
},
|
||||
{
|
||||
name: "Missing url for routing_type: url",
|
||||
name: "Missing url for routingType: url",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "url",
|
||||
Target: target{
|
||||
@@ -248,13 +297,13 @@ func TestValidateRules_Failure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
expectedErr: "invalid rule: url is required for routing_type 'url'",
|
||||
expectedErr: "invalid rule: url is required for routingType 'url'",
|
||||
},
|
||||
{
|
||||
name: "Missing topic_id for routing_type: msgq",
|
||||
name: "Missing topic_id for routingType: msgq",
|
||||
rules: []routingRule{
|
||||
{
|
||||
Domain: "example.com",
|
||||
Domain: "retail",
|
||||
Version: "1.0.0",
|
||||
RoutingType: "msgq",
|
||||
Target: target{
|
||||
@@ -263,7 +312,7 @@ func TestValidateRules_Failure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
expectedErr: "invalid rule: topic_id is required for routing_type 'msgq'",
|
||||
expectedErr: "invalid rule: topicId is required for routingType 'msgq'",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -279,65 +328,63 @@ func TestValidateRules_Failure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestRoute tests the Route function.
|
||||
func TestRoute(t *testing.T) {
|
||||
// TestRouteSuccess tests the Route function for success cases.
|
||||
func TestRouteSuccess(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
rulesFilePath := setupTestConfig(t)
|
||||
defer os.RemoveAll(filepath.Dir(rulesFilePath))
|
||||
|
||||
config := &Config{
|
||||
RoutingConfig: rulesFilePath,
|
||||
}
|
||||
|
||||
router, closeFunc, err := New(ctx, config)
|
||||
if err != nil {
|
||||
t.Fatalf("New failed: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
if err := closeFunc(); err != nil {
|
||||
t.Errorf("closeFunc failed: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Define test cases
|
||||
// Define success test cases
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
body string
|
||||
expectedError string
|
||||
name string
|
||||
configFile string
|
||||
url string
|
||||
body string
|
||||
}{
|
||||
{
|
||||
name: "Valid domain, version, and endpoint",
|
||||
url: "https://example.com/v1/ondc/select",
|
||||
body: `{"context": {"domain": "ONDC:TRV11", "version": "2.0.0"}}`,
|
||||
name: "Valid domain, version, and endpoint (bpp routing with gateway URL)",
|
||||
configFile: "bap_caller.yaml",
|
||||
url: "https://example.com/v1/ondc/search",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
|
||||
},
|
||||
{
|
||||
name: "Unsupported endpoint",
|
||||
url: "https://example.com/v1/ondc/unsupported",
|
||||
body: `{"context": {"domain": "ONDC:TRV11", "version": "2.0.0"}}`,
|
||||
expectedError: "endpoint 'unsupported' is not supported for domain ONDC:TRV11 and version 2.0.0",
|
||||
name: "Valid domain, version, and endpoint (bpp routing with bpp_uri)",
|
||||
configFile: "bap_caller.yaml",
|
||||
url: "https://example.com/v1/ondc/select",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0", "bpp_uri": "https://bpp1.example.com"}}`,
|
||||
},
|
||||
{
|
||||
name: "No matching rule",
|
||||
url: "https://example.com/v1/ondc/select",
|
||||
body: `{"context": {"domain": "ONDC:SRV11", "version": "2.0.0"}}`,
|
||||
expectedError: "no matching routing rule found for domain ONDC:SRV11 and version 2.0.0",
|
||||
name: "Valid domain, version, and endpoint (url routing)",
|
||||
configFile: "bpp_receiver.yaml",
|
||||
url: "https://example.com/v1/ondc/select",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
|
||||
},
|
||||
{
|
||||
name: "Valid domain, version, and endpoint (msgq routing)",
|
||||
configFile: "bpp_receiver.yaml",
|
||||
url: "https://example.com/v1/ondc/search",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
|
||||
},
|
||||
{
|
||||
name: "Valid domain, version, and endpoint (bap routing with bap_uri)",
|
||||
configFile: "bpp_caller.yaml",
|
||||
url: "https://example.com/v1/ondc/on_select",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0", "bap_uri": "https://bap1.example.com"}}`,
|
||||
},
|
||||
{
|
||||
name: "Valid domain, version, and endpoint (bpp routing with bpp_uri)",
|
||||
configFile: "bap_receiver.yaml",
|
||||
url: "https://example.com/v1/ondc/on_select",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0", "bpp_uri": "https://bpp1.example.com"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
router, _, rulesFilePath := setupRouter(t, tt.configFile)
|
||||
defer os.RemoveAll(filepath.Dir(rulesFilePath))
|
||||
|
||||
parsedURL, _ := url.Parse(tt.url)
|
||||
_, err := router.Route(ctx, parsedURL, []byte(tt.body))
|
||||
|
||||
// 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)
|
||||
@@ -345,3 +392,61 @@ func TestRoute(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestRouteFailure tests the Route function for failure cases.
|
||||
func TestRouteFailure(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Define failure test cases
|
||||
tests := []struct {
|
||||
name string
|
||||
configFile string
|
||||
url string
|
||||
body string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "Unsupported endpoint",
|
||||
configFile: "bpp_receiver.yaml",
|
||||
url: "https://example.com/v1/ondc/unsupported",
|
||||
body: `{"context": {"domain": "ONDC:TRV11", "version": "2.0.0"}}`,
|
||||
expectedError: "endpoint 'unsupported' is not supported for domain ONDC:TRV11 and version 2.0.0",
|
||||
},
|
||||
{
|
||||
name: "No matching rule",
|
||||
configFile: "bpp_receiver.yaml",
|
||||
url: "https://example.com/v1/ondc/select",
|
||||
body: `{"context": {"domain": "ONDC:SRV11", "version": "2.0.0"}}`,
|
||||
expectedError: "no matching routing rule found for domain ONDC:SRV11 and version 2.0.0",
|
||||
},
|
||||
{
|
||||
name: "Missing bap_uri for bap routing",
|
||||
configFile: "bpp_caller.yaml",
|
||||
url: "https://example.com/v1/ondc/on_search",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
|
||||
expectedError: "no target URI or URL found for bap routing type and on_search endpoint",
|
||||
},
|
||||
{
|
||||
name: "Missing bpp_uri for bpp routing",
|
||||
configFile: "bap_caller.yaml",
|
||||
url: "https://example.com/v1/ondc/select",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
|
||||
expectedError: "no target URI or URL found for bpp routing type and select endpoint",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
router, _, rulesFilePath := setupRouter(t, tt.configFile)
|
||||
defer os.RemoveAll(filepath.Dir(rulesFilePath))
|
||||
|
||||
parsedURL, _ := url.Parse(tt.url)
|
||||
_, err := router.Route(ctx, parsedURL, []byte(tt.body))
|
||||
|
||||
// Check for expected error
|
||||
if err == nil || !strings.Contains(err.Error(), tt.expectedError) {
|
||||
t.Errorf("expected error %q, got %v", tt.expectedError, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user