package compose import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCaddyExtension(t *testing.T) { tests := []struct { name string composeYAML string wantConfig string wantErr string }{ { name: "x-caddy as string", composeYAML: ` services: web: image: nginx x-caddy: | example.com { reverse_proxy web:80 } `, wantConfig: `example.com { reverse_proxy web:80 } `, }, { name: "x-caddy as object with config field", composeYAML: ` services: web: image: nginx x-caddy: config: | example.com { reverse_proxy web:80 } `, wantConfig: `example.com { reverse_proxy web:80 } `, }, { name: "x-caddy with path to Caddyfile", composeYAML: ` services: web: image: nginx x-caddy: testdata/Caddyfile `, wantConfig: `test.example.com { reverse_proxy test:8000 } `, }, { name: "x-caddy with empty object", composeYAML: ` services: web: image: nginx x-caddy: {} `, wantConfig: "", }, { name: "x-caddy with empty string", composeYAML: ` services: web: image: nginx x-caddy: "" `, wantConfig: "", }, { name: "x-caddy with extra unknown field should fail", composeYAML: ` services: web: image: nginx x-caddy: config: | example.com { reverse_proxy web:80 } unknown_field: "should cause error" `, wantErr: "invalid keys: unknown_field", }, { name: "x-caddy with non-string config field should fail", composeYAML: ` services: web: image: nginx x-caddy: config: 123 `, wantErr: "expected type 'string'", }, { name: "x-caddy with x-ports conflict", composeYAML: ` services: web: image: nginx x-caddy: | example.com { reverse_proxy web:80 } x-ports: - example.com:80/http `, wantErr: "cannot specify both 'x-caddy' and 'x-ports'", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { project, err := loadProjectFromContent(t, tt.composeYAML) if tt.wantErr != "" { require.ErrorContains(t, err, tt.wantErr) return } require.NoError(t, err) service, err := project.GetService("web") require.NoError(t, err) // Verify the x-caddy extension was parsed correctly. caddyExt, ok := service.Extensions[CaddyExtensionKey] require.True(t, ok, "x-caddy extension not found") caddy, ok := caddyExt.(Caddy) require.True(t, ok, "x-caddy extension is not Caddy type") assert.Equal(t, tt.wantConfig, caddy.Config) }) } } func TestIsCaddyfilePath(t *testing.T) { tests := []struct { name string input string want bool }{ // Should be detected as file paths. {"relative path with slash", "./Caddyfile", true}, {"relative path parent", "../Caddyfile", true}, {"relative path", "relative/path/to/file", true}, {"absolute path", "/etc/caddy/Caddyfile", true}, {"just Caddyfile", "Caddyfile", true}, {"Caddyfile with suffix", "Caddyfile.app", true}, {"caddyfile lowercase", "caddyfile", true}, {"with .caddyfile extension", "my.caddyfile", true}, {"with .Caddyfile extension", "my.Caddyfile", true}, {"with .caddy extension", "config.caddy", true}, {"with .conf extension", "caddy.conf", true}, {"simple filename", "config", true}, // Should NOT be detected as file paths. {"multiline config", "example.com {\n reverse_proxy :8080\n}", false}, {"empty string", "", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := isCaddyfilePath(tt.input) assert.Equal(t, tt.want, result, "isCaddyfilePath(%q) should be %v", tt.input, tt.want) }) } }