mavweb: add notifications history page at /notifications

RecentNudges IPC method, store adapter, dispatch, and client proxy.
Web UI at /notifications showing recent nudge history with color-coded
outcomes, nav links from /dash and /history.
This commit is contained in:
kami
2026-07-05 13:20:24 +04:00
parent 85013f7b8b
commit 5c34fb14f9
5 changed files with 135 additions and 2 deletions
+58
View File
@@ -52,6 +52,9 @@ type fakeCore struct {
revertNewID int64
revertErr error
// for handleNotifications tests
nudgesErr error
// for handleHistory tests
historyFacts []ipc.Fact
historyErr error
@@ -115,6 +118,9 @@ func (f *fakeCore) RecentFacts(_ context.Context, _ int) ([]ipc.Fact, error) {
}
func (f *fakeCore) RecentNudges(_ context.Context, _ int) ([]ipc.Nudge, error) {
if f.nudgesErr != nil {
return nil, f.nudgesErr
}
if f.dashErr != nil {
return nil, f.dashErr
}
@@ -549,6 +555,58 @@ func TestHandleDash(t *testing.T) {
})
}
// --- handleNotifications ---
func TestHandleNotifications_NilCore_503(t *testing.T) {
t.Parallel()
rr := httptest.NewRecorder()
handleNotifications(rr, httptest.NewRequest(http.MethodGet, "/notifications", nil), nil)
if rr.Code != http.StatusServiceUnavailable {
t.Errorf("status = %d, want 503", rr.Code)
}
}
func TestHandleNotifications_CoreError_502(t *testing.T) {
t.Parallel()
core := &fakeCore{nudgesErr: ipc.ErrNudgeNotFound}
rr := httptest.NewRecorder()
handleNotifications(rr, httptest.NewRequest(http.MethodGet, "/notifications", nil), core)
if rr.Code != http.StatusBadGateway {
t.Errorf("status = %d, want 502", rr.Code)
}
}
func TestHandleNotifications_Renders(t *testing.T) {
t.Parallel()
core := &fakeCore{
nudges: []ipc.Nudge{
{Rule: "test-rule", Channel: "telegram", Message: "hello world", Outcome: "pending"},
{Rule: "other-rule", Channel: "voice", Message: "something happened", Outcome: "acted"},
},
}
rr := httptest.NewRecorder()
handleNotifications(rr, httptest.NewRequest(http.MethodGet, "/notifications", nil), core)
if rr.Code != http.StatusOK {
t.Fatalf("status = %d, want 200; body=%s", rr.Code, rr.Body.String())
}
body := rr.Body.String()
if !strings.Contains(body, "test-rule") {
t.Error("rendered output missing rule name")
}
if !strings.Contains(body, "telegram") {
t.Error("rendered output missing channel")
}
if !strings.Contains(body, "pending") {
t.Error("rendered output missing outcome")
}
if !strings.Contains(body, "hello world") {
t.Error("rendered output missing message")
}
if !strings.Contains(body, "other-rule") {
t.Error("rendered output missing second rule")
}
}
// --- handleHistory ---
func TestHandleHistory(t *testing.T) {