test(handlers): #1286 rows.Err + query-error coverage for admin_delegations.go #2824
@@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -304,6 +305,104 @@ func TestAdminDelegations_Stats_EmptyTable(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminDelegations_List_RowsErr_PartialResults(t *testing.T) {
|
||||
mock := setupTestDB(t)
|
||||
h := NewAdminDelegationsHandler(nil)
|
||||
|
||||
now := time.Now()
|
||||
rows := sqlmock.NewRows([]string{
|
||||
"delegation_id", "caller_id", "callee_id", "task_preview",
|
||||
"status", "last_heartbeat", "deadline", "result_preview", "error_detail",
|
||||
"retry_count", "created_at", "updated_at",
|
||||
}).
|
||||
AddRow("deleg-1", "caller-1", "callee-1", "task 1", "queued", now, now.Add(time.Hour), nil, nil, 0, now, now).
|
||||
AddRow("deleg-2", "caller-2", "callee-2", "task 2", "dispatched", now, now.Add(time.Hour), nil, nil, 0, now, now).
|
||||
RowError(1, errors.New("storage engine fault"))
|
||||
|
||||
mock.ExpectQuery(`SELECT delegation_id`).
|
||||
WithArgs("queued", "dispatched", "in_progress", 100).
|
||||
WillReturnRows(rows)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest("GET", "/admin/delegations", nil)
|
||||
h.List(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
var body struct {
|
||||
Delegations []any `json:"delegations"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
if body.Count != 1 {
|
||||
t.Errorf("expected 1 partial result, got %d", body.Count)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminDelegations_Stats_QueryError_Returns500(t *testing.T) {
|
||||
mock := setupTestDB(t)
|
||||
h := NewAdminDelegationsHandler(nil)
|
||||
|
||||
mock.ExpectQuery(`SELECT status, COUNT\(\*\) FROM delegations GROUP BY status`).
|
||||
WillReturnError(errors.New("db down"))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest("GET", "/admin/delegations/stats", nil)
|
||||
h.Stats(c)
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("expected 500, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminDelegations_Stats_RowsErr_Returns200(t *testing.T) {
|
||||
// rows.Err() after the scan loop is non-fatal — the handler logs and
|
||||
// returns whatever counts were successfully read. The exact row that
|
||||
// sqlmock loses depends on driver internals, so we only assert the
|
||||
// happy-path row and the HTTP status, not the presence/absence of the
|
||||
// row after the injected error.
|
||||
mock := setupTestDB(t)
|
||||
h := NewAdminDelegationsHandler(nil)
|
||||
|
||||
rows := sqlmock.NewRows([]string{"status", "count"}).
|
||||
AddRow("in_progress", 7).
|
||||
AddRow("completed", 130).
|
||||
RowError(1, errors.New("storage engine fault"))
|
||||
|
||||
mock.ExpectQuery(`SELECT status, COUNT\(\*\) FROM delegations GROUP BY status`).
|
||||
WillReturnRows(rows)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest("GET", "/admin/delegations/stats", nil)
|
||||
h.Stats(c)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
|
||||
}
|
||||
var stats map[string]int
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &stats); err != nil {
|
||||
t.Fatalf("parse: %v", err)
|
||||
}
|
||||
if stats["in_progress"] != 7 {
|
||||
t.Errorf("in_progress: expected 7, got %d", stats["in_progress"])
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unmet: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// statusFilters is a contract surface — every key here is documented in
|
||||
// the endpoint comment + accepted by the validator. Pin it.
|
||||
func TestStatusFiltersTableShape(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user