Make delivery and integration failures explicit
Persist reminder presentations and retry state, atomically complete collapsed deliveries, fall back across away reaches, and block permanent failures visibly (V-715, V-678). Fail closed when enabled integrations lack credentials and keep remote arms explicitly dark (V-691). Give mavweb one sanitized, request-correlated error contract (V-689). Owner explicitly requested direct commits to master.
This commit is contained in:
@@ -435,10 +435,21 @@ func TestNormaliseDropsAddresslessWorkstation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelDisabledKeepsLiveSTT(t *testing.T) {
|
||||
p := writeConfig(t, `{"workstation":{"model_disabled":true,"stt":{"url":"http://192.168.1.105:8081/transcribe","token":"secret"}}}`)
|
||||
c, err := Load(p)
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if c.Workstation == nil || c.Workstation.Stt == nil || !c.Workstation.ModelDisabled {
|
||||
t.Fatalf("model-only dark state did not preserve STT: %+v", c.Workstation)
|
||||
}
|
||||
}
|
||||
|
||||
// The health endpoint defaults to the supervisor's, not llama-server's: mavgpud
|
||||
// answers 503 while the card is held, and that refusal is the whole signal.
|
||||
func TestNormaliseFillsWorkstationDefaults(t *testing.T) {
|
||||
c := &Config{Workstation: &WorkstationConfig{URL: "http://192.168.1.105:8080/"}}
|
||||
c := &Config{Workstation: &WorkstationConfig{URL: "http://192.168.1.105:8080/", Token: "secret"}}
|
||||
c.applyDefaults()
|
||||
if c.Workstation == nil {
|
||||
t.Fatal("dropped a usable workstation block")
|
||||
@@ -460,6 +471,7 @@ func TestNormaliseKeepsExplicitWorkstationHealth(t *testing.T) {
|
||||
c := &Config{Workstation: &WorkstationConfig{
|
||||
URL: "http://192.168.1.105:8080",
|
||||
Health: "http://192.168.1.105:9000/ready",
|
||||
Token: "secret",
|
||||
}}
|
||||
c.applyDefaults()
|
||||
if got, want := c.Workstation.Health, "http://192.168.1.105:9000/ready"; got != want {
|
||||
@@ -467,6 +479,112 @@ func TestNormaliseKeepsExplicitWorkstationHealth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkstationLANEndpointsRequireSecrets(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
body string
|
||||
}{
|
||||
{"model", `{"workstation":{"url":"http://192.168.1.105:8080"}}`},
|
||||
{"stt", `{"workstation":{"url":"http://127.0.0.1:8080","stt":{"url":"http://192.168.1.105:8081/transcribe"}}}`},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if _, err := Load(writeConfig(t, tc.body)); err == nil {
|
||||
t.Fatal("Load accepted an enabled LAN endpoint with no secret")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkstationDisabledAllowsEmptySecrets(t *testing.T) {
|
||||
p := writeConfig(t, `{"workstation":{"disabled":true,"url":"http://192.168.1.105:8080","stt":{"url":"http://192.168.1.105:8081/transcribe"}}}`)
|
||||
c, err := Load(p)
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
if c.Workstation != nil {
|
||||
t.Fatalf("disabled workstation survived normalisation: %+v", c.Workstation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkstationArmsMayBeDisabledIndependently(t *testing.T) {
|
||||
modelOff := writeConfig(t, `{"workstation":{"model_disabled":true,"url":"http://192.168.1.105:8080","stt":{"url":"http://127.0.0.1:8081/transcribe"}}}`)
|
||||
if _, err := Load(modelOff); err != nil {
|
||||
t.Fatalf("Load model-disabled config: %v", err)
|
||||
}
|
||||
|
||||
sttOff := writeConfig(t, `{"workstation":{"url":"http://127.0.0.1:8080","stt":{"disabled":true,"url":"http://192.168.1.105:8081/transcribe"}}}`)
|
||||
c, err := Load(sttOff)
|
||||
if err != nil {
|
||||
t.Fatalf("Load STT-disabled config: %v", err)
|
||||
}
|
||||
if c.Workstation == nil || c.Workstation.Stt != nil {
|
||||
t.Fatalf("STT-only dark state changed model arm: %+v", c.Workstation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkstationLoopbackMayRunWithoutSecret(t *testing.T) {
|
||||
p := writeConfig(t, `{"workstation":{"url":"http://127.0.0.1:8080","stt":{"url":"http://localhost:8081/transcribe"}}}`)
|
||||
if _, err := Load(p); err != nil {
|
||||
t.Fatalf("Load loopback development endpoints: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTelegramLiveBlockRequiresBothSecrets(t *testing.T) {
|
||||
for _, body := range []string{
|
||||
`{"telegram":{"chat_id":"42"}}`,
|
||||
`{"telegram":{"bot_token":"token"}}`,
|
||||
} {
|
||||
if _, err := Load(writeConfig(t, body)); err == nil {
|
||||
t.Fatalf("Load accepted enabled Telegram block: %s", body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTelegramDisabledAllowsEmptySecrets(t *testing.T) {
|
||||
if _, err := Load(writeConfig(t, `{"telegram":{"disabled":true}}`)); err != nil {
|
||||
t.Fatalf("Load disabled Telegram: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNtfyLiveBlockRequiresSecret(t *testing.T) {
|
||||
p := writeConfig(t, `{"ntfy":{"base_url":"https://ntfy.example","topic":"maven"}}`)
|
||||
if _, err := Load(p); err == nil {
|
||||
t.Fatal("Load accepted enabled ntfy block with no credential")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNtfyDisabledAllowsEmptySecret(t *testing.T) {
|
||||
p := writeConfig(t, `{"ntfy":{"disabled":true,"base_url":"https://ntfy.example","topic":"maven"}}`)
|
||||
if _, err := Load(p); err != nil {
|
||||
t.Fatalf("Load disabled ntfy: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmartHomeLiveBlockRequiresSecret(t *testing.T) {
|
||||
p := writeConfig(t, `{"smarthome":{"enabled":true,"provider":"homeassistant","url":"http://192.168.1.50:8123"}}`)
|
||||
if _, err := Load(p); err == nil {
|
||||
t.Fatal("Load accepted enabled Home Assistant block with no token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmartHomeDisabledAllowsEmptySecret(t *testing.T) {
|
||||
p := writeConfig(t, `{"smarthome":{"enabled":false,"provider":"homeassistant","url":"http://192.168.1.50:8123"}}`)
|
||||
if _, err := Load(p); err != nil {
|
||||
t.Fatalf("Load disabled Home Assistant: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfiguredDBKeyEnvCannotBeEmpty(t *testing.T) {
|
||||
t.Setenv("MAVEN_TEST_DB_KEY", "")
|
||||
c := &Config{DBKeyEnv: "MAVEN_TEST_DB_KEY"}
|
||||
if _, err := c.DBEncryptionKey(); err == nil {
|
||||
t.Fatal("configured empty database key did not fail startup resolution")
|
||||
}
|
||||
if key, err := (&Config{}).DBEncryptionKey(); err != nil || key != nil {
|
||||
t.Fatalf("explicit plaintext config = (%v, %v), want (nil, nil)", key, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTelegramIntakeRefusesNamedChat(t *testing.T) {
|
||||
// The push half accepts an @channelusername and the intake half cannot use
|
||||
// one, so a box with both boots clean and answers nothing. Refuse the
|
||||
|
||||
Reference in New Issue
Block a user