2025-09-29 17:24:59 +02:00
|
|
|
// ABOUTME: Comprehensive tests for EdgeConnect configuration parser with validation scenarios
|
|
|
|
|
// ABOUTME: Tests all validation rules, error conditions, and successful parsing cases
|
|
|
|
|
package config
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestNewParser(t *testing.T) {
|
|
|
|
|
parser := NewParser()
|
|
|
|
|
assert.NotNil(t, parser)
|
|
|
|
|
assert.IsType(t, &ConfigParser{}, parser)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfigParser_ParseBytes(t *testing.T) {
|
|
|
|
|
parser := NewParser()
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
yaml string
|
|
|
|
|
wantErr bool
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "valid k8s config",
|
|
|
|
|
yaml: `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
k8sApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
manifestFile: "./test-manifest.yaml"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`,
|
|
|
|
|
wantErr: true, // Will fail because manifest file doesn't exist
|
|
|
|
|
errMsg: "manifestFile does not exist",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "valid docker config",
|
|
|
|
|
yaml: `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
dockerApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
image: "nginx:latest"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`,
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "missing kind",
|
|
|
|
|
yaml: `
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
k8sApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
manifestFile: "./test-manifest.yaml"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`,
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "kind is required",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid kind",
|
|
|
|
|
yaml: `
|
|
|
|
|
kind: invalid-kind
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
dockerApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
image: "nginx:latest"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`,
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "unsupported kind: invalid-kind",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "missing app definition",
|
|
|
|
|
yaml: `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`,
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "spec must define either k8sApp or dockerApp",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "both k8s and docker apps",
|
|
|
|
|
yaml: `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
k8sApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
manifestFile: "./test-manifest.yaml"
|
|
|
|
|
dockerApp:
|
|
|
|
|
appName: "test-app"
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
image: "nginx:latest"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`,
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "spec cannot define both k8sApp and dockerApp",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "empty infrastructure template",
|
|
|
|
|
yaml: `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
dockerApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
image: "nginx:latest"
|
|
|
|
|
infraTemplate: []
|
|
|
|
|
`,
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "infraTemplate is required and must contain at least one target",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "with network config",
|
|
|
|
|
yaml: `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
dockerApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
image: "nginx:latest"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
network:
|
|
|
|
|
outboundConnections:
|
|
|
|
|
- protocol: "tcp"
|
|
|
|
|
portRangeMin: 80
|
|
|
|
|
portRangeMax: 80
|
|
|
|
|
remoteCIDR: "0.0.0.0/0"
|
|
|
|
|
`,
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "empty data",
|
|
|
|
|
yaml: "",
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "configuration data cannot be empty",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
config, err := parser.ParseBytes([]byte(tt.yaml))
|
|
|
|
|
|
|
|
|
|
if tt.wantErr {
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
if tt.errMsg != "" {
|
|
|
|
|
assert.Contains(t, err.Error(), tt.errMsg)
|
|
|
|
|
}
|
|
|
|
|
assert.Nil(t, config)
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
assert.NotNil(t, config)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfigParser_ParseFile(t *testing.T) {
|
|
|
|
|
parser := NewParser()
|
|
|
|
|
|
|
|
|
|
// Create temporary directory for test files
|
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
// Create a valid config file
|
|
|
|
|
validConfig := `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
dockerApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
image: "nginx:latest"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
validFile := filepath.Join(tempDir, "valid.yaml")
|
|
|
|
|
err := os.WriteFile(validFile, []byte(validConfig), 0644)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Test valid file parsing
|
|
|
|
|
config, err := parser.ParseFile(validFile)
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
assert.NotNil(t, config)
|
|
|
|
|
assert.Equal(t, "edgeconnect-deployment", config.Kind)
|
|
|
|
|
assert.Equal(t, "test-app", config.Metadata.Name)
|
|
|
|
|
|
|
|
|
|
// Test non-existent file
|
|
|
|
|
nonExistentFile := filepath.Join(tempDir, "nonexistent.yaml")
|
|
|
|
|
config, err = parser.ParseFile(nonExistentFile)
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
assert.Contains(t, err.Error(), "does not exist")
|
|
|
|
|
assert.Nil(t, config)
|
|
|
|
|
|
|
|
|
|
// Test empty filename
|
|
|
|
|
config, err = parser.ParseFile("")
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
assert.Contains(t, err.Error(), "filename cannot be empty")
|
|
|
|
|
assert.Nil(t, config)
|
|
|
|
|
|
|
|
|
|
// Test invalid YAML
|
|
|
|
|
invalidFile := filepath.Join(tempDir, "invalid.yaml")
|
|
|
|
|
err = os.WriteFile(invalidFile, []byte("invalid: yaml: content: ["), 0644)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
config, err = parser.ParseFile(invalidFile)
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
assert.Contains(t, err.Error(), "YAML parsing failed")
|
|
|
|
|
assert.Nil(t, config)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfigParser_RelativePathResolution(t *testing.T) {
|
|
|
|
|
parser := NewParser()
|
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
// Create a manifest file
|
|
|
|
|
manifestContent := "apiVersion: v1\nkind: Pod\nmetadata:\n name: test\n"
|
|
|
|
|
manifestFile := filepath.Join(tempDir, "manifest.yaml")
|
|
|
|
|
err := os.WriteFile(manifestFile, []byte(manifestContent), 0644)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// Create config with relative path
|
|
|
|
|
configContent := `
|
|
|
|
|
kind: edgeconnect-deployment
|
|
|
|
|
metadata:
|
|
|
|
|
name: "test-app"
|
|
|
|
|
spec:
|
|
|
|
|
k8sApp:
|
|
|
|
|
appVersion: "1.0.0"
|
|
|
|
|
manifestFile: "./manifest.yaml"
|
|
|
|
|
infraTemplate:
|
|
|
|
|
- organization: "testorg"
|
|
|
|
|
region: "US"
|
|
|
|
|
cloudletOrg: "TestOP"
|
|
|
|
|
cloudletName: "TestCloudlet"
|
|
|
|
|
flavorName: "small"
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
configFile := filepath.Join(tempDir, "config.yaml")
|
|
|
|
|
err = os.WriteFile(configFile, []byte(configContent), 0644)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
config, err := parser.ParseFile(configFile)
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
assert.NotNil(t, config)
|
|
|
|
|
|
|
|
|
|
// Check that relative path was resolved to absolute
|
|
|
|
|
expectedPath := filepath.Join(tempDir, "manifest.yaml")
|
|
|
|
|
assert.Equal(t, expectedPath, config.Spec.K8sApp.ManifestFile)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestEdgeConnectConfig_Validate(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
config EdgeConnectConfig
|
|
|
|
|
wantErr bool
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "valid config",
|
|
|
|
|
config: EdgeConnectConfig{
|
|
|
|
|
Kind: "edgeconnect-deployment",
|
|
|
|
|
Metadata: Metadata{
|
|
|
|
|
Name: "test-app",
|
|
|
|
|
},
|
|
|
|
|
Spec: Spec{
|
|
|
|
|
DockerApp: &DockerApp{
|
|
|
|
|
AppVersion: "1.0.0",
|
|
|
|
|
Image: "nginx:latest",
|
|
|
|
|
},
|
|
|
|
|
InfraTemplate: []InfraTemplate{
|
|
|
|
|
{
|
|
|
|
|
Organization: "testorg",
|
|
|
|
|
Region: "US",
|
|
|
|
|
CloudletOrg: "TestOP",
|
|
|
|
|
CloudletName: "TestCloudlet",
|
|
|
|
|
FlavorName: "small",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "missing kind",
|
|
|
|
|
config: EdgeConnectConfig{
|
|
|
|
|
Metadata: Metadata{Name: "test"},
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "kind is required",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid kind",
|
|
|
|
|
config: EdgeConnectConfig{
|
|
|
|
|
Kind: "invalid",
|
|
|
|
|
Metadata: Metadata{Name: "test"},
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "unsupported kind",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
err := tt.config.Validate()
|
|
|
|
|
|
|
|
|
|
if tt.wantErr {
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
if tt.errMsg != "" {
|
|
|
|
|
assert.Contains(t, err.Error(), tt.errMsg)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestMetadata_Validate(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
metadata Metadata
|
|
|
|
|
wantErr bool
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "valid metadata",
|
|
|
|
|
metadata: Metadata{Name: "test-app"},
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "empty name",
|
|
|
|
|
metadata: Metadata{Name: ""},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "metadata.name is required",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "name with leading whitespace",
|
|
|
|
|
metadata: Metadata{Name: " test-app"},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "cannot have leading/trailing whitespace",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "name with trailing whitespace",
|
|
|
|
|
metadata: Metadata{Name: "test-app "},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "cannot have leading/trailing whitespace",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
err := tt.metadata.Validate()
|
|
|
|
|
|
|
|
|
|
if tt.wantErr {
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
if tt.errMsg != "" {
|
|
|
|
|
assert.Contains(t, err.Error(), tt.errMsg)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestOutboundConnection_Validate(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
connection OutboundConnection
|
|
|
|
|
wantErr bool
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "valid connection",
|
|
|
|
|
connection: OutboundConnection{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 80,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "missing protocol",
|
|
|
|
|
connection: OutboundConnection{
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 80,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "protocol is required",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid protocol",
|
|
|
|
|
connection: OutboundConnection{
|
|
|
|
|
Protocol: "invalid",
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 80,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "protocol must be one of: tcp, udp, icmp",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid port range min",
|
|
|
|
|
connection: OutboundConnection{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 0,
|
|
|
|
|
PortRangeMax: 80,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "portRangeMin must be between 1 and 65535",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid port range max",
|
|
|
|
|
connection: OutboundConnection{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 65536,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "portRangeMax must be between 1 and 65535",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "min greater than max",
|
|
|
|
|
connection: OutboundConnection{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 443,
|
|
|
|
|
PortRangeMax: 80,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "portRangeMin (443) cannot be greater than portRangeMax (80)",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "missing remote CIDR",
|
|
|
|
|
connection: OutboundConnection{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 80,
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "remoteCIDR is required",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
err := tt.connection.Validate()
|
|
|
|
|
|
|
|
|
|
if tt.wantErr {
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
if tt.errMsg != "" {
|
|
|
|
|
assert.Contains(t, err.Error(), tt.errMsg)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfigParser_ValidateInfrastructureUniqueness(t *testing.T) {
|
|
|
|
|
parser := &ConfigParser{}
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
config *EdgeConnectConfig
|
|
|
|
|
wantErr bool
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "unique infrastructure",
|
|
|
|
|
config: &EdgeConnectConfig{
|
|
|
|
|
Spec: Spec{
|
|
|
|
|
InfraTemplate: []InfraTemplate{
|
|
|
|
|
{
|
|
|
|
|
Organization: "org1",
|
|
|
|
|
Region: "US",
|
|
|
|
|
CloudletOrg: "cloudlet1",
|
|
|
|
|
CloudletName: "name1",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Organization: "org1",
|
|
|
|
|
Region: "EU",
|
|
|
|
|
CloudletOrg: "cloudlet1",
|
|
|
|
|
CloudletName: "name1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "duplicate infrastructure",
|
|
|
|
|
config: &EdgeConnectConfig{
|
|
|
|
|
Spec: Spec{
|
|
|
|
|
InfraTemplate: []InfraTemplate{
|
|
|
|
|
{
|
|
|
|
|
Organization: "org1",
|
|
|
|
|
Region: "US",
|
|
|
|
|
CloudletOrg: "cloudlet1",
|
|
|
|
|
CloudletName: "name1",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Organization: "org1",
|
|
|
|
|
Region: "US",
|
|
|
|
|
CloudletOrg: "cloudlet1",
|
|
|
|
|
CloudletName: "name1",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "duplicate infrastructure target",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
err := parser.ValidateInfrastructureUniqueness(tt.config)
|
|
|
|
|
|
|
|
|
|
if tt.wantErr {
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
if tt.errMsg != "" {
|
|
|
|
|
assert.Contains(t, err.Error(), tt.errMsg)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConfigParser_ValidatePortRanges(t *testing.T) {
|
|
|
|
|
parser := &ConfigParser{}
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
config *EdgeConnectConfig
|
|
|
|
|
wantErr bool
|
|
|
|
|
errMsg string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "no network config",
|
|
|
|
|
config: &EdgeConnectConfig{
|
|
|
|
|
Spec: Spec{
|
|
|
|
|
Network: nil,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "non-overlapping ports",
|
|
|
|
|
config: &EdgeConnectConfig{
|
|
|
|
|
Spec: Spec{
|
|
|
|
|
Network: &NetworkConfig{
|
|
|
|
|
OutboundConnections: []OutboundConnection{
|
|
|
|
|
{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 80,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 443,
|
|
|
|
|
PortRangeMax: 443,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
wantErr: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "overlapping ports same protocol and CIDR",
|
|
|
|
|
config: &EdgeConnectConfig{
|
|
|
|
|
Spec: Spec{
|
|
|
|
|
Network: &NetworkConfig{
|
|
|
|
|
OutboundConnections: []OutboundConnection{
|
|
|
|
|
{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 90,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 85,
|
|
|
|
|
PortRangeMax: 95,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
wantErr: true,
|
|
|
|
|
errMsg: "overlapping port ranges",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "overlapping ports different protocol",
|
|
|
|
|
config: &EdgeConnectConfig{
|
|
|
|
|
Spec: Spec{
|
|
|
|
|
Network: &NetworkConfig{
|
|
|
|
|
OutboundConnections: []OutboundConnection{
|
|
|
|
|
{
|
|
|
|
|
Protocol: "tcp",
|
|
|
|
|
PortRangeMin: 80,
|
|
|
|
|
PortRangeMax: 90,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
Protocol: "udp",
|
|
|
|
|
PortRangeMin: 85,
|
|
|
|
|
PortRangeMax: 95,
|
|
|
|
|
RemoteCIDR: "0.0.0.0/0",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
wantErr: false, // Different protocols can overlap
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
err := parser.ValidatePortRanges(tt.config)
|
|
|
|
|
|
|
|
|
|
if tt.wantErr {
|
|
|
|
|
assert.Error(t, err)
|
|
|
|
|
if tt.errMsg != "" {
|
|
|
|
|
assert.Contains(t, err.Error(), tt.errMsg)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestGetInstanceName(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
appName string
|
|
|
|
|
appVersion string
|
|
|
|
|
expected string
|
|
|
|
|
}{
|
|
|
|
|
{"myapp", "1.0.0", "myapp-1.0.0-instance"},
|
|
|
|
|
{"test-app", "v2.1", "test-app-v2.1-instance"},
|
|
|
|
|
{"app", "latest", "app-latest-instance"},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.appName+"-"+tt.appVersion, func(t *testing.T) {
|
|
|
|
|
result := GetInstanceName(tt.appName, tt.appVersion)
|
|
|
|
|
assert.Equal(t, tt.expected, result)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSpec_GetMethods(t *testing.T) {
|
|
|
|
|
k8sSpec := &Spec{
|
|
|
|
|
K8sApp: &K8sApp{
|
|
|
|
|
AppVersion: "1.0.0",
|
|
|
|
|
ManifestFile: "k8s.yaml",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dockerSpec := &Spec{
|
|
|
|
|
DockerApp: &DockerApp{
|
|
|
|
|
AppVersion: "2.0.0",
|
|
|
|
|
ManifestFile: "docker.yaml",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "1.0.0", k8sSpec.GetAppVersion())
|
|
|
|
|
assert.Equal(t, "k8s.yaml", k8sSpec.GetManifestFile())
|
|
|
|
|
assert.True(t, k8sSpec.IsK8sApp())
|
|
|
|
|
assert.False(t, k8sSpec.IsDockerApp())
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, "2.0.0", dockerSpec.GetAppVersion())
|
|
|
|
|
assert.Equal(t, "docker.yaml", dockerSpec.GetManifestFile())
|
|
|
|
|
assert.False(t, dockerSpec.IsK8sApp())
|
|
|
|
|
assert.True(t, dockerSpec.IsDockerApp())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPortRangesOverlap(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
min1 int
|
|
|
|
|
max1 int
|
|
|
|
|
min2 int
|
|
|
|
|
max2 int
|
|
|
|
|
expected bool
|
|
|
|
|
}{
|
|
|
|
|
{"no overlap", 10, 20, 30, 40, false},
|
|
|
|
|
{"overlap", 10, 20, 15, 25, true},
|
|
|
|
|
{"adjacent", 10, 20, 21, 30, false},
|
|
|
|
|
{"touching", 10, 20, 20, 30, true},
|
|
|
|
|
{"contained", 10, 30, 15, 25, true},
|
|
|
|
|
{"same range", 10, 20, 10, 20, true},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
result := portRangesOverlap(tt.min1, tt.max1, tt.min2, tt.max2)
|
|
|
|
|
assert.Equal(t, tt.expected, result)
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-09-30 11:33:52 +02:00
|
|
|
}
|