v0.279.0: after_install (decision 45), known default logins on the page, Part D empty-backup alarm, night chain (R-705), R-706
gates / gates (push) Successful in 27s
gates / gates (push) Successful in 27s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package stacks
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// v0.279.0 (decision 45) — after_install: the one command after a FRESH install that replaces a known default
|
||||
// login with the generated one. The exec is the seam; nothing reaches Docker.
|
||||
// COMPANION RED-PROOFS: (1) drop the success-marker check in runAfterInstallNow → a failing command is
|
||||
// recorded ok and the second case fails; (2) make expandAfterInstall allow an undeclared/empty name → the
|
||||
// command runs with a hole and the third case fails.
|
||||
func TestAfterInstall_ReplacesTheDefaultLoginOnceAndRecordsIt(t *testing.T) {
|
||||
m, _ := keptManager(t)
|
||||
var logBuf bytes.Buffer
|
||||
m.logger = log.New(&logBuf, "", 0)
|
||||
afterInstallTries, afterInstallGap = 3, 0
|
||||
t.Cleanup(func() { afterInstallTries, afterInstallGap = 6, 20*time.Second })
|
||||
dir := filepath.Dir(m.stacks["cloudapp"].ComposePath)
|
||||
must(t, SaveAppConfig(dir, &AppConfig{Deployed: true, Env: map[string]string{"ADMIN_PASSWORD": "Gen3r4tedValue"}}, m.encKey, nil))
|
||||
ai := &AfterInstallCommand{Service: "cloudapp", Env: []string{"ADMIN_PASSWORD"},
|
||||
Command: []string{"/app/bin/tool", "set-admin", "${ADMIN_PASSWORD}"}, Success: "FELHOM_OK"}
|
||||
m.mu.Lock()
|
||||
m.stacks["cloudapp"].Meta.AfterInstall = ai
|
||||
m.mu.Unlock()
|
||||
record := func(ok bool, d string) {
|
||||
m.mutateAppConfig("cloudapp", dir, "after_install", func(c *AppConfig) bool {
|
||||
c.AfterInstall = &AfterInstallRecord{At: "t", OK: ok, Detail: d}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// 1. Success: the value is filled in, the command runs ONCE, the record says ok, the log never carries it.
|
||||
var calls [][]string
|
||||
m.afterLoadFn = func(_ string, args ...string) (string, error) { calls = append(calls, args); return "... FELHOM_OK", nil }
|
||||
cmd, err := expandAfterInstall(ai.Command, ai.Env, map[string]string{"ADMIN_PASSWORD": "Gen3r4tedValue"})
|
||||
must(t, err)
|
||||
if err := m.runAfterInstallNow("cloudapp", ai, cmd, record); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(calls) != 1 || strings.Join(calls[0], " ") != "exec -T cloudapp /app/bin/tool set-admin Gen3r4tedValue" {
|
||||
t.Fatalf("ran %v", calls)
|
||||
}
|
||||
if c := LoadAppConfig(dir); c.AfterInstall == nil || !c.AfterInstall.OK {
|
||||
t.Fatalf("record: %+v", c.AfterInstall)
|
||||
}
|
||||
if strings.Contains(logBuf.String(), "Gen3r4tedValue") {
|
||||
t.Fatal("the generated password reached the log")
|
||||
}
|
||||
|
||||
// 2. No success marker: retried, then recorded as FAILED — never recorded ok.
|
||||
calls = nil
|
||||
m.afterLoadFn = func(_ string, args ...string) (string, error) { calls = append(calls, args); return "boom", errors.New("exit 1") }
|
||||
if err := m.runAfterInstallNow("cloudapp", ai, cmd, record); err == nil {
|
||||
t.Fatal("a failing command reported success")
|
||||
}
|
||||
if len(calls) != 3 {
|
||||
t.Fatalf("tries %d, want 3", len(calls))
|
||||
}
|
||||
if c := LoadAppConfig(dir); c.AfterInstall == nil || c.AfterInstall.OK {
|
||||
t.Fatalf("a failure was recorded as ok: %+v", c.AfterInstall)
|
||||
}
|
||||
|
||||
// 2b. Exit 0 WITHOUT the marker (claper's `rpc` exits 0 on an Elixir error): still a failure.
|
||||
calls = nil
|
||||
m.afterLoadFn = func(_ string, args ...string) (string, error) {
|
||||
calls = append(calls, args)
|
||||
return "FELHOM_AFTER_INSTALL_FAILED {:error, changeset}", nil
|
||||
}
|
||||
if err := m.runAfterInstallNow("cloudapp", ai, cmd, record); err == nil {
|
||||
t.Fatal("an exit-0 command without the success marker reported success")
|
||||
}
|
||||
if c := LoadAppConfig(dir); c.AfterInstall == nil || c.AfterInstall.OK {
|
||||
t.Fatalf("an exit-0 failure was recorded as ok: %+v", c.AfterInstall)
|
||||
}
|
||||
|
||||
// 3. A value that is not declared, or empty, refuses BEFORE anything runs (never an empty password).
|
||||
for _, env := range []map[string]string{{}, {"ADMIN_PASSWORD": ""}} {
|
||||
if _, err := expandAfterInstall(ai.Command, ai.Env, env); err == nil {
|
||||
t.Fatalf("expanded with env %v", env)
|
||||
}
|
||||
}
|
||||
if _, err := expandAfterInstall([]string{"x ${OTHER}"}, ai.Env, map[string]string{"OTHER": "v"}); err == nil {
|
||||
t.Fatal("an undeclared name was filled in")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user