package main import ( "context" "encoding/json" "strings" "testing" ) // An empty attention list used to be answered "ничего не требует внимания" // unconditionally, which is an all-clear Maven had no way to know was true // (ECOSYSTEM-SPEC §2.6, Vikunja #540). func TestAttentionEmptyWithHealthySourcesIsAllClear(t *testing.T) { praxis := newFakePraxisWithSources(t, `[]`, `[{"source_id":"src_ntfy","health":"ok"}]`) h := newPraxisTestHandler(t, praxis) reply := h.handlePraxisAct(context.Background(), praxisActDec("list_attention")) if !strings.Contains(reply, "ничего не требует внимания") { t.Fatalf("healthy and quiet should be an all-clear, got %q", reply) } } func TestAttentionEmptyWithAFailedSourceHedges(t *testing.T) { praxis := newFakePraxisWithSources(t, `[]`, `[ {"source_id":"src_ntfy","health":"ok"}, {"source_id":"src_llamacpp","health":"failed"}, {"source_id":"src_imap","health":"stale"} ]`) h := newPraxisTestHandler(t, praxis) reply := h.handlePraxisAct(context.Background(), praxisActDec("list_attention")) if strings.Contains(reply, "ничего не требует внимания") { t.Fatalf("a failed source must not read as all-clear, got %q", reply) } for _, want := range []string{"src_llamacpp", "src_imap"} { if !strings.Contains(reply, want) { t.Errorf("reply names no %s: %q", want, reply) } } if strings.Contains(reply, "src_ntfy") { t.Errorf("the healthy source is named as a problem: %q", reply) } } // A Praxis that polls nothing knows nothing, which is the state the box is in. func TestAttentionEmptyWithNoSourcesHedges(t *testing.T) { praxis := newFakePraxisWithSources(t, `[]`, `[]`) h := newPraxisTestHandler(t, praxis) reply := h.handlePraxisAct(context.Background(), praxisActDec("list_attention")) if strings.Contains(reply, "ничего не требует внимания") { t.Fatalf("a Praxis with no sources must not answer all-clear, got %q", reply) } if !strings.Contains(reply, "источник") { t.Errorf("reply does not say why she cannot tell: %q", reply) } } // The spec's own mechanism, which the deployed Praxis does not send yet: the // envelope's degraded array is believed without a second call. func TestAttentionDegradedEnvelopeIsReadWithoutASourcesCall(t *testing.T) { praxis := newFakePraxisWithSources(t, `{"items":[],"degraded":["src_metrics"]}`, `[{"source_id":"src_ntfy","health":"ok"}]`) h := newPraxisTestHandler(t, praxis) reply := h.handlePraxisAct(context.Background(), praxisActDec("list_attention")) if !strings.Contains(reply, "src_metrics") { t.Fatalf("the envelope's degraded source is not named: %q", reply) } for _, r := range praxis.Requests() { if r.Path == "/api/v1/sources" { t.Error("sources was read even though the response carried degraded") } } } // A sources endpoint that errors is not evidence of a fault: the attention call // itself succeeded, and hedging on it would make her permanently uncertain. func TestAttentionKeepsAllClearWhenSourcesCannotBeRead(t *testing.T) { praxis := newFakePraxisWithSources(t, `[]`, `[]`) praxis.SetRouteFault("/api/v1/sources", 500) h := newPraxisTestHandler(t, praxis) reply := h.handlePraxisAct(context.Background(), praxisActDec("list_attention")) if !strings.Contains(reply, "ничего не требует внимания") { t.Fatalf("an unreadable sources list should leave the answer alone, got %q", reply) } } // Both response shapes decode, because the spec says one and the box sends the // other. func TestPraxisAttentionDecodesBothShapes(t *testing.T) { var bare praxisAttention if err := json.Unmarshal([]byte(`[{"id":"item_1"}]`), &bare); err != nil { t.Fatalf("bare array: %v", err) } if len(bare.Items) != 1 || len(bare.Degraded) != 0 { t.Errorf("bare array decoded as %+v", bare) } var env praxisAttention if err := json.Unmarshal([]byte(`{"items":[{"id":"item_2"}],"degraded":["src_a"]}`), &env); err != nil { t.Fatalf("envelope: %v", err) } if len(env.Items) != 1 || len(env.Degraded) != 1 || env.Degraded[0] != "src_a" { t.Errorf("envelope decoded as %+v", env) } } // A source that reports no health at all counts as healthy. A Praxis that never // fills the field would otherwise make every quiet turn a hedge. func TestUnhealthySourcesTreatsAnAbsentHealthFieldAsHealthy(t *testing.T) { praxis := newFakePraxisWithSources(t, `[]`, `[{"source_id":"src_a"},{"id":"src_b","health":"stale"}]`) bad, total, err := newPraxisClient(praxis.URL).UnhealthySources(context.Background()) if err != nil { t.Fatalf("UnhealthySources: %v", err) } if total != 2 { t.Errorf("total = %d, want 2", total) } if len(bad) != 1 || bad[0] != "src_b" { t.Errorf("bad = %v, want [src_b]", bad) } }