Skip to content

Commit 53eee87

Browse files
committed
test(missions): add table-driven unit tests for pure functions
Signed-off-by: AdeshDeshmukh <adeshkd123@gmail.com>
1 parent 0050574 commit 53eee87

3 files changed

Lines changed: 255 additions & 0 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package missions
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestEmbeddedHiddenMissionEntry_HiddenFiles(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
entry string
13+
hidden bool
14+
}{
15+
{name: "dotfile", entry: ".hidden", hidden: true},
16+
{name: "double dot", entry: "..something", hidden: true},
17+
{name: "hidden dir entry", entry: ".config", hidden: true},
18+
{name: "index json", entry: "index.json", hidden: true},
19+
{name: "search state", entry: "search-state.json", hidden: true},
20+
{name: "regular file", entry: "mission.yaml", hidden: false},
21+
{name: "regular dir", entry: "fixes", hidden: false},
22+
{name: "with extension", entry: "README.md", hidden: false},
23+
{name: "empty string", entry: "", hidden: false},
24+
{name: "index as suffix", entry: "not-index.json", hidden: false},
25+
}
26+
for _, tt := range tests {
27+
tt := tt
28+
t.Run(tt.name, func(t *testing.T) {
29+
result := embeddedHiddenMissionEntry(tt.entry)
30+
assert.Equal(t, tt.hidden, result)
31+
})
32+
}
33+
}
34+
35+
func TestEmbeddedHiddenMissionEntry_Deterministic(t *testing.T) {
36+
v1 := embeddedHiddenMissionEntry(".secret")
37+
v2 := embeddedHiddenMissionEntry(".secret")
38+
assert.Equal(t, v1, v2, "pure function must be deterministic")
39+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package missions
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"net/http"
7+
"net/http/httptest"
8+
"testing"
9+
10+
"github.com/gofiber/fiber/v2"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestIsDemoMode(t *testing.T) {
16+
app := fiber.New()
17+
app.Get("/test-demo", func(c *fiber.Ctx) error {
18+
if isDemoMode(c) {
19+
return c.SendString("true")
20+
}
21+
return c.SendString("false")
22+
})
23+
24+
tests := []struct {
25+
name string
26+
header string
27+
want bool
28+
}{
29+
{name: "exact true", header: "true", want: true},
30+
{name: "uppercase TRUE", header: "TRUE", want: false},
31+
{name: "numeric 1", header: "1", want: false},
32+
{name: "literal false", header: "false", want: false},
33+
{name: "no header", header: "", want: false},
34+
{name: "yes value", header: "yes", want: false},
35+
}
36+
for _, tt := range tests {
37+
tt := tt
38+
t.Run(tt.name, func(t *testing.T) {
39+
req := httptest.NewRequest(http.MethodGet, "/test-demo", nil)
40+
if tt.header != "" {
41+
req.Header.Set("X-Demo-Mode", tt.header)
42+
}
43+
resp, err := app.Test(req, 5000)
44+
require.NoError(t, err)
45+
defer resp.Body.Close()
46+
body, err := io.ReadAll(resp.Body)
47+
require.NoError(t, err)
48+
assert.Equal(t, fmt.Sprintf("%t", tt.want), string(body))
49+
})
50+
}
51+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package missions
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestValidateSlackWebhookURL_Valid(t *testing.T) {
12+
validURL := "https://hooks.slack.com/services/T00/B00/XXX"
13+
err := validateSlackWebhookURL(validURL)
14+
assert.NoError(t, err)
15+
}
16+
17+
func TestValidateSlackWebhookURL_Invalid(t *testing.T) {
18+
tests := []struct {
19+
name string
20+
url string
21+
msg string
22+
}{
23+
{name: "http instead of https", url: "http://hooks.slack.com/services/T00/B00/XXX", msg: "must use https"},
24+
{name: "javascript protocol", url: "javascript:alert(1)", msg: "must use https"},
25+
{name: "file protocol", url: "file:///etc/passwd", msg: "must use https"},
26+
{name: "SSRF internal IP", url: "https://192.168.1.1/services/T", msg: "host must be hooks.slack.com"},
27+
{name: "SSRF localhost", url: "https://localhost/services/T", msg: "host must be hooks.slack.com"},
28+
{name: "wrong domain", url: "https://evil.com/services/T00/B00/XXX", msg: "host must be hooks.slack.com"},
29+
{name: "subdomain confusion", url: "https://hooks.slack.com.evil.com/services/T", msg: "host must be hooks.slack.com"},
30+
{name: "empty string", url: "", msg: "webhook URL is required"},
31+
}
32+
for _, tt := range tests {
33+
tt := tt
34+
t.Run(tt.name, func(t *testing.T) {
35+
err := validateSlackWebhookURL(tt.url)
36+
assert.Error(t, err)
37+
assert.Contains(t, err.Error(), tt.msg)
38+
})
39+
}
40+
}
41+
42+
func TestResolveAllowedShareRepos(t *testing.T) {
43+
const envVar = "KC_ALLOWED_SHARE_REPOS"
44+
45+
t.Run("env unset includes default repos", func(t *testing.T) {
46+
t.Cleanup(func() {
47+
os.Unsetenv(envVar)
48+
})
49+
os.Unsetenv(envVar)
50+
51+
repos := resolveAllowedShareRepos()
52+
assert.NotEmpty(t, repos, "allowlist must not be empty")
53+
assert.Contains(t, repos, "kubestellar/console-kb")
54+
})
55+
56+
t.Run("env with additional repos", func(t *testing.T) {
57+
t.Cleanup(func() {
58+
os.Unsetenv(envVar)
59+
})
60+
require.NoError(t, os.Setenv(envVar, "org1/repo1,org2/repo2"))
61+
62+
repos := resolveAllowedShareRepos()
63+
assert.Contains(t, repos, "kubestellar/console-kb")
64+
assert.Contains(t, repos, "org1/repo1")
65+
assert.Contains(t, repos, "org2/repo2")
66+
assert.GreaterOrEqual(t, len(repos), 3)
67+
})
68+
69+
t.Run("env values are trimmed", func(t *testing.T) {
70+
t.Cleanup(func() {
71+
os.Unsetenv(envVar)
72+
})
73+
require.NoError(t, os.Setenv(envVar, " org1/repo1 , org2/repo2 "))
74+
75+
repos := resolveAllowedShareRepos()
76+
assert.Contains(t, repos, "kubestellar/console-kb")
77+
assert.Contains(t, repos, "org1/repo1")
78+
assert.Contains(t, repos, "org2/repo2")
79+
})
80+
81+
t.Run("env with empty entries are ignored", func(t *testing.T) {
82+
t.Cleanup(func() {
83+
os.Unsetenv(envVar)
84+
})
85+
require.NoError(t, os.Setenv(envVar, "org1/repo1,,org2/repo2"))
86+
87+
repos := resolveAllowedShareRepos()
88+
assert.Contains(t, repos, "kubestellar/console-kb")
89+
assert.Contains(t, repos, "org1/repo1")
90+
assert.Contains(t, repos, "org2/repo2")
91+
})
92+
}
93+
94+
func TestIsRepoAllowedForShareWithList(t *testing.T) {
95+
allowlist := []string{"kubestellar/console-kb", "org1/repo1"}
96+
97+
t.Run("repo in list", func(t *testing.T) {
98+
assert.True(t, isRepoAllowedForShareWithList("kubestellar/console-kb", allowlist))
99+
})
100+
101+
t.Run("case insensitive match", func(t *testing.T) {
102+
assert.True(t, isRepoAllowedForShareWithList("KubeStellar/Console-KB", allowlist))
103+
})
104+
105+
t.Run("repo not in list", func(t *testing.T) {
106+
assert.False(t, isRepoAllowedForShareWithList("evil/repo", allowlist))
107+
})
108+
109+
t.Run("empty list", func(t *testing.T) {
110+
assert.False(t, isRepoAllowedForShareWithList("kubestellar/console-kb", []string{}))
111+
})
112+
113+
t.Run("nil list", func(t *testing.T) {
114+
assert.False(t, isRepoAllowedForShareWithList("kubestellar/console-kb", nil))
115+
})
116+
}
117+
118+
func TestIsRepoAllowedForShare(t *testing.T) {
119+
const envVar = "KC_ALLOWED_SHARE_REPOS"
120+
121+
t.Run("exact match", func(t *testing.T) {
122+
t.Cleanup(func() {
123+
os.Unsetenv(envVar)
124+
})
125+
os.Unsetenv(envVar)
126+
127+
assert.True(t, isRepoAllowedForShare("kubestellar/console-kb"))
128+
})
129+
130+
t.Run("uppercase variant", func(t *testing.T) {
131+
t.Cleanup(func() {
132+
os.Unsetenv(envVar)
133+
})
134+
os.Unsetenv(envVar)
135+
136+
assert.True(t, isRepoAllowedForShare("KUBESTELLAR/CONSOLE-KB"))
137+
})
138+
139+
t.Run("not in list", func(t *testing.T) {
140+
t.Cleanup(func() {
141+
os.Unsetenv(envVar)
142+
})
143+
os.Unsetenv(envVar)
144+
145+
assert.False(t, isRepoAllowedForShare("evil/repo"))
146+
})
147+
148+
t.Run("empty string", func(t *testing.T) {
149+
t.Cleanup(func() {
150+
os.Unsetenv(envVar)
151+
})
152+
os.Unsetenv(envVar)
153+
154+
assert.False(t, isRepoAllowedForShare(""))
155+
})
156+
157+
t.Run("path traversal attempt", func(t *testing.T) {
158+
t.Cleanup(func() {
159+
os.Unsetenv(envVar)
160+
})
161+
os.Unsetenv(envVar)
162+
163+
assert.False(t, isRepoAllowedForShare("layer5io/../admin/repo"))
164+
})
165+
}

0 commit comments

Comments
 (0)