1package domain
2
3import (
4 "strings"
5 "testing"
6
7 "github.com/stretchr/testify/assert"
8 "github.com/stretchr/testify/require"
9)
10
11func TestSanitizeDomainForEnvFile(t *testing.T) {
12 tests := []struct {
13 name string
14 domain string
15 want string
16 wantErr bool
17 }{
18 {name: "simple domain", domain: "example.com", want: "example_com"},
19 {name: "subdomain", domain: "app.example.com", want: "app_example_com"},
20 {name: "domain with port", domain: "example.com:8080", want: "example_com_8080"},
21 {name: "domain with path", domain: "example.com/path", want: "example_com_path"},
22 {name: "single char", domain: "a", want: "a"},
23 {name: "hyphenated", domain: "my-app.example.com", want: "my-app_example_com"},
24 {name: "empty", domain: "", wantErr: true},
25 {name: "path traversal", domain: "../etc/passwd", wantErr: true},
26 {name: "double dots", domain: "foo..bar", wantErr: true},
27 {name: "starts with dot", domain: ".hidden", wantErr: true},
28 {name: "ends with dot", domain: "trailing.", wantErr: true},
29 {name: "space", domain: "has space", wantErr: true},
30 {name: "special chars", domain: "bad$domain", wantErr: true},
31 {name: "idempotent simple", domain: "example_com", want: "example_com"},
32 {name: "idempotent complex", domain: "my-app_example_com_8080", want: "my-app_example_com_8080"},
33 }
34
35 for _, tt := range tests {
36 t.Run(tt.name, func(t *testing.T) {
37 got, err := SanitizeDomainForEnvFile(tt.domain)
38 if tt.wantErr {
39 assert.Error(t, err)
40 return
41 }
42 require.NoError(t, err)
43 assert.Equal(t, tt.want, got)
44 })
45 }
46}
47
48func TestParseEnvData(t *testing.T) {
49 tests := []struct {
50 name string
51 data string
52 want map[string]string
53 wantErr bool
54 }{
55 {
56 name: "simple key-value",
57 data: "FOO=bar\nBAZ=qux",
58 want: map[string]string{"FOO": "bar", "BAZ": "qux"},
59 },
60 {
61 name: "double-quoted value",
62 data: `KEY="hello world"`,
63 want: map[string]string{"KEY": "hello world"},
64 },
65 {
66 name: "single-quoted value",
67 data: `KEY='hello world'`,
68 want: map[string]string{"KEY": "hello world"},
69 },
70 {
71 name: "comment lines",
72 data: "# this is a comment\nKEY=val",
73 want: map[string]string{"KEY": "val"},
74 },
75 {
76 name: "empty lines",
77 data: "\n\nKEY=val\n\n",
78 want: map[string]string{"KEY": "val"},
79 },
80 {
81 name: "value with equals",
82 data: "URL=postgres://host:5432/db?opt=1",
83 want: map[string]string{"URL": "postgres://host:5432/db?opt=1"},
84 },
85 {
86 name: "no value",
87 data: "NOVALUE",
88 want: map[string]string{},
89 },
90 {
91 name: "empty value",
92 data: "KEY=",
93 want: map[string]string{"KEY": ""},
94 },
95 {
96 name: "empty input",
97 data: "",
98 want: map[string]string{},
99 },
100 {
101 name: "large value within buffer",
102 data: "BIG=" + strings.Repeat("x", 100000),
103 want: map[string]string{"BIG": strings.Repeat("x", 100000)},
104 },
105 {
106 name: "quoted value with inner spaces",
107 data: `KEY=" hello "`,
108 want: map[string]string{"KEY": " hello "},
109 },
110 {
111 name: "unquoted value with outer spaces",
112 data: "KEY= hello ",
113 want: map[string]string{"KEY": "hello"},
114 },
115 }
116
117 for _, tt := range tests {
118 t.Run(tt.name, func(t *testing.T) {
119 got, err := ParseEnvData([]byte(tt.data))
120 if tt.wantErr {
121 assert.Error(t, err)
122 return
123 }
124 require.NoError(t, err)
125 assert.Equal(t, tt.want, got)
126 })
127 }
128}
129
130func TestValidateEnvKey(t *testing.T) {
131 tests := []struct {
132 name string
133 key string
134 wantErr bool
135 wantErrType error
136 }{
137 {name: "simple", key: "FOO", wantErr: false},
138 {name: "with underscore", key: "FOO_BAR", wantErr: false},
139 {name: "starts with underscore", key: "_PRIVATE", wantErr: false},
140 {name: "lowercase", key: "foo", wantErr: false},
141 {name: "mixed", key: "myApp_v2", wantErr: false},
142 {name: "empty", key: "", wantErr: true, wantErrType: ErrInvalidEnvKey},
143 {name: "starts with number", key: "1BAD", wantErr: true, wantErrType: ErrInvalidEnvKey},
144 {name: "contains dot", key: "FOO.BAR", wantErr: true, wantErrType: ErrInvalidEnvKey},
145 {name: "contains slash", key: "FOO/BAR", wantErr: true, wantErrType: ErrPathTraversal},
146 {name: "contains backslash", key: `FOO\BAR`, wantErr: true, wantErrType: ErrPathTraversal},
147 {name: "path traversal", key: "..", wantErr: true, wantErrType: ErrPathTraversal},
148 {name: "contains hyphen", key: "FOO-BAR", wantErr: true, wantErrType: ErrInvalidEnvKey},
149 {name: "contains space", key: "FOO BAR", wantErr: true, wantErrType: ErrInvalidEnvKey},
150 }
151
152 for _, tt := range tests {
153 t.Run(tt.name, func(t *testing.T) {
154 err := ValidateEnvKey(tt.key)
155 if tt.wantErr {
156 require.Error(t, err)
157 if tt.wantErrType != nil {
158 assert.ErrorIs(t, err, tt.wantErrType)
159 }
160 } else {
161 assert.NoError(t, err)
162 }
163 })
164 }
165}
166
167func TestValidateContainerName(t *testing.T) {
168 tests := []struct {
169 name string
170 container string
171 wantErr bool
172 wantErrType error
173 }{
174 {name: "simple", container: "postgres", wantErr: false},
175 {name: "with hyphen", container: "gitea-postgres", wantErr: false},
176 {name: "with underscore", container: "gitea_postgres", wantErr: false},
177 {name: "complex real container", container: "gordon-git-example-com-gitea-postgres", wantErr: false},
178 {name: "starts with letter", container: "a-b", wantErr: false},
179 {name: "lowercase", container: "my-container", wantErr: false},
180 {name: "mixed case", container: "MyContainer-123", wantErr: false},
181 {name: "empty", container: "", wantErr: true, wantErrType: ErrInvalidContainerName},
182 {name: "starts with number", container: "1container", wantErr: true, wantErrType: ErrInvalidContainerName},
183 {name: "starts with hyphen", container: "-container", wantErr: true, wantErrType: ErrInvalidContainerName},
184 {name: "starts with underscore", container: "_container", wantErr: true, wantErrType: ErrInvalidContainerName},
185 {name: "contains slash", container: "container/name", wantErr: true, wantErrType: ErrPathTraversal},
186 {name: "contains backslash", container: `container\name`, wantErr: true, wantErrType: ErrPathTraversal},
187 {name: "path traversal", container: "..", wantErr: true, wantErrType: ErrPathTraversal},
188 {name: "contains dot", container: "container.name", wantErr: true, wantErrType: ErrInvalidContainerName},
189 {name: "contains space", container: "container name", wantErr: true, wantErrType: ErrInvalidContainerName},
190 {name: "contains special char", container: "container$name", wantErr: true, wantErrType: ErrInvalidContainerName},
191 }
192
193 for _, tt := range tests {
194 t.Run(tt.name, func(t *testing.T) {
195 err := ValidateContainerName(tt.container)
196 if tt.wantErr {
197 require.Error(t, err)
198 if tt.wantErrType != nil {
199 assert.ErrorIs(t, err, tt.wantErrType)
200 }
201 } else {
202 assert.NoError(t, err)
203 }
204 })
205 }
206}