d15ad105be
stop/start/restart/update, remove and delete matched "protected", "not found", "not deployed", "still running", "not orphaned" in err.Error(). New sentinels in internal/stacks (stack_errors.go) carried by the producers in manager.go/delete.go via util.KindErrorf (message bytes unchanged); api.stackOpStatusFor maps them. Tests: reworded-message table per family, wiring check over all three handlers, producers keep kind + words. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
52 lines
2.4 KiB
Go
52 lines
2.4 KiB
Go
package stacks
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
// R-569 — the stack-lifecycle refusals the API maps to 404/403/409 carry their KIND, and their
|
|
// messages are byte-for-byte what they were. Every case below refuses BEFORE anything is executed
|
|
// (no compose, no docker): unknown name, protected name, or a scanned app that is not orphaned.
|
|
//
|
|
// RED-PROOF (REPORT): put any one producer back to plain fmt.Errorf and its errors.Is row fails while
|
|
// its message row still passes — the silent state the API's old text match lived in.
|
|
func TestR569_StackProducersCarryKindAndKeepTheirWords(t *testing.T) {
|
|
m := r553Manager(t, "display_name: R569\n")
|
|
cases := []struct {
|
|
name string
|
|
call func() error
|
|
kind error
|
|
wantMsg string
|
|
}{
|
|
{"start unknown", func() error { return m.StartStack("nope") }, ErrStackNotFound, `stack "nope" not found`},
|
|
{"stop unknown", func() error { return m.StopStack("nope") }, ErrStackNotFound, `stack "nope" not found`},
|
|
{"restart unknown", func() error { return m.RestartStack("nope") }, ErrStackNotFound, `stack "nope" not found`},
|
|
{"stop protected", func() error { return m.StopStack("samba") }, ErrProtectedStack,
|
|
`stack "samba" is protected and cannot be stopped`},
|
|
{"remove unknown", func() error { _, err := m.RemoveStack("nope", false, nil); return err }, ErrStackNotFound,
|
|
`stack "nope" not found`},
|
|
{"remove protected", func() error { _, err := m.RemoveStack("samba", false, nil); return err }, ErrProtectedStack,
|
|
`stack "samba" is protected and cannot be removed`},
|
|
{"delete unknown", func() error { _, err := m.DeleteStack("nope", false); return err }, ErrStackNotFound,
|
|
`stack "nope" not found`},
|
|
{"delete protected", func() error { _, err := m.DeleteStack("samba", false); return err }, ErrProtectedStack,
|
|
`stack "samba" is protected and cannot be deleted`},
|
|
{"delete not orphaned", func() error { _, err := m.DeleteStack("r553app", false); return err }, ErrNotOrphaned,
|
|
`stack "r553app" is not orphaned — only orphaned stacks can be deleted`},
|
|
}
|
|
for _, c := range cases {
|
|
err := c.call()
|
|
if err == nil {
|
|
t.Errorf("%s: no refusal", c.name)
|
|
continue
|
|
}
|
|
if !errors.Is(err, c.kind) {
|
|
t.Errorf("%s: errors.Is(err, %v) = false — the API would answer 500 (err=%q)", c.name, c.kind, err.Error())
|
|
}
|
|
if err.Error() != c.wantMsg {
|
|
t.Errorf("%s: message moved:\n got %q\n want %q", c.name, err.Error(), c.wantMsg)
|
|
}
|
|
}
|
|
}
|