mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 18:41:30 +00:00
29b55bfd01
CompletedAt was set to Now()-1h which falls on "yesterday" when CI runs near midnight UTC, causing the date bucket lookup to miss. Use Now() directly since the test only needs jobs completed "today". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
250 lines
8.1 KiB
Go
250 lines
8.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/shankar0123/certctl/internal/domain"
|
|
)
|
|
|
|
func newTestStatsService() (*StatsService, *mockCertRepo, *mockJobRepo, *mockAgentRepo) {
|
|
certRepo := &mockCertRepo{Certs: make(map[string]*domain.ManagedCertificate)}
|
|
jobRepo := newMockJobRepository()
|
|
agentRepo := newMockAgentRepository()
|
|
svc := NewStatsService(certRepo, jobRepo, agentRepo)
|
|
return svc, certRepo, jobRepo, agentRepo
|
|
}
|
|
|
|
func TestGetDashboardSummary_Empty(t *testing.T) {
|
|
svc, _, _, _ := newTestStatsService()
|
|
result, err := svc.GetDashboardSummary(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
summary, ok := result.(*DashboardSummary)
|
|
if !ok {
|
|
t.Fatal("expected *DashboardSummary")
|
|
}
|
|
if summary.TotalCertificates != 0 {
|
|
t.Errorf("expected 0 total certs, got %d", summary.TotalCertificates)
|
|
}
|
|
if summary.TotalAgents != 0 {
|
|
t.Errorf("expected 0 total agents, got %d", summary.TotalAgents)
|
|
}
|
|
}
|
|
|
|
func TestGetDashboardSummary_WithData(t *testing.T) {
|
|
svc, certRepo, jobRepo, agentRepo := newTestStatsService()
|
|
|
|
now := time.Now()
|
|
tenDays := now.AddDate(0, 0, 10)
|
|
pastDate := now.AddDate(0, 0, -5)
|
|
futureDate := now.AddDate(0, 0, 60)
|
|
|
|
// Add certificates
|
|
certRepo.Certs["mc-active"] = &domain.ManagedCertificate{ID: "mc-active", Status: domain.CertificateStatusActive, ExpiresAt: futureDate}
|
|
certRepo.Certs["mc-expiring"] = &domain.ManagedCertificate{ID: "mc-expiring", Status: domain.CertificateStatusActive, ExpiresAt: tenDays}
|
|
certRepo.Certs["mc-expired"] = &domain.ManagedCertificate{ID: "mc-expired", Status: domain.CertificateStatusExpired, ExpiresAt: pastDate}
|
|
certRepo.Certs["mc-revoked"] = &domain.ManagedCertificate{ID: "mc-revoked", Status: domain.CertificateStatusRevoked}
|
|
|
|
// Add agents
|
|
recentHeartbeat := now.Add(-2 * time.Minute)
|
|
oldHeartbeat := now.Add(-10 * time.Minute)
|
|
agentRepo.AddAgent(&domain.Agent{ID: "a-1", LastHeartbeatAt: &recentHeartbeat})
|
|
agentRepo.AddAgent(&domain.Agent{ID: "a-2", LastHeartbeatAt: &oldHeartbeat})
|
|
agentRepo.AddAgent(&domain.Agent{ID: "a-3"}) // no heartbeat
|
|
|
|
// Add jobs
|
|
jobRepo.AddJob(&domain.Job{ID: "j-1", Status: domain.JobStatusPending})
|
|
jobRepo.AddJob(&domain.Job{ID: "j-2", Status: domain.JobStatusCompleted})
|
|
jobRepo.AddJob(&domain.Job{ID: "j-3", Status: domain.JobStatusFailed})
|
|
|
|
result, err := svc.GetDashboardSummary(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
summary := result.(*DashboardSummary)
|
|
|
|
if summary.TotalCertificates != 4 {
|
|
t.Errorf("expected 4 total certs, got %d", summary.TotalCertificates)
|
|
}
|
|
if summary.ExpiringCertificates != 1 {
|
|
t.Errorf("expected 1 expiring, got %d", summary.ExpiringCertificates)
|
|
}
|
|
if summary.ExpiredCertificates != 1 {
|
|
t.Errorf("expected 1 expired, got %d", summary.ExpiredCertificates)
|
|
}
|
|
if summary.RevokedCertificates != 1 {
|
|
t.Errorf("expected 1 revoked, got %d", summary.RevokedCertificates)
|
|
}
|
|
if summary.TotalAgents != 3 {
|
|
t.Errorf("expected 3 total agents, got %d", summary.TotalAgents)
|
|
}
|
|
if summary.ActiveAgents != 1 {
|
|
t.Errorf("expected 1 active agent, got %d", summary.ActiveAgents)
|
|
}
|
|
if summary.OfflineAgents != 2 {
|
|
t.Errorf("expected 2 offline agents, got %d", summary.OfflineAgents)
|
|
}
|
|
if summary.PendingJobs != 1 {
|
|
t.Errorf("expected 1 pending job, got %d", summary.PendingJobs)
|
|
}
|
|
if summary.CompleteJobs != 1 {
|
|
t.Errorf("expected 1 complete job, got %d", summary.CompleteJobs)
|
|
}
|
|
if summary.FailedJobs != 1 {
|
|
t.Errorf("expected 1 failed job, got %d", summary.FailedJobs)
|
|
}
|
|
}
|
|
|
|
func TestGetDashboardSummary_CertRepoError(t *testing.T) {
|
|
svc, certRepo, _, _ := newTestStatsService()
|
|
certRepo.ListErr = errNotFound
|
|
_, err := svc.GetDashboardSummary(context.Background())
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|
|
|
|
func TestGetCertificatesByStatus_Empty(t *testing.T) {
|
|
svc, _, _, _ := newTestStatsService()
|
|
result, err := svc.GetCertificatesByStatus(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
counts := result.([]CertificateStatusCount)
|
|
if len(counts) != 0 {
|
|
t.Errorf("expected 0 status counts, got %d", len(counts))
|
|
}
|
|
}
|
|
|
|
func TestGetCertificatesByStatus_WithData(t *testing.T) {
|
|
svc, certRepo, _, _ := newTestStatsService()
|
|
future := time.Now().AddDate(0, 0, 60)
|
|
certRepo.Certs["mc-1"] = &domain.ManagedCertificate{ID: "mc-1", Status: domain.CertificateStatusActive, ExpiresAt: future}
|
|
certRepo.Certs["mc-2"] = &domain.ManagedCertificate{ID: "mc-2", Status: domain.CertificateStatusRevoked}
|
|
|
|
result, err := svc.GetCertificatesByStatus(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
counts := result.([]CertificateStatusCount)
|
|
if len(counts) < 2 {
|
|
t.Errorf("expected at least 2 status counts, got %d", len(counts))
|
|
}
|
|
}
|
|
|
|
func TestGetExpirationTimeline_Default(t *testing.T) {
|
|
svc, certRepo, _, _ := newTestStatsService()
|
|
expiresIn10d := time.Now().AddDate(0, 0, 10)
|
|
certRepo.Certs["mc-1"] = &domain.ManagedCertificate{ID: "mc-1", ExpiresAt: expiresIn10d}
|
|
|
|
result, err := svc.GetExpirationTimeline(context.Background(), 30)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
buckets := result.([]ExpirationBucket)
|
|
if len(buckets) != 30 {
|
|
t.Errorf("expected 30 buckets, got %d", len(buckets))
|
|
}
|
|
// At least one bucket should have count > 0
|
|
hasNonZero := false
|
|
for _, b := range buckets {
|
|
if b.Count > 0 {
|
|
hasNonZero = true
|
|
break
|
|
}
|
|
}
|
|
if !hasNonZero {
|
|
t.Error("expected at least one non-zero bucket")
|
|
}
|
|
}
|
|
|
|
func TestGetExpirationTimeline_InvalidDays(t *testing.T) {
|
|
svc, _, _, _ := newTestStatsService()
|
|
result, err := svc.GetExpirationTimeline(context.Background(), -1)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
buckets := result.([]ExpirationBucket)
|
|
if len(buckets) != 30 {
|
|
t.Errorf("expected default 30 buckets for invalid days, got %d", len(buckets))
|
|
}
|
|
}
|
|
|
|
func TestGetJobStats_Empty(t *testing.T) {
|
|
svc, _, _, _ := newTestStatsService()
|
|
result, err := svc.GetJobStats(context.Background(), 7)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
points := result.([]JobTrendDataPoint)
|
|
if len(points) != 7 {
|
|
t.Errorf("expected 7 data points, got %d", len(points))
|
|
}
|
|
}
|
|
|
|
func TestGetJobStats_WithData(t *testing.T) {
|
|
svc, _, jobRepo, _ := newTestStatsService()
|
|
completedAt := time.Now()
|
|
jobRepo.AddJob(&domain.Job{ID: "j-1", Status: domain.JobStatusCompleted, CompletedAt: &completedAt})
|
|
jobRepo.AddJob(&domain.Job{ID: "j-2", Status: domain.JobStatusFailed, CompletedAt: &completedAt})
|
|
|
|
result, err := svc.GetJobStats(context.Background(), 7)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
points := result.([]JobTrendDataPoint)
|
|
|
|
// The last data point should have today's data
|
|
todayPoint := points[len(points)-1]
|
|
if todayPoint.CompletedCount != 1 {
|
|
t.Errorf("expected 1 completed today, got %d", todayPoint.CompletedCount)
|
|
}
|
|
if todayPoint.FailedCount != 1 {
|
|
t.Errorf("expected 1 failed today, got %d", todayPoint.FailedCount)
|
|
}
|
|
if todayPoint.SuccessRate != 50.0 {
|
|
t.Errorf("expected 50%% success rate, got %.1f%%", todayPoint.SuccessRate)
|
|
}
|
|
}
|
|
|
|
func TestGetIssuanceRate_Empty(t *testing.T) {
|
|
svc, _, _, _ := newTestStatsService()
|
|
result, err := svc.GetIssuanceRate(context.Background(), 7)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
points := result.([]IssuanceRateDataPoint)
|
|
if len(points) != 7 {
|
|
t.Errorf("expected 7 data points, got %d", len(points))
|
|
}
|
|
}
|
|
|
|
func TestGetIssuanceRate_WithData(t *testing.T) {
|
|
svc, certRepo, _, _ := newTestStatsService()
|
|
certRepo.Certs["mc-1"] = &domain.ManagedCertificate{ID: "mc-1", CreatedAt: time.Now()}
|
|
certRepo.Certs["mc-2"] = &domain.ManagedCertificate{ID: "mc-2", CreatedAt: time.Now()}
|
|
|
|
result, err := svc.GetIssuanceRate(context.Background(), 7)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
points := result.([]IssuanceRateDataPoint)
|
|
|
|
todayPoint := points[len(points)-1]
|
|
if todayPoint.IssuedCount != 2 {
|
|
t.Errorf("expected 2 issued today, got %d", todayPoint.IssuedCount)
|
|
}
|
|
}
|
|
|
|
func TestGetIssuanceRate_RepoError(t *testing.T) {
|
|
svc, certRepo, _, _ := newTestStatsService()
|
|
certRepo.ListErr = errNotFound
|
|
_, err := svc.GetIssuanceRate(context.Background(), 7)
|
|
if err == nil {
|
|
t.Fatal("expected error")
|
|
}
|
|
}
|