-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain_test.go
More file actions
153 lines (142 loc) · 4.3 KB
/
Copy pathmain_test.go
File metadata and controls
153 lines (142 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package main
import (
"errors"
"os"
"path/filepath"
"strings"
"testing"
)
// --- lint against representative catalog samples (both must exit 0) -------
// testdata/whoami (minimal) + testdata/files-demo (a use-case-folder grant) are
// kept here as local fixtures: the shipping catalog moved to the control plane
// (cloud #62), so the box repo no longer bakes a catalog/ tree to point at.
func TestLint_RealSamples(t *testing.T) {
for _, p := range []string{
"testdata/whoami/manifest.yml",
"testdata/files-demo/manifest.yml",
} {
if err := lint(p); err != nil {
t.Errorf("lint(%s): want clean, got %v", p, err)
}
}
}
// --- lint rejects malformed manifests with an actionable message ----------
const validManifest = `id: test-app
manifest_version: 1
name: Test App
version: "1.0"
compose_file: compose.yml
main_service: web
main_port: 8080
`
const validCompose = `services:
web:
image: nginx:1.0
`
// writeApp lays out a manifest + (optionally) a sibling compose in a fresh temp
// dir and returns the manifest path. An empty compose string skips the compose
// file, so the relative resolution hits a missing file.
func writeApp(t *testing.T, manifestYAML, composeYAML string) string {
t.Helper()
dir := t.TempDir()
mp := filepath.Join(dir, "manifest.yml")
if err := os.WriteFile(mp, []byte(manifestYAML), 0o644); err != nil {
t.Fatalf("write manifest: %v", err)
}
if composeYAML != "" {
if err := os.WriteFile(filepath.Join(dir, "compose.yml"), []byte(composeYAML), 0o644); err != nil {
t.Fatalf("write compose: %v", err)
}
}
return mp
}
func TestLint_Rejects(t *testing.T) {
cases := []struct {
name string
manifest string
compose string
wantMsg string // substring the error must contain
}{
{
name: "missing required field",
manifest: strings.Replace(validManifest, "name: Test App\n", "", 1),
compose: validCompose,
wantMsg: "name",
},
{
name: "bad slug",
manifest: strings.Replace(validManifest, "id: test-app", "id: Test_App", 1),
compose: validCompose,
wantMsg: "kebab-case",
},
{
name: "unsupported manifest_version",
manifest: strings.Replace(validManifest, "manifest_version: 1", "manifest_version: 2", 1),
compose: validCompose,
wantMsg: "manifest_version",
},
{
name: "missing compose file",
manifest: validManifest,
compose: "", // don't write compose.yml
wantMsg: "compose_file",
},
{
name: "compose declares no services",
manifest: validManifest,
compose: "version: \"3\"\n",
wantMsg: "no services",
},
{
name: "main_service absent from compose",
manifest: strings.Replace(validManifest, "main_service: web", "main_service: api", 1),
compose: validCompose,
wantMsg: "main_service",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := lint(writeApp(t, tc.manifest, tc.compose))
if err == nil {
t.Fatalf("want a lint error, got nil")
}
if !strings.Contains(err.Error(), tc.wantMsg) {
t.Fatalf("error %q does not name the problem (want substring %q)", err, tc.wantMsg)
}
})
}
}
func TestLint_MissingManifestFile(t *testing.T) {
err := lint(filepath.Join(t.TempDir(), "nope.yml"))
if err == nil || !strings.Contains(err.Error(), "read manifest") {
t.Fatalf("missing manifest: want a read error, got %v", err)
}
}
// --- argument dispatch ----------------------------------------------------
func TestRun_Dispatch(t *testing.T) {
good := writeApp(t, validManifest, validCompose)
cases := []struct {
name string
args []string
wantUsage bool // expect errUsage; otherwise expect success (nil)
}{
{"valid lint", []string{"manifest", "lint", good}, false},
{"no args", nil, true},
{"manifest only", []string{"manifest"}, true},
{"lint without path", []string{"manifest", "lint"}, true},
{"check without path", []string{"manifest", "check"}, true},
{"unknown subcommand", []string{"frobnicate"}, true},
{"extra args", []string{"manifest", "lint", good, "extra"}, true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := run(tc.args)
switch {
case tc.wantUsage && !errors.Is(err, errUsage):
t.Fatalf("args %v: want errUsage, got %v", tc.args, err)
case !tc.wantUsage && err != nil:
t.Fatalf("args %v: want success, got %v", tc.args, err)
}
})
}
}