1package appmanifest_test
2
3import (
4 "testing"
5
6 "github.com/stretchr/testify/assert"
7 "github.com/stretchr/testify/require"
8
9 "github.com/bnema/gordon/internal/adapters/in/appmanifest"
10 "github.com/bnema/gordon/internal/domain"
11)
12
13const secretsHead = "name = \"blog\"\n[services.web]\nimage = \"img:1\"\n"
14
15func TestParse_SecretForms(t *testing.T) {
16 cases := []struct {
17 name string
18 doc string
19 wantMap map[string]string
20 wantFiles []string
21 }{
22 {"short form", "[services.web.secrets]\nDB = \"db\"\n", map[string]string{"DB": "db"}, nil},
23 {"table name only", "[services.web.secrets]\nDB = { name = \"db\" }\n", map[string]string{"DB": "db"}, nil},
24 {"table env", "[services.web.secrets]\nDB = { name = \"db\", as = \"env\" }\n", map[string]string{"DB": "db"}, nil},
25 {"table file", "[services.web.secrets]\nDB = { name = \"db\", as = \"file\" }\n", map[string]string{"DB": "db"}, []string{"DB"}},
26 {"sub-table", "[services.web.secrets.DB]\nname = \"db\"\nas = \"file\"\n", map[string]string{"DB": "db"}, []string{"DB"}},
27 {
28 "mixed",
29 "[services.web.secrets]\nZ = { name = \"z\", as = \"file\" }\nA = { name = \"a\", as = \"file\" }\nM = \"m\"\nE = { name = \"e\", as = \"env\" }\n",
30 map[string]string{"Z": "z", "A": "a", "M": "m", "E": "e"},
31 []string{"A", "Z"},
32 },
33 }
34 for _, tc := range cases {
35 t.Run(tc.name, func(t *testing.T) {
36 spec, _, err := appmanifest.Parse([]byte(secretsHead+tc.doc), "blog.toml")
37 require.NoError(t, err)
38 require.Len(t, spec.Services, 1)
39 assert.Equal(t, tc.wantMap, spec.Services[0].Secrets)
40 assert.Equal(t, tc.wantFiles, spec.Services[0].SecretFiles)
41 })
42 }
43}
44
45func TestParse_NoSecretsKeepsEmptyMap(t *testing.T) {
46 spec, _, err := appmanifest.Parse([]byte(secretsHead), "blog.toml")
47 require.NoError(t, err)
48 assert.NotNil(t, spec.Services[0].Secrets)
49 assert.Empty(t, spec.Services[0].Secrets)
50 assert.Nil(t, spec.Services[0].SecretFiles)
51}
52
53func TestParse_SecretFormsInvalid(t *testing.T) {
54 cases := []struct {
55 name string
56 doc string
57 wantErr string
58 }{
59 {"bad as", "[services.web.secrets]\nDB = { name = \"db\", as = \"volume\" }\n", "as must be"},
60 {"empty as", "[services.web.secrets]\nDB = { name = \"db\", as = \"\" }\n", "as must be"},
61 {"as not string", "[services.web.secrets]\nDB = { name = \"db\", as = 1 }\n", "as must be"},
62 {"missing name", "[services.web.secrets]\nDB = { as = \"file\" }\n", "name must be"},
63 {"name not string", "[services.web.secrets]\nDB = { name = 1 }\n", "name must be"},
64 {"extra key", "[services.web.secrets]\nDB = { name = \"db\", mode = \"x\" }\n", "unknown key \"mode\""},
65 {"integer value", "[services.web.secrets]\nDB = 42\n", "secret name string"},
66 {"array value", "[services.web.secrets]\nDB = [\"db\"]\n", "secret name string"},
67 {"file key collision", "[services.web.env]\nDB_FILE = \"x\"\n[services.web.secrets]\nDB = { name = \"db\", as = \"file\" }\n", "collides with a secret file key"},
68 }
69 for _, tc := range cases {
70 t.Run(tc.name, func(t *testing.T) {
71 _, _, err := appmanifest.Parse([]byte(secretsHead+tc.doc), "blog.toml")
72 require.Error(t, err)
73 assert.ErrorIs(t, err, domain.ErrInvalidAppSpec)
74 assert.ErrorContains(t, err, tc.wantErr)
75 })
76 }
77}