Skip to content

Commit 2b0a4b6

Browse files
committed
fix: complete raw secret provider coverage
1 parent 528aecb commit 2b0a4b6

12 files changed

Lines changed: 331 additions & 172 deletions

cmd/secret/set_test.go

Lines changed: 90 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ func TestRunSecretSet_GlobalScopeWithoutComponentPreservesType(t *testing.T) {
107107
svc := newFakeSecretService()
108108
svc.scopes = map[string]secrets.Scope{"SHARED_TOKEN": secrets.ScopeGlobal}
109109
installService(t, svc, nil)
110+
originalLoadService := loadServiceFn
111+
var loadedScope secretScope
112+
loadServiceFn = func(scope secretScope) (secretService, error) {
113+
loadedScope = scope
114+
return originalLoadService(scope)
115+
}
116+
t.Cleanup(func() { loadServiceFn = originalLoadService })
110117
overrideEnumerateScopes(t, []scopeEntry{
111118
{
112119
Stack: "dev",
@@ -122,75 +129,96 @@ func TestRunSecretSet_GlobalScopeWithoutComponentPreservesType(t *testing.T) {
122129
err := runSecretSubcommand(t, "set", "SHARED_TOKEN=v1", "--stack", "dev", "--type", "helm")
123130
require.NoError(t, err)
124131
require.Len(t, svc.setCalls, 1)
132+
assert.Equal(t, "helm", loadedScope.ComponentType)
125133
}
126134

127135
func TestFindGlobalSetContext(t *testing.T) {
128-
t.Run("enumeration error", func(t *testing.T) {
129-
sentinel := errors.New("stack enumeration failed")
130-
overrideEnumerateScopes(t, nil, sentinel)
131-
132-
_, _, err := findGlobalSetContext(secretScope{Stack: "dev"}, "SHARED_TOKEN")
133-
require.ErrorIs(t, err, errUtils.ErrRequiredFlagNotProvided)
134-
})
135-
136-
t.Run("no matching declaration", func(t *testing.T) {
137-
overrideEnumerateScopes(t, []scopeEntry{
138-
{
139-
Stack: "prod",
140-
Component: "other-stack-service",
141-
ComponentType: "helm",
142-
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"}),
143-
},
144-
{
145-
Stack: "dev",
146-
Component: "other-type-service",
147-
ComponentType: "terraform",
148-
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"}),
149-
},
150-
{
151-
Stack: "dev",
152-
Component: "example-service",
153-
ComponentType: "helm",
154-
Section: secretDeclarationSection("OTHER_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"}),
136+
sharedSection := secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"})
137+
tests := []struct {
138+
name string
139+
entries []scopeEntry
140+
enumerationErr error
141+
scope secretScope
142+
expectedComponent string
143+
expectedType string
144+
expectedErr error
145+
}{
146+
{
147+
name: "enumeration error",
148+
enumerationErr: errors.New("stack enumeration failed"),
149+
scope: secretScope{Stack: "dev"},
150+
expectedErr: errUtils.ErrRequiredFlagNotProvided,
151+
},
152+
{
153+
name: "no matching declaration",
154+
entries: []scopeEntry{
155+
{
156+
Stack: "prod",
157+
Component: "other-stack-service",
158+
ComponentType: "helm",
159+
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"}),
160+
},
161+
{
162+
Stack: "dev",
163+
Component: "other-type-service",
164+
ComponentType: "terraform",
165+
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"}),
166+
},
167+
{
168+
Stack: "dev",
169+
Component: "example-service",
170+
ComponentType: "helm",
171+
Section: secretDeclarationSection("OTHER_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"}),
172+
},
155173
},
156-
}, nil)
157-
158-
_, _, err := findGlobalSetContext(secretScope{Stack: "dev", ComponentType: "helm"}, "SHARED_TOKEN")
159-
require.ErrorIs(t, err, errUtils.ErrRequiredFlagNotProvided)
160-
})
161-
162-
t.Run("inconsistent declarations", func(t *testing.T) {
163-
overrideEnumerateScopes(t, []scopeEntry{
164-
{
165-
Stack: "dev",
166-
Component: "example-service-a",
167-
ComponentType: "helm",
168-
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets-a", "scope": "global"}),
174+
scope: secretScope{Stack: "dev", ComponentType: "helm"},
175+
expectedErr: errUtils.ErrRequiredFlagNotProvided,
176+
},
177+
{
178+
name: "inconsistent declarations",
179+
entries: []scopeEntry{
180+
{
181+
Stack: "dev",
182+
Component: "example-service-a",
183+
ComponentType: "helm",
184+
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets-a", "scope": "global"}),
185+
},
186+
{
187+
Stack: "dev",
188+
Component: "example-service-b",
189+
ComponentType: "helm",
190+
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets-b", "scope": "global"}),
191+
},
169192
},
170-
{
171-
Stack: "dev",
172-
Component: "example-service-b",
173-
ComponentType: "helm",
174-
Section: secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets-b", "scope": "global"}),
193+
scope: secretScope{Stack: "dev"},
194+
expectedErr: errUtils.ErrRequiredFlagNotProvided,
195+
},
196+
{
197+
name: "identical declarations select first component",
198+
entries: []scopeEntry{
199+
{Stack: "dev", Component: "example-service-a", ComponentType: "helm", Section: sharedSection},
200+
{Stack: "dev", Component: "example-service-b", ComponentType: "helm", Section: sharedSection},
175201
},
176-
}, nil)
177-
178-
_, _, err := findGlobalSetContext(secretScope{Stack: "dev"}, "SHARED_TOKEN")
179-
require.ErrorIs(t, err, errUtils.ErrRequiredFlagNotProvided)
180-
})
202+
scope: secretScope{Stack: "dev"},
203+
expectedComponent: "example-service-a",
204+
expectedType: "helm",
205+
},
206+
}
181207

182-
t.Run("identical declarations select first component", func(t *testing.T) {
183-
section := secretDeclarationSection("SHARED_TOKEN", map[string]any{"store": "example-secrets", "scope": "global"})
184-
overrideEnumerateScopes(t, []scopeEntry{
185-
{Stack: "dev", Component: "example-service-a", ComponentType: "helm", Section: section},
186-
{Stack: "dev", Component: "example-service-b", ComponentType: "helm", Section: section},
187-
}, nil)
188-
189-
component, componentType, err := findGlobalSetContext(secretScope{Stack: "dev"}, "SHARED_TOKEN")
190-
require.NoError(t, err)
191-
assert.Equal(t, "example-service-a", component)
192-
assert.Equal(t, "helm", componentType)
193-
})
208+
for _, tt := range tests {
209+
t.Run(tt.name, func(t *testing.T) {
210+
overrideEnumerateScopes(t, tt.entries, tt.enumerationErr)
211+
212+
component, componentType, err := findGlobalSetContext(tt.scope, "SHARED_TOKEN")
213+
if tt.expectedErr != nil {
214+
require.ErrorIs(t, err, tt.expectedErr)
215+
return
216+
}
217+
require.NoError(t, err)
218+
assert.Equal(t, tt.expectedComponent, component)
219+
assert.Equal(t, tt.expectedType, componentType)
220+
})
221+
}
194222
}
195223

196224
func TestRunSecretSet_NonGlobalScopeStillRequiresComponent(t *testing.T) {

pkg/component/helm/secret_values_integration_test.go

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ func (s *jsonDecodingStore) GetRaw(stack, component, key string) (string, error)
5050
}
5151

5252
func TestNativeHelmSecretRawAndStructuredValuesMaskIndentedMultilineValues(t *testing.T) {
53+
fixture, atmosConfig, info := prepareHelmSecretValuesFixture(t)
54+
assertResolvedHelmSecretValues(t, info)
55+
rendered, renderedEnv := renderHelmSecretValuesFixture(t, fixture, atmosConfig, info)
56+
assert.Equal(t, serviceAccountJSON, envValue(t, renderedEnv, "SERVICE_ACCOUNT_JSON"), "Helm env.value must remain a scalar string")
57+
assertMaskedHelmSecretValues(t, rendered)
58+
}
59+
60+
func prepareHelmSecretValuesFixture(t *testing.T) (string, *schema.AtmosConfiguration, schema.ConfigAndStacksInfo) {
61+
t.Helper()
62+
5363
fixture, err := filepath.Abs(filepath.Join("..", "..", "..", "tests", "fixtures", "scenarios", "helm-secret-values"))
5464
require.NoError(t, err)
5565
t.Chdir(fixture)
@@ -80,6 +90,11 @@ func TestNativeHelmSecretRawAndStructuredValuesMaskIndentedMultilineValues(t *te
8090
info.SecretsMaskOnly = false
8191
info, err = e.ProcessStacks(&atmosConfig, info, true, true, true, nil, auth.AuthManager(nil))
8292
require.NoError(t, err)
93+
return fixture, &atmosConfig, info
94+
}
95+
96+
func assertResolvedHelmSecretValues(t *testing.T, info schema.ConfigAndStacksInfo) {
97+
t.Helper()
8398

8499
values, ok := info.ComponentSection[cfg.ValuesSectionName].(map[string]any)
85100
require.True(t, ok)
@@ -93,26 +108,37 @@ func TestNativeHelmSecretRawAndStructuredValuesMaskIndentedMultilineValues(t *te
93108
assert.Equal(t, "service@example.iam.gserviceaccount.com", envValue(t, env, "CLIENT_EMAIL"))
94109
assert.Equal(t, plainSecret, envValue(t, env, "PLAIN_TOKEN"))
95110
assert.Equal(t, privateKey, envValue(t, env, "SIGNING_KEY"))
111+
}
112+
113+
func renderHelmSecretValuesFixture(
114+
t *testing.T,
115+
fixture string,
116+
atmosConfig *schema.AtmosConfiguration,
117+
info schema.ConfigAndStacksInfo,
118+
) (string, []any) {
119+
t.Helper()
96120

97121
componentPath := filepath.Join(fixture, "components", "helm", "secret-values")
98-
spec, err := buildChartSpec(&atmosConfig, &info, componentPath)
122+
spec, err := buildChartSpec(atmosConfig, &info, componentPath)
99123
require.NoError(t, err)
100124
rendered, err := renderManifest(context.Background(), spec)
101125
require.NoError(t, err)
102126

103127
objects, err := manifest.DecodeObjects([]byte(rendered))
104128
require.NoError(t, err)
105129
require.Len(t, objects, 1)
106-
renderedEnv, found, err := unstructured.NestedSlice(objects[0].Object, "spec", "template", "spec", "containers", "0", "env")
107-
if err != nil || !found {
108-
// NestedSlice cannot index arrays by string; extract the container first.
109-
containers, containersFound, containersErr := unstructured.NestedSlice(objects[0].Object, "spec", "template", "spec", "containers")
110-
require.NoError(t, containersErr)
111-
require.True(t, containersFound)
112-
container := containers[0].(map[string]any)
113-
renderedEnv = container["env"].([]any)
114-
}
115-
assert.Equal(t, serviceAccountJSON, envValue(t, renderedEnv, "SERVICE_ACCOUNT_JSON"), "Helm env.value must remain a scalar string")
130+
containers, found, err := unstructured.NestedSlice(objects[0].Object, "spec", "template", "spec", "containers")
131+
require.NoError(t, err)
132+
require.True(t, found)
133+
require.NotEmpty(t, containers)
134+
require.IsType(t, map[string]any{}, containers[0])
135+
container := containers[0].(map[string]any)
136+
require.IsType(t, []any{}, container["env"])
137+
return rendered, container["env"].([]any)
138+
}
139+
140+
func assertMaskedHelmSecretValues(t *testing.T, rendered string) {
141+
t.Helper()
116142

117143
masker := iolib.GetContext().Masker()
118144
t.Cleanup(func() {

pkg/function/parser/parser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ func ParseStoreGet(input string) (StoreGetArgs, error) {
337337
return StoreGetArgs{Store: words[0], Key: words[1], Default: options.defaultValue, Query: options.query}, nil
338338
}
339339

340-
// ParseSecret parses `name [| path expression] [| raw] [| default value]`.
340+
// ParseSecret parses `name [| path expression | raw] [| default value]`.
341341
func ParseSecret(input string) (SecretArgs, error) {
342342
hasPath := false
343343
tokens, err := tokenize(input)

pkg/function/parser/parser_test.go

Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -194,33 +194,47 @@ func TestParseStoreGet(t *testing.T) {
194194
}
195195

196196
func TestParseSecret(t *testing.T) {
197-
actual, err := ParseSecret(`SERVICE_CONFIG | path ".credentials.token" | default "not set"`)
198-
require.NoError(t, err)
199-
assert.Equal(t, SecretArgs{Name: "SERVICE_CONFIG", Path: ".credentials.token", Default: stringPtr("not set")}, actual)
200-
201-
actual, err = ParseSecret(`SERVICE_CONFIG | path .credentials.token // "not set"`)
202-
require.NoError(t, err)
203-
assert.Equal(t, `.credentials.token // "not set"`, actual.Path)
197+
tests := []struct {
198+
name string
199+
input string
200+
expected SecretArgs
201+
wantErr bool
202+
}{
203+
{
204+
name: "path with default",
205+
input: `SERVICE_CONFIG | path ".credentials.token" | default "not set"`,
206+
expected: SecretArgs{Name: "SERVICE_CONFIG", Path: ".credentials.token", Default: stringPtr("not set")},
207+
},
208+
{
209+
name: "path expression with spaces",
210+
input: `SERVICE_CONFIG | path .credentials.token // "not set"`,
211+
expected: SecretArgs{Name: "SERVICE_CONFIG", Path: `.credentials.token // "not set"`},
212+
},
213+
{
214+
name: "compact raw with empty default",
215+
input: `SERVICE_CREDENTIALS |raw | default ""`,
216+
expected: SecretArgs{Name: "SERVICE_CREDENTIALS", Raw: true, Default: stringPtr("")},
217+
},
218+
{name: "empty input", input: "", wantErr: true},
219+
{name: "empty name", input: "| raw", wantErr: true},
220+
{name: "missing delimiter", input: "SERVICE_CONFIG raw", wantErr: true},
221+
{name: "raw with value", input: "SERVICE_CONFIG | raw value", wantErr: true},
222+
{name: "path without value", input: "SERVICE_CONFIG | path", wantErr: true},
223+
{name: "unknown option", input: "SERVICE_CONFIG | unknown value", wantErr: true},
224+
{name: "raw path conflict", input: `SERVICE_CONFIG | raw | path ".token"`, wantErr: true},
225+
{name: "empty path conflicts with raw", input: `SERVICE_CONFIG | path "" | raw`, wantErr: true},
226+
}
204227

205-
actual, err = ParseSecret(`SERVICE_CREDENTIALS |raw | default ""`)
206-
require.NoError(t, err)
207-
assert.Equal(t, "SERVICE_CREDENTIALS", actual.Name)
208-
assert.True(t, actual.Raw)
209-
require.NotNil(t, actual.Default)
210-
assert.Empty(t, *actual.Default)
211-
212-
for _, input := range []string{
213-
"",
214-
"| raw",
215-
"SERVICE_CONFIG raw",
216-
"SERVICE_CONFIG | raw value",
217-
"SERVICE_CONFIG | path",
218-
"SERVICE_CONFIG | unknown value",
219-
`SERVICE_CONFIG | raw | path ".token"`,
220-
`SERVICE_CONFIG | path "" | raw`,
221-
} {
222-
_, err = ParseSecret(input)
223-
require.Error(t, err, input)
228+
for _, tt := range tests {
229+
t.Run(tt.name, func(t *testing.T) {
230+
actual, err := ParseSecret(tt.input)
231+
if tt.wantErr {
232+
require.Error(t, err)
233+
return
234+
}
235+
require.NoError(t, err)
236+
assert.Equal(t, tt.expected, actual)
237+
})
224238
}
225239
}
226240

pkg/secrets/providers/store_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,34 @@ func TestStoreProvider_GetRawRejectsStructuredFallback(t *testing.T) {
128128
require.ErrorIs(t, err, ErrRawNotSupported)
129129
}
130130

131+
type rawFakeStore struct {
132+
rawPayload string
133+
getCalls int
134+
rawCalls int
135+
}
136+
137+
func (s *rawFakeStore) Set(_, _, _ string, _ any) error { return nil }
138+
func (s *rawFakeStore) Get(_, _, _ string) (any, error) {
139+
s.getCalls++
140+
return "decoded", nil
141+
}
142+
func (s *rawFakeStore) GetKey(_ string) (any, error) { return nil, nil }
143+
func (s *rawFakeStore) GetRaw(_, _, _ string) (string, error) {
144+
s.rawCalls++
145+
return s.rawPayload, nil
146+
}
147+
148+
func TestStoreProvider_GetRawDelegatesToNativeRawStore(t *testing.T) {
149+
rawStore := &rawFakeStore{rawPayload: `{"enabled":true}`}
150+
p := &storeProvider{name: "app", kind: "example/raw", store: rawStore}
151+
152+
got, err := p.GetRaw(Coordinate{Stack: "prod", Component: "api", Key: "CONFIG"})
153+
require.NoError(t, err)
154+
assert.Equal(t, `{"enabled":true}`, got)
155+
assert.Equal(t, 1, rawStore.rawCalls)
156+
assert.Zero(t, rawStore.getCalls)
157+
}
158+
131159
func TestStoreProvider_DeleteUnsupported(t *testing.T) {
132160
ctrl := gomock.NewController(t)
133161
defer ctrl.Finish()

0 commit comments

Comments
 (0)