Files
certctl/internal/service/agent_round_out_test.go
T
shankar0123 7cb453a336 chore(fmt): repo-wide gofmt -w sweep — close drift surfaced by ci-pipeline-cleanup Phase 4
Mechanical reformat. The new 'gofmt drift' CI step (added in
ci-pipeline-cleanup Phase 4, commit 0f205a8) surfaced 111 files
with accumulated gofmt drift across cmd/, internal/, and deploy/test/.

Each file's diff is gofmt-standard: whitespace adjustments, intra-
group import sorting (alphabetical by import path within blank-line-
separated groups), and struct-tag column alignment. No semantic
changes — verified via 'git diff --ignore-all-space' which shows only
the line-position deltas from import reordering.

The gate stays in place after this commit. Going forward it catches
gofmt drift at PR time.
2026-04-30 22:33:57 +00:00

172 lines
5.4 KiB
Go

package service
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/shankar0123/certctl/internal/domain"
)
// Bundle N.C-extended: agent service-layer round-out (target +5pp).
// Targets uncovered handler-interface delegators on AgentService:
// GetAgent, RegisterAgent, CSRSubmit, CSRSubmitForCert, GetWork,
// GetWorkWithTargets, UpdateJobStatus, CertificatePickup, plus
// SetProfileRepo / GetCertificateForAgent / GetAgentByAPIKey.
func newTestAgentSvc(t *testing.T) (*AgentService, *mockAgentRepo, *mockCertRepo, *mockJobRepo, *mockTargetRepo) {
t.Helper()
agentRepo := &mockAgentRepo{
Agents: make(map[string]*domain.Agent),
HeartbeatUpdates: make(map[string]time.Time),
}
certRepo := &mockCertRepo{
Certs: make(map[string]*domain.ManagedCertificate),
Versions: make(map[string][]*domain.CertificateVersion),
}
jobRepo := &mockJobRepo{
Jobs: make(map[string]*domain.Job),
StatusUpdates: make(map[string]domain.JobStatus),
}
targetRepo := &mockTargetRepo{
Targets: make(map[string]*domain.DeploymentTarget),
}
auditRepo := &mockAuditRepo{}
auditService := NewAuditService(auditRepo)
issuerRegistry := NewIssuerRegistry(nil)
svc := NewAgentService(agentRepo, certRepo, jobRepo, targetRepo, auditService, issuerRegistry, nil)
return svc, agentRepo, certRepo, jobRepo, targetRepo
}
func TestAgentService_GetAgent_DelegatesToRepo(t *testing.T) {
svc, repo, _, _, _ := newTestAgentSvc(t)
repo.Agents["a-1"] = &domain.Agent{ID: "a-1", Name: "test"}
got, err := svc.GetAgent(context.Background(), "a-1")
if err != nil {
t.Fatalf("GetAgent: %v", err)
}
if got.Name != "test" {
t.Errorf("expected name=test, got %q", got.Name)
}
}
func TestAgentService_RegisterAgent_PopulatesIDStatusKey(t *testing.T) {
svc, _, _, _, _ := newTestAgentSvc(t)
got, err := svc.RegisterAgent(context.Background(), domain.Agent{Name: "fresh"})
if err != nil {
t.Fatalf("RegisterAgent: %v", err)
}
if got.ID == "" {
t.Errorf("expected ID populated")
}
if got.Status != domain.AgentStatusOnline {
t.Errorf("expected Online status, got %s", got.Status)
}
if got.APIKeyHash == "" {
t.Errorf("expected APIKeyHash populated")
}
if got.RegisteredAt.IsZero() {
t.Errorf("expected RegisteredAt populated")
}
}
func TestAgentService_RegisterAgent_RepoError(t *testing.T) {
svc, repo, _, _, _ := newTestAgentSvc(t)
repo.CreateErr = errors.New("conflict")
_, err := svc.RegisterAgent(context.Background(), domain.Agent{Name: "x"})
if err == nil || !strings.Contains(err.Error(), "register agent") {
t.Errorf("expected register-agent error wrapper, got %v", err)
}
}
func TestAgentService_GetWork_NoJobs(t *testing.T) {
svc, repo, _, _, _ := newTestAgentSvc(t)
repo.Agents["a-1"] = &domain.Agent{ID: "a-1", Status: domain.AgentStatusOnline}
got, err := svc.GetWork(context.Background(), "a-1")
if err != nil {
t.Fatalf("GetWork: %v", err)
}
if len(got) != 0 {
t.Errorf("expected 0 jobs, got %d", len(got))
}
}
func TestAgentService_GetWorkWithTargets_NoJobs(t *testing.T) {
svc, repo, _, _, _ := newTestAgentSvc(t)
repo.Agents["a-1"] = &domain.Agent{ID: "a-1", Status: domain.AgentStatusOnline}
got, err := svc.GetWorkWithTargets(context.Background(), "a-1")
if err != nil {
t.Fatalf("GetWorkWithTargets: %v", err)
}
if len(got) != 0 {
t.Errorf("expected 0 work items, got %d", len(got))
}
}
func TestAgentService_UpdateJobStatus_DelegatesToReportJobStatus(t *testing.T) {
svc, repo, _, jobRepo, _ := newTestAgentSvc(t)
repo.Agents["a-1"] = &domain.Agent{ID: "a-1", Status: domain.AgentStatusOnline}
jobRepo.Jobs["j-1"] = &domain.Job{
ID: "j-1",
AgentID: strPtr("a-1"),
Status: domain.JobStatusRunning,
}
err := svc.UpdateJobStatus(context.Background(), "a-1", "j-1", "Completed", "")
if err != nil {
t.Errorf("UpdateJobStatus: %v", err)
}
}
// Local strPtr to avoid colliding with other test files.
func strPtr(s string) *string { return &s }
func TestAgentService_CSRSubmit_NoCertID(t *testing.T) {
svc, _, _, _, _ := newTestAgentSvc(t)
// CSRSubmit calls SubmitCSR which performs validation. Pass an obviously
// invalid CSR to exercise the error path.
_, err := svc.CSRSubmit(context.Background(), "a-1", "not-a-csr")
if err == nil {
t.Errorf("expected SubmitCSR error to surface for invalid CSR")
}
}
func TestAgentService_CSRSubmitForCert_InvalidPEM(t *testing.T) {
svc, _, _, _, _ := newTestAgentSvc(t)
_, err := svc.CSRSubmitForCert(context.Background(), "a-1", "mc-1", "not-a-csr")
if err == nil {
t.Errorf("expected error for invalid CSR")
}
}
func TestAgentService_CertificatePickup_AgentNotFound(t *testing.T) {
svc, _, _, _, _ := newTestAgentSvc(t)
_, err := svc.CertificatePickup(context.Background(), "a-missing", "mc-1")
if err == nil {
t.Errorf("expected error for missing agent")
}
}
func TestAgentService_GetAgentByAPIKey_NotFound(t *testing.T) {
svc, _, _, _, _ := newTestAgentSvc(t)
_, err := svc.GetAgentByAPIKey(context.Background(), "no-such-key")
if err == nil {
t.Errorf("expected error for unknown API key")
}
}
func TestAgentService_GetCertificateForAgent_AgentNotFound(t *testing.T) {
svc, _, _, _, _ := newTestAgentSvc(t)
_, err := svc.GetCertificateForAgent(context.Background(), "a-missing", "mc-1")
if err == nil {
t.Errorf("expected error for missing agent")
}
}
func TestAgentService_SetProfileRepo_NoCrash(t *testing.T) {
svc, _, _, _, _ := newTestAgentSvc(t)
// SetProfileRepo accepts nil — confirm no panic.
svc.SetProfileRepo(nil)
}