Files
felhom-controller/controller/internal/stacks/r757_inject_test.go
T
admin 1991c742a3 R-757: a secret only after_install reads is not invented for an installed app
InjectMissingFields skips a new generated secret that is in after_install's
env list and named by no compose definition: after_install does not run
again, so the app never received the value and the reveal would answer
a login the app does not have (calibre-web ADMIN_USER, 2026-10-01).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-05 23:12:59 +02:00

49 lines
1.8 KiB
Go

package stacks
import (
"os"
"path/filepath"
"testing"
)
// R-757 — the consequence: after a template gains a generated secret that only after_install reads,
// an INSTALLED app does not get an invented value (calibre-web's ADMIN_USER, 2026-10-01); a new secret
// the compose file reads is still generated as before.
func TestR757_AfterInstallOnlyFieldIsNotInvented(t *testing.T) {
compose := "services:\n app:\n image: nginx:1.27\n environment:\n - NEW_KEY=${NEW_KEY}\n"
m, dir, _ := newR442Manager(t, "app", compose, "deployed: true\nenv:\n KEEP: x\n", "")
meta := "display_name: App\nslug: app\n" +
"deploy_fields:\n" +
" - env_var: ADMIN_USER\n label: U\n type: secret\n generate: \"hex:5\"\n" +
" - env_var: NEW_KEY\n label: K\n type: secret\n generate: \"hex:8\"\n" +
"after_install:\n service: app\n env: [ADMIN_USER]\n command: [\"true\", \"${ADMIN_USER}\"]\n success: OK\n"
if err := os.WriteFile(filepath.Join(dir, ".felhom.yml"), []byte(meta), 0o644); err != nil {
t.Fatal(err)
}
if md := LoadMetadata(dir); md.AfterInstall == nil || len(md.DeployFields) != 2 {
t.Fatalf("fixture metadata did not parse: after_install=%v fields=%d", md.AfterInstall, len(md.DeployFields))
}
m.InjectMissingFields([]string{"app"})
cfg := LoadAppConfig(dir)
if cfg == nil {
t.Fatal("app.yaml unreadable after injection")
}
env := cfg.Env
if _, ok := env["ADMIN_USER"]; ok {
t.Errorf("ADMIN_USER was INVENTED for an installed app (only after_install reads it): %v", keysOf(env))
}
if v := env["NEW_KEY"]; v == "" {
t.Errorf("a new secret the compose file reads must still be generated: %v", keysOf(env))
}
}
func keysOf(m map[string]string) []string {
var out []string
for k := range m {
out = append(out, k)
}
return out
}