mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 15:01:32 +00:00
8326d95210
Three new round-out test files targeting handler-interface delegators
on CertificateService + AgentService + IssuerHandler/HealthCheckHandler.
Coverage deltas
=================
internal/service: 70.5% -> 73.4% (+2.9pp; 17 new tests)
internal/api/handler: 79.4% -> 79.8% (+0.4pp; 4 new tests)
Service round-out tests (certificate_round_out_test.go, ~165 LoC)
=================
- GetCertificate (delegate-to-repo + NotFound)
- CreateCertificate (defaults populated + repo error)
- UpdateCertificate (patch merge + NotFound + repo error)
- ArchiveCertificate (delegate + repo error)
- GetCertificateVersions (pagination defaults + page-out-of-range +
repo error)
- SetJobRepo / SetKeygenMode (no-crash setters)
Service round-out tests (agent_round_out_test.go, ~140 LoC)
=================
- GetAgent (delegate)
- RegisterAgent (defaults populated + repo error)
- GetWork / GetWorkWithTargets (no-jobs path)
- UpdateJobStatus (delegate to ReportJobStatus)
- CSRSubmit / CSRSubmitForCert (invalid-CSR error)
- CertificatePickup (agent-not-found)
- GetAgentByAPIKey (unknown key)
- GetCertificateForAgent (missing agent)
- SetProfileRepo (no-crash)
Handler round-out tests (round_out_test.go, ~40 LoC)
=================
- NewIssuerHandlerWithLogger (logger wired through)
- UpdateHealthCheck dispatch arm with bad ID
- GetHealthCheckHistory dispatch arm with bad ID
Why partial
=================
M-002 / M-003 prescribed >=80%. Service at 73.4% and handler at 79.8%
miss the gate by 6.6pp / 0.2pp respectively. The remaining service
gap is in CSR-submit happy-path and large-population list-filter
flows that need deeper repo plumbing (3-4 hr more focused work).
The handler 0.2pp is in parseSignedDataForCSR (SCEP), DeleteHealthCheck,
AcknowledgeHealthCheck — needs repo fixtures.
These extensions are a meaningful step but don't fully close M-002
and M-003. Tracked as N.C-final follow-on; not blocking on a CI
floor at 73 / 79.
Audit deliverables
=================
- gap-backlog.md M-002, M-003: partial-strikethrough with progress
note + remaining-gap analysis
- extension-progress.md: N.C-extended marked PARTIAL
Closes (partial): M-002, M-003
Bundle: N.C-extended (Coverage Audit Extension)
196 lines
6.3 KiB
Go
196 lines
6.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/shankar0123/certctl/internal/domain"
|
|
)
|
|
|
|
// Bundle N.C-extended: service-layer round-out (70.5% → ≥80%).
|
|
// Targets the previously-uncovered handler-interface methods on
|
|
// CertificateService that delegate to the repo: GetCertificate,
|
|
// CreateCertificate, UpdateCertificate, ArchiveCertificate,
|
|
// GetCertificateVersions, SetJobRepo, SetKeygenMode,
|
|
// ListCertificatesWithFilter, TriggerDeployment.
|
|
|
|
func newTestCertSvc(t *testing.T) (*CertificateService, *mockCertRepo) {
|
|
t.Helper()
|
|
certRepo := &mockCertRepo{
|
|
Certs: make(map[string]*domain.ManagedCertificate),
|
|
Versions: make(map[string][]*domain.CertificateVersion),
|
|
}
|
|
auditRepo := &mockAuditRepo{}
|
|
auditService := NewAuditService(auditRepo)
|
|
svc := NewCertificateService(certRepo, nil, auditService)
|
|
return svc, certRepo
|
|
}
|
|
|
|
func TestCertificateService_GetCertificate_DelegatesToRepo(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.Certs["mc-1"] = &domain.ManagedCertificate{ID: "mc-1", Name: "x"}
|
|
got, err := svc.GetCertificate(context.Background(), "mc-1")
|
|
if err != nil {
|
|
t.Fatalf("GetCertificate: %v", err)
|
|
}
|
|
if got == nil || got.ID != "mc-1" {
|
|
t.Errorf("expected mc-1, got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_GetCertificate_NotFound(t *testing.T) {
|
|
svc, _ := newTestCertSvc(t)
|
|
_, err := svc.GetCertificate(context.Background(), "missing")
|
|
if err == nil {
|
|
t.Errorf("expected NotFound error")
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_CreateCertificate_PopulatesDefaults(t *testing.T) {
|
|
svc, _ := newTestCertSvc(t)
|
|
cert := domain.ManagedCertificate{Name: "no-id-no-status"}
|
|
got, err := svc.CreateCertificate(context.Background(), cert)
|
|
if err != nil {
|
|
t.Fatalf("CreateCertificate: %v", err)
|
|
}
|
|
if got.ID == "" {
|
|
t.Errorf("expected ID populated, got empty")
|
|
}
|
|
if got.Status == "" {
|
|
t.Errorf("expected default status populated")
|
|
}
|
|
if got.Tags == nil {
|
|
t.Errorf("expected Tags initialized to non-nil map")
|
|
}
|
|
if got.CreatedAt.IsZero() {
|
|
t.Errorf("expected CreatedAt populated")
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_CreateCertificate_RepoError(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.CreateErr = errors.New("db down")
|
|
_, err := svc.CreateCertificate(context.Background(), domain.ManagedCertificate{ID: "mc-x", Name: "x"})
|
|
if err == nil || !strings.Contains(err.Error(), "failed to create") {
|
|
t.Errorf("expected create-error wrapper, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_UpdateCertificate_MergesPatch(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.Certs["mc-u"] = &domain.ManagedCertificate{
|
|
ID: "mc-u",
|
|
Name: "old",
|
|
CommonName: "old.example.com",
|
|
Environment: "staging",
|
|
}
|
|
patch := domain.ManagedCertificate{
|
|
Name: "new",
|
|
CommonName: "new.example.com",
|
|
Environment: "prod",
|
|
SANs: []string{"new.example.com"},
|
|
OwnerID: "o-alice",
|
|
TeamID: "t-platform",
|
|
IssuerID: "iss-le",
|
|
}
|
|
got, err := svc.UpdateCertificate(context.Background(), "mc-u", patch)
|
|
if err != nil {
|
|
t.Fatalf("UpdateCertificate: %v", err)
|
|
}
|
|
if got.Name != "new" || got.CommonName != "new.example.com" || got.Environment != "prod" {
|
|
t.Errorf("expected merged fields, got %+v", got)
|
|
}
|
|
if got.OwnerID != "o-alice" || got.TeamID != "t-platform" {
|
|
t.Errorf("expected owner/team merged, got %s/%s", got.OwnerID, got.TeamID)
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_UpdateCertificate_NotFound(t *testing.T) {
|
|
svc, _ := newTestCertSvc(t)
|
|
_, err := svc.UpdateCertificate(context.Background(), "missing", domain.ManagedCertificate{Name: "x"})
|
|
if err == nil || !strings.Contains(err.Error(), "not found") {
|
|
t.Errorf("expected NotFound error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_UpdateCertificate_RepoUpdateError(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.Certs["mc-u"] = &domain.ManagedCertificate{ID: "mc-u", Name: "old"}
|
|
repo.UpdateErr = errors.New("constraint violation")
|
|
_, err := svc.UpdateCertificate(context.Background(), "mc-u", domain.ManagedCertificate{Name: "new"})
|
|
if err == nil || !strings.Contains(err.Error(), "failed to update") {
|
|
t.Errorf("expected update-error wrapper, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_ArchiveCertificate_DelegatesToRepo(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.Certs["mc-a"] = &domain.ManagedCertificate{ID: "mc-a"}
|
|
if err := svc.ArchiveCertificate(context.Background(), "mc-a"); err != nil {
|
|
t.Errorf("ArchiveCertificate: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_ArchiveCertificate_RepoError(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.ArchiveErr = errors.New("archive fail")
|
|
if err := svc.ArchiveCertificate(context.Background(), "mc-a"); err == nil {
|
|
t.Errorf("expected archive error to propagate")
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_GetCertificateVersions_PaginationDefaults(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
versions := []*domain.CertificateVersion{
|
|
{SerialNumber: "01"}, {SerialNumber: "02"}, {SerialNumber: "03"},
|
|
}
|
|
repo.ListVersionsResult = versions
|
|
repo.Versions["mc-v"] = versions
|
|
|
|
got, total, err := svc.GetCertificateVersions(context.Background(), "mc-v", 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("GetCertificateVersions: %v", err)
|
|
}
|
|
if total != 3 {
|
|
t.Errorf("expected total=3, got %d", total)
|
|
}
|
|
if len(got) != 3 {
|
|
t.Errorf("expected 3 versions returned, got %d", len(got))
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_GetCertificateVersions_PageOutOfRange(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.ListVersionsResult = []*domain.CertificateVersion{{SerialNumber: "01"}}
|
|
|
|
got, total, err := svc.GetCertificateVersions(context.Background(), "mc-v", 99, 50)
|
|
if err != nil {
|
|
t.Fatalf("GetCertificateVersions: %v", err)
|
|
}
|
|
if total != 1 {
|
|
t.Errorf("expected total=1, got %d", total)
|
|
}
|
|
if len(got) != 0 {
|
|
t.Errorf("expected 0 results for out-of-range page, got %d", len(got))
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_GetCertificateVersions_RepoError(t *testing.T) {
|
|
svc, repo := newTestCertSvc(t)
|
|
repo.ListVersionsErr = errors.New("list down")
|
|
_, _, err := svc.GetCertificateVersions(context.Background(), "mc-v", 1, 50)
|
|
if err == nil {
|
|
t.Errorf("expected versions-list error to propagate")
|
|
}
|
|
}
|
|
|
|
func TestCertificateService_SetJobRepo_SetKeygenMode_NoCrash(t *testing.T) {
|
|
svc, _ := newTestCertSvc(t)
|
|
// SetJobRepo accepts a repo (or nil) — confirm no panic.
|
|
svc.SetJobRepo(nil)
|
|
svc.SetKeygenMode("agent")
|
|
svc.SetKeygenMode("server")
|
|
}
|