244 lines
7.9 KiB
Go
244 lines
7.9 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/config"
|
|
)
|
|
|
|
func TestOpsServiceListSystemLogs_DefaultClampAndSuccess(t *testing.T) {
|
|
var gotFilter *OpsSystemLogFilter
|
|
repo := &opsRepoMock{
|
|
ListSystemLogsFn: func(ctx context.Context, filter *OpsSystemLogFilter) (*OpsSystemLogList, error) {
|
|
gotFilter = filter
|
|
return &OpsSystemLogList{
|
|
Logs: []*OpsSystemLog{{ID: 1, Level: "warn", Message: "x"}},
|
|
Total: 1,
|
|
Page: filter.Page,
|
|
PageSize: filter.PageSize,
|
|
}, nil
|
|
},
|
|
}
|
|
svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
|
|
out, err := svc.ListSystemLogs(context.Background(), &OpsSystemLogFilter{
|
|
Page: 0,
|
|
PageSize: 999,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ListSystemLogs() error: %v", err)
|
|
}
|
|
if gotFilter == nil {
|
|
t.Fatalf("expected repository to receive filter")
|
|
}
|
|
if gotFilter.Page != 1 || gotFilter.PageSize != 200 {
|
|
t.Fatalf("filter normalized unexpectedly: page=%d pageSize=%d", gotFilter.Page, gotFilter.PageSize)
|
|
}
|
|
if out.Total != 1 || len(out.Logs) != 1 {
|
|
t.Fatalf("unexpected result: %+v", out)
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceListSystemLogs_MonitoringDisabled(t *testing.T) {
|
|
svc := NewOpsService(
|
|
&opsRepoMock{},
|
|
nil,
|
|
&config.Config{Ops: config.OpsConfig{Enabled: false}},
|
|
nil, nil, nil, nil, nil, nil, nil, nil,
|
|
)
|
|
_, err := svc.ListSystemLogs(context.Background(), &OpsSystemLogFilter{})
|
|
if err == nil {
|
|
t.Fatalf("expected disabled error")
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceListSystemLogs_NilRepoReturnsEmpty(t *testing.T) {
|
|
svc := NewOpsService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
out, err := svc.ListSystemLogs(context.Background(), nil)
|
|
if err != nil {
|
|
t.Fatalf("ListSystemLogs() error: %v", err)
|
|
}
|
|
if out == nil || out.Page != 1 || out.PageSize != 50 || out.Total != 0 || len(out.Logs) != 0 {
|
|
t.Fatalf("unexpected nil-repo result: %+v", out)
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceListSystemLogs_RepoErrorMapped(t *testing.T) {
|
|
repo := &opsRepoMock{
|
|
ListSystemLogsFn: func(ctx context.Context, filter *OpsSystemLogFilter) (*OpsSystemLogList, error) {
|
|
return nil, errors.New("db down")
|
|
},
|
|
}
|
|
svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
_, err := svc.ListSystemLogs(context.Background(), &OpsSystemLogFilter{})
|
|
if err == nil {
|
|
t.Fatalf("expected mapped internal error")
|
|
}
|
|
if !strings.Contains(err.Error(), "OPS_SYSTEM_LOG_LIST_FAILED") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceCleanupSystemLogs_SuccessAndAudit(t *testing.T) {
|
|
var audit *OpsSystemLogCleanupAudit
|
|
repo := &opsRepoMock{
|
|
DeleteSystemLogsFn: func(ctx context.Context, filter *OpsSystemLogCleanupFilter) (int64, error) {
|
|
return 3, nil
|
|
},
|
|
InsertSystemLogCleanupAuditFn: func(ctx context.Context, input *OpsSystemLogCleanupAudit) error {
|
|
audit = input
|
|
return nil
|
|
},
|
|
}
|
|
svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
userID := int64(7)
|
|
now := time.Now().UTC()
|
|
filter := &OpsSystemLogCleanupFilter{
|
|
StartTime: &now,
|
|
Level: "warn",
|
|
RequestID: "req-1",
|
|
ClientRequestID: "creq-1",
|
|
UserID: &userID,
|
|
Query: "timeout",
|
|
}
|
|
|
|
deleted, err := svc.CleanupSystemLogs(context.Background(), filter, 99)
|
|
if err != nil {
|
|
t.Fatalf("CleanupSystemLogs() error: %v", err)
|
|
}
|
|
if deleted != 3 {
|
|
t.Fatalf("deleted=%d, want 3", deleted)
|
|
}
|
|
if audit == nil {
|
|
t.Fatalf("expected cleanup audit")
|
|
}
|
|
if !strings.Contains(audit.Conditions, `"client_request_id":"creq-1"`) {
|
|
t.Fatalf("audit conditions should include client_request_id: %s", audit.Conditions)
|
|
}
|
|
if !strings.Contains(audit.Conditions, `"user_id":7`) {
|
|
t.Fatalf("audit conditions should include user_id: %s", audit.Conditions)
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceCleanupSystemLogs_RepoUnavailableAndInvalidOperator(t *testing.T) {
|
|
svc := NewOpsService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
if _, err := svc.CleanupSystemLogs(context.Background(), &OpsSystemLogCleanupFilter{RequestID: "r"}, 1); err == nil {
|
|
t.Fatalf("expected repo unavailable error")
|
|
}
|
|
|
|
svc = NewOpsService(&opsRepoMock{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
if _, err := svc.CleanupSystemLogs(context.Background(), &OpsSystemLogCleanupFilter{RequestID: "r"}, 0); err == nil {
|
|
t.Fatalf("expected invalid operator error")
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceCleanupSystemLogs_FilterRequired(t *testing.T) {
|
|
repo := &opsRepoMock{
|
|
DeleteSystemLogsFn: func(ctx context.Context, filter *OpsSystemLogCleanupFilter) (int64, error) {
|
|
return 0, errors.New("cleanup requires at least one filter condition")
|
|
},
|
|
}
|
|
svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
_, err := svc.CleanupSystemLogs(context.Background(), &OpsSystemLogCleanupFilter{}, 1)
|
|
if err == nil {
|
|
t.Fatalf("expected filter required error")
|
|
}
|
|
if !strings.Contains(strings.ToLower(err.Error()), "filter") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceCleanupSystemLogs_InvalidRange(t *testing.T) {
|
|
repo := &opsRepoMock{}
|
|
svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
start := time.Now().UTC()
|
|
end := start.Add(-time.Hour)
|
|
_, err := svc.CleanupSystemLogs(context.Background(), &OpsSystemLogCleanupFilter{
|
|
StartTime: &start,
|
|
EndTime: &end,
|
|
}, 1)
|
|
if err == nil {
|
|
t.Fatalf("expected invalid range error")
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceCleanupSystemLogs_NoRowsAndInternalError(t *testing.T) {
|
|
repo := &opsRepoMock{
|
|
DeleteSystemLogsFn: func(ctx context.Context, filter *OpsSystemLogCleanupFilter) (int64, error) {
|
|
return 0, sql.ErrNoRows
|
|
},
|
|
}
|
|
svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
deleted, err := svc.CleanupSystemLogs(context.Background(), &OpsSystemLogCleanupFilter{
|
|
RequestID: "req-1",
|
|
}, 1)
|
|
if err != nil || deleted != 0 {
|
|
t.Fatalf("expected no rows shortcut, deleted=%d err=%v", deleted, err)
|
|
}
|
|
|
|
repo.DeleteSystemLogsFn = func(ctx context.Context, filter *OpsSystemLogCleanupFilter) (int64, error) {
|
|
return 0, errors.New("boom")
|
|
}
|
|
if _, err := svc.CleanupSystemLogs(context.Background(), &OpsSystemLogCleanupFilter{
|
|
RequestID: "req-1",
|
|
}, 1); err == nil {
|
|
t.Fatalf("expected internal cleanup error")
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceCleanupSystemLogs_AuditFailureIgnored(t *testing.T) {
|
|
repo := &opsRepoMock{
|
|
DeleteSystemLogsFn: func(ctx context.Context, filter *OpsSystemLogCleanupFilter) (int64, error) {
|
|
return 5, nil
|
|
},
|
|
InsertSystemLogCleanupAuditFn: func(ctx context.Context, input *OpsSystemLogCleanupAudit) error {
|
|
return errors.New("audit down")
|
|
},
|
|
}
|
|
svc := NewOpsService(repo, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
deleted, err := svc.CleanupSystemLogs(context.Background(), &OpsSystemLogCleanupFilter{
|
|
RequestID: "r1",
|
|
}, 1)
|
|
if err != nil || deleted != 5 {
|
|
t.Fatalf("audit failure should not break cleanup, deleted=%d err=%v", deleted, err)
|
|
}
|
|
}
|
|
|
|
func TestMarshalSystemLogCleanupConditions_NilAndMarshalError(t *testing.T) {
|
|
if got := marshalSystemLogCleanupConditions(nil); got != "{}" {
|
|
t.Fatalf("nil filter should return {}, got %s", got)
|
|
}
|
|
|
|
now := time.Now().UTC()
|
|
userID := int64(1)
|
|
filter := &OpsSystemLogCleanupFilter{
|
|
StartTime: &now,
|
|
EndTime: &now,
|
|
UserID: &userID,
|
|
}
|
|
got := marshalSystemLogCleanupConditions(filter)
|
|
if !strings.Contains(got, `"start_time"`) || !strings.Contains(got, `"user_id":1`) {
|
|
t.Fatalf("unexpected marshal payload: %s", got)
|
|
}
|
|
}
|
|
|
|
func TestOpsServiceGetSystemLogSinkHealth(t *testing.T) {
|
|
svc := NewOpsService(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
|
|
health := svc.GetSystemLogSinkHealth()
|
|
if health.QueueCapacity != 0 || health.QueueDepth != 0 {
|
|
t.Fatalf("unexpected health for nil sink: %+v", health)
|
|
}
|
|
|
|
sink := NewOpsSystemLogSink(&opsRepoMock{})
|
|
svc = NewOpsService(&opsRepoMock{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, sink)
|
|
health = svc.GetSystemLogSinkHealth()
|
|
if health.QueueCapacity <= 0 {
|
|
t.Fatalf("expected non-zero queue capacity: %+v", health)
|
|
}
|
|
}
|