package domain import ( "encoding/base64" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSanitizeDomainForEnvFile(t *testing.T) { tests := []struct { name string domain string want string wantErr bool }{ {name: "simple domain", domain: "example.com", want: base64.RawURLEncoding.EncodeToString([]byte("example.com"))}, {name: "subdomain", domain: "app.example.com", want: base64.RawURLEncoding.EncodeToString([]byte("app.example.com"))}, {name: "domain with port", domain: "example.com:8080", want: base64.RawURLEncoding.EncodeToString([]byte("example.com:8080"))}, {name: "domain with path", domain: "example.com/path", want: base64.RawURLEncoding.EncodeToString([]byte("example.com/path"))}, {name: "single char", domain: "a", want: base64.RawURLEncoding.EncodeToString([]byte("a"))}, {name: "hyphenated", domain: "my-app.example.com", want: base64.RawURLEncoding.EncodeToString([]byte("my-app.example.com"))}, {name: "empty", domain: "", wantErr: true}, {name: "path traversal", domain: "../etc/passwd", wantErr: true}, {name: "double dots", domain: "foo..bar", wantErr: true}, {name: "starts with dot", domain: ".hidden", wantErr: true}, {name: "ends with dot", domain: "trailing.", wantErr: true}, {name: "space", domain: "has space", wantErr: true}, {name: "special chars", domain: "bad$domain", wantErr: true}, {name: "underscore rejected as invalid domain character", domain: "example_com", wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := SanitizeDomainForEnvFile(tt.domain) if tt.wantErr { assert.Error(t, err) return } require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } func TestSanitizeDomainForEnvFile_DistinguishesSeparators(t *testing.T) { gotDot, err := SanitizeDomainForEnvFile("app.example.com") require.NoError(t, err) gotPort, err := SanitizeDomainForEnvFile("app:example:com") require.NoError(t, err) gotPath, err := SanitizeDomainForEnvFile("app/example/com") require.NoError(t, err) assert.NotEqual(t, gotDot, gotPort) assert.NotEqual(t, gotDot, gotPath) assert.NotEqual(t, gotPort, gotPath) } func TestParseEnvData(t *testing.T) { tests := []struct { name string data string want map[string]string wantErr bool }{ { name: "simple key-value", data: "FOO=bar\nBAZ=qux", want: map[string]string{"FOO": "bar", "BAZ": "qux"}, }, { name: "double-quoted value", data: `KEY="hello world"`, want: map[string]string{"KEY": "hello world"}, }, { name: "single-quoted value", data: `KEY='hello world'`, want: map[string]string{"KEY": "hello world"}, }, { name: "comment lines", data: "# this is a comment\nKEY=val", want: map[string]string{"KEY": "val"}, }, { name: "empty lines", data: "\n\nKEY=val\n\n", want: map[string]string{"KEY": "val"}, }, { name: "value with equals", data: "URL=postgres://host:5432/db?opt=1", want: map[string]string{"URL": "postgres://host:5432/db?opt=1"}, }, { name: "no value", data: "NOVALUE", want: map[string]string{}, }, { name: "empty value", data: "KEY=", want: map[string]string{"KEY": ""}, }, { name: "empty input", data: "", want: map[string]string{}, }, { name: "large value within buffer", data: "BIG=" + strings.Repeat("x", 100000), want: map[string]string{"BIG": strings.Repeat("x", 100000)}, }, { name: "quoted value with inner spaces", data: `KEY=" hello "`, want: map[string]string{"KEY": " hello "}, }, { name: "unquoted value with outer spaces", data: "KEY= hello ", want: map[string]string{"KEY": "hello"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseEnvData([]byte(tt.data)) if tt.wantErr { assert.Error(t, err) return } require.NoError(t, err) assert.Equal(t, tt.want, got) }) } } func TestValidateEnvKey(t *testing.T) { tests := []struct { name string key string wantErr bool wantErrType error }{ {name: "simple", key: "FOO", wantErr: false}, {name: "with underscore", key: "FOO_BAR", wantErr: false}, {name: "starts with underscore", key: "_PRIVATE", wantErr: false}, {name: "lowercase", key: "foo", wantErr: false}, {name: "mixed", key: "myApp_v2", wantErr: false}, {name: "empty", key: "", wantErr: true, wantErrType: ErrInvalidEnvKey}, {name: "starts with number", key: "1BAD", wantErr: true, wantErrType: ErrInvalidEnvKey}, {name: "contains dot", key: "FOO.BAR", wantErr: true, wantErrType: ErrInvalidEnvKey}, {name: "contains slash", key: "FOO/BAR", wantErr: true, wantErrType: ErrPathTraversal}, {name: "contains backslash", key: `FOO\BAR`, wantErr: true, wantErrType: ErrPathTraversal}, {name: "path traversal", key: "..", wantErr: true, wantErrType: ErrPathTraversal}, {name: "contains hyphen", key: "FOO-BAR", wantErr: true, wantErrType: ErrInvalidEnvKey}, {name: "contains space", key: "FOO BAR", wantErr: true, wantErrType: ErrInvalidEnvKey}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateEnvKey(tt.key) if tt.wantErr { require.Error(t, err) if tt.wantErrType != nil { assert.ErrorIs(t, err, tt.wantErrType) } } else { assert.NoError(t, err) } }) } } func TestValidateContainerName(t *testing.T) { tests := []struct { name string container string wantErr bool wantErrType error }{ {name: "simple", container: "postgres", wantErr: false}, {name: "with hyphen", container: "gitea-postgres", wantErr: false}, {name: "with underscore", container: "gitea_postgres", wantErr: false}, {name: "complex real container", container: "gordon-git-example-com-gitea-postgres", wantErr: false}, {name: "starts with letter", container: "a-b", wantErr: false}, {name: "lowercase", container: "my-container", wantErr: false}, {name: "mixed case", container: "MyContainer-123", wantErr: false}, {name: "empty", container: "", wantErr: true, wantErrType: ErrInvalidContainerName}, {name: "starts with number", container: "1container", wantErr: true, wantErrType: ErrInvalidContainerName}, {name: "starts with hyphen", container: "-container", wantErr: true, wantErrType: ErrInvalidContainerName}, {name: "starts with underscore", container: "_container", wantErr: true, wantErrType: ErrInvalidContainerName}, {name: "contains slash", container: "container/name", wantErr: true, wantErrType: ErrPathTraversal}, {name: "contains backslash", container: `container\name`, wantErr: true, wantErrType: ErrPathTraversal}, {name: "path traversal", container: "..", wantErr: true, wantErrType: ErrPathTraversal}, {name: "contains dot", container: "container.name", wantErr: true, wantErrType: ErrInvalidContainerName}, {name: "contains space", container: "container name", wantErr: true, wantErrType: ErrInvalidContainerName}, {name: "contains special char", container: "container$name", wantErr: true, wantErrType: ErrInvalidContainerName}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateContainerName(tt.container) if tt.wantErr { require.Error(t, err) if tt.wantErrType != nil { assert.ErrorIs(t, err, tt.wantErrType) } } else { assert.NoError(t, err) } }) } }