|
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"strings"
|
|
|
|
|
"tera-clone/services/tera-api/internal/adapters/mock"
|
|
|
|
|
"tera-clone/services/tera-api/internal/domain"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestSystemClearRequiresConfirmationAndDeletesAuditProjection(t *testing.T) {
|
|
|
|
|
s := mock.NewStore(fixedClock)
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
_, err := s.RecordSystemLog(ctx, domain.SystemLogEvent{Severity: "info", Source: "test", Message: "delete ordinary"})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
id, err := s.RecordAudit(ctx, domain.AuditEvent{Action: "auth.login", Target: "login", Result: "failed", Message: "preserve audit"})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
h := NewRouter(s)
|
|
|
|
|
for _, tc := range []struct {
|
|
|
|
|
actor, body string
|
|
|
|
|
status int
|
|
|
|
|
}{{"", `{"confirmation":"SYSTEM"}`, 401}, {"mock-port-operator", `{"confirmation":"SYSTEM"}`, 403}, {"mock-admin", `{}`, 400}, {"mock-admin", `{"confirmation":"system"}`, 400}, {"mock-admin", `{"confirmation":"SYSTEM","class":"audit"}`, 400}} {
|
|
|
|
|
r := httptest.NewRequest(http.MethodPost, "/api/v1/logs/system/clear", strings.NewReader(tc.body))
|
|
|
|
|
if tc.actor != "" {
|
|
|
|
|
r.Header.Set("X-Mock-Actor", tc.actor)
|
|
|
|
|
}
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
h.ServeHTTP(w, r)
|
|
|
|
|
if w.Code != tc.status {
|
|
|
|
|
t.Fatalf("clear %s: %d %s", tc.actor, w.Code, w.Body.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result := getJSONAs(t, h, http.MethodPost, "/api/v1/logs/system/clear", `{"confirmation":"SYSTEM"}`, "mock-admin")
|
|
|
|
|
if result["deleted"].(float64) < 2 {
|
|
|
|
|
t.Fatalf("deleted=%v", result)
|
|
|
|
|
}
|
|
|
|
|
rows, err := s.SystemLogEvents(ctx, domain.SystemLogFilter{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
found := false
|
|
|
|
|
for _, row := range rows {
|
|
|
|
|
if row.Message == "delete ordinary" {
|
|
|
|
|
t.Fatal("ordinary event survived")
|
|
|
|
|
}
|
|
|
|
|
if row.AuditID == id {
|
|
|
|
|
found = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if found {
|
|
|
|
|
t.Fatal("old audit projection survived")
|
|
|
|
|
}
|
|
|
|
|
audits, err := s.AuditEvents(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
found = false
|
|
|
|
|
for _, event := range audits {
|
|
|
|
|
if event.Action == "log.system.clear" && event.Result == "succeeded" {
|
|
|
|
|
found = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !found {
|
|
|
|
|
t.Fatal("deletion audit missing")
|
|
|
|
|
}
|
|
|
|
|
}
|