- Created `qa-test-output-after-fix.txt` and `qa-test-output.txt` to log results of certificate page authentication tests. - Added `build.sh` for deterministic backend builds in CI, utilizing `go list` for efficiency. - Introduced `codeql_scan.sh` for CodeQL database creation and analysis for Go and JavaScript/TypeScript. - Implemented `dockerfile_check.sh` to validate Dockerfiles for base image and package manager mismatches. - Added `sourcery_precommit_wrapper.sh` to facilitate Sourcery CLI usage in pre-commit hooks.
307 lines
7.5 KiB
Go
307 lines
7.5 KiB
Go
package caddy
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestNewImporter(t *testing.T) {
|
|
importer := NewImporter("/usr/bin/caddy")
|
|
assert.NotNil(t, importer)
|
|
assert.Equal(t, "/usr/bin/caddy", importer.caddyBinaryPath)
|
|
|
|
importerDefault := NewImporter("")
|
|
assert.NotNil(t, importerDefault)
|
|
assert.Equal(t, "caddy", importerDefault.caddyBinaryPath)
|
|
}
|
|
|
|
func TestImporter_ParseCaddyfile_NotFound(t *testing.T) {
|
|
importer := NewImporter("caddy")
|
|
_, err := importer.ParseCaddyfile("non-existent-file")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "caddyfile not found")
|
|
}
|
|
|
|
type MockExecutor struct {
|
|
Output []byte
|
|
Err error
|
|
}
|
|
|
|
func (m *MockExecutor) Execute(name string, args ...string) ([]byte, error) {
|
|
return m.Output, m.Err
|
|
}
|
|
|
|
func TestImporter_ParseCaddyfile_Success(t *testing.T) {
|
|
importer := NewImporter("caddy")
|
|
mockExecutor := &MockExecutor{
|
|
Output: []byte(`{"apps": {"http": {"servers": {}}}}`),
|
|
Err: nil,
|
|
}
|
|
importer.executor = mockExecutor
|
|
|
|
// Create a dummy file to bypass os.Stat check
|
|
tmpFile := filepath.Join(t.TempDir(), "Caddyfile")
|
|
err := os.WriteFile(tmpFile, []byte("foo"), 0o644)
|
|
assert.NoError(t, err)
|
|
|
|
output, err := importer.ParseCaddyfile(tmpFile)
|
|
assert.NoError(t, err)
|
|
assert.JSONEq(t, `{"apps": {"http": {"servers": {}}}}`, string(output))
|
|
}
|
|
|
|
func TestImporter_ParseCaddyfile_Failure(t *testing.T) {
|
|
importer := NewImporter("caddy")
|
|
mockExecutor := &MockExecutor{
|
|
Output: []byte("syntax error"),
|
|
Err: assert.AnError,
|
|
}
|
|
importer.executor = mockExecutor
|
|
|
|
// Create a dummy file
|
|
tmpFile := filepath.Join(t.TempDir(), "Caddyfile")
|
|
err := os.WriteFile(tmpFile, []byte("foo"), 0o644)
|
|
assert.NoError(t, err)
|
|
|
|
_, err = importer.ParseCaddyfile(tmpFile)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "caddy adapt failed")
|
|
}
|
|
|
|
func TestImporter_ExtractHosts(t *testing.T) {
|
|
importer := NewImporter("caddy")
|
|
|
|
// Test Case 1: Empty Config
|
|
emptyJSON := []byte(`{}`)
|
|
result, err := importer.ExtractHosts(emptyJSON)
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, result.Hosts)
|
|
|
|
// Test Case 2: Invalid JSON
|
|
invalidJSON := []byte(`{invalid`)
|
|
_, err = importer.ExtractHosts(invalidJSON)
|
|
assert.Error(t, err)
|
|
|
|
// Test Case 3: Valid Config with Reverse Proxy
|
|
validJSON := []byte(`{
|
|
"apps": {
|
|
"http": {
|
|
"servers": {
|
|
"srv0": {
|
|
"routes": [
|
|
{
|
|
"match": [{"host": ["example.com"]}],
|
|
"handle": [
|
|
{
|
|
"handler": "reverse_proxy",
|
|
"upstreams": [{"dial": "127.0.0.1:8080"}]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`)
|
|
result, err = importer.ExtractHosts(validJSON)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Hosts, 1)
|
|
assert.Equal(t, "example.com", result.Hosts[0].DomainNames)
|
|
assert.Equal(t, "127.0.0.1", result.Hosts[0].ForwardHost)
|
|
assert.Equal(t, 8080, result.Hosts[0].ForwardPort)
|
|
|
|
// Test Case 4: Duplicate Domain
|
|
duplicateJSON := []byte(`{
|
|
"apps": {
|
|
"http": {
|
|
"servers": {
|
|
"srv0": {
|
|
"routes": [
|
|
{
|
|
"match": [{"host": ["example.com"]}],
|
|
"handle": [{"handler": "reverse_proxy"}]
|
|
},
|
|
{
|
|
"match": [{"host": ["example.com"]}],
|
|
"handle": [{"handler": "reverse_proxy"}]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`)
|
|
result, err = importer.ExtractHosts(duplicateJSON)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Hosts, 1)
|
|
assert.Len(t, result.Conflicts, 1)
|
|
assert.Equal(t, "example.com", result.Conflicts[0])
|
|
|
|
// Test Case 5: Unsupported Features
|
|
unsupportedJSON := []byte(`{
|
|
"apps": {
|
|
"http": {
|
|
"servers": {
|
|
"srv0": {
|
|
"routes": [
|
|
{
|
|
"match": [{"host": ["files.example.com"]}],
|
|
"handle": [
|
|
{"handler": "file_server"},
|
|
{"handler": "rewrite"}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`)
|
|
result, err = importer.ExtractHosts(unsupportedJSON)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Hosts, 1)
|
|
assert.Len(t, result.Hosts[0].Warnings, 2)
|
|
assert.Contains(t, result.Hosts[0].Warnings, "File server directives not supported")
|
|
assert.Contains(t, result.Hosts[0].Warnings, "Rewrite rules not supported - manual configuration required")
|
|
|
|
// Test Case 6: SSL Detection via Listen Address (:443)
|
|
sslViaListenJSON := []byte(`{
|
|
"apps": {
|
|
"http": {
|
|
"servers": {
|
|
"srv0": {
|
|
"listen": [":443"],
|
|
"routes": [
|
|
{
|
|
"match": [{"host": ["secure.example.com"]}],
|
|
"handle": [
|
|
{
|
|
"handler": "reverse_proxy",
|
|
"upstreams": [{"dial": "127.0.0.1:9000"}]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`)
|
|
result, err = importer.ExtractHosts(sslViaListenJSON)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Hosts, 1)
|
|
assert.Equal(t, "secure.example.com", result.Hosts[0].DomainNames)
|
|
assert.True(t, result.Hosts[0].SSLForced, "SSLForced should be true when server listens on :443")
|
|
}
|
|
|
|
func TestImporter_ImportFile(t *testing.T) {
|
|
importer := NewImporter("caddy")
|
|
mockExecutor := &MockExecutor{
|
|
Output: []byte(`{
|
|
"apps": {
|
|
"http": {
|
|
"servers": {
|
|
"srv0": {
|
|
"routes": [
|
|
{
|
|
"match": [{"host": ["example.com"]}],
|
|
"handle": [
|
|
{
|
|
"handler": "reverse_proxy",
|
|
"upstreams": [{"dial": "127.0.0.1:8080"}]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}`),
|
|
Err: nil,
|
|
}
|
|
importer.executor = mockExecutor
|
|
|
|
// Create a dummy file
|
|
tmpFile := filepath.Join(t.TempDir(), "Caddyfile")
|
|
err := os.WriteFile(tmpFile, []byte("foo"), 0o644)
|
|
assert.NoError(t, err)
|
|
|
|
result, err := importer.ImportFile(tmpFile)
|
|
assert.NoError(t, err)
|
|
assert.Len(t, result.Hosts, 1)
|
|
assert.Equal(t, "example.com", result.Hosts[0].DomainNames)
|
|
}
|
|
|
|
func TestConvertToProxyHosts(t *testing.T) {
|
|
parsedHosts := []ParsedHost{
|
|
{
|
|
DomainNames: "example.com",
|
|
ForwardScheme: "http",
|
|
ForwardHost: "127.0.0.1",
|
|
ForwardPort: 8080,
|
|
SSLForced: true,
|
|
WebsocketSupport: true,
|
|
},
|
|
{
|
|
DomainNames: "invalid.com",
|
|
ForwardHost: "", // Invalid
|
|
},
|
|
}
|
|
|
|
hosts := ConvertToProxyHosts(parsedHosts)
|
|
assert.Len(t, hosts, 1)
|
|
assert.Equal(t, "example.com", hosts[0].DomainNames)
|
|
assert.Equal(t, "127.0.0.1", hosts[0].ForwardHost)
|
|
assert.Equal(t, 8080, hosts[0].ForwardPort)
|
|
assert.True(t, hosts[0].SSLForced)
|
|
assert.True(t, hosts[0].WebsocketSupport)
|
|
}
|
|
|
|
func TestImporter_ValidateCaddyBinary(t *testing.T) {
|
|
importer := NewImporter("caddy")
|
|
|
|
// Success
|
|
importer.executor = &MockExecutor{Output: []byte("v2.0.0"), Err: nil}
|
|
err := importer.ValidateCaddyBinary()
|
|
assert.NoError(t, err)
|
|
|
|
// Failure
|
|
importer.executor = &MockExecutor{Output: nil, Err: assert.AnError}
|
|
err = importer.ValidateCaddyBinary()
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "caddy binary not found or not executable", err.Error())
|
|
}
|
|
|
|
func TestBackupCaddyfile(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
originalFile := filepath.Join(tmpDir, "Caddyfile")
|
|
err := os.WriteFile(originalFile, []byte("original content"), 0o644)
|
|
assert.NoError(t, err)
|
|
|
|
backupDir := filepath.Join(tmpDir, "backups")
|
|
|
|
// Success
|
|
backupPath, err := BackupCaddyfile(originalFile, backupDir)
|
|
assert.NoError(t, err)
|
|
assert.FileExists(t, backupPath)
|
|
|
|
content, err := os.ReadFile(backupPath)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "original content", string(content))
|
|
|
|
// Failure - Source not found
|
|
_, err = BackupCaddyfile("non-existent", backupDir)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestDefaultExecutor_Execute(t *testing.T) {
|
|
executor := &DefaultExecutor{}
|
|
output, err := executor.Execute("echo", "hello")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "hello\n", string(output))
|
|
}
|