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 71b2245) 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.
This commit is contained in:
shankar0123
2026-04-30 22:33:57 +00:00
parent 251db46f26
commit 482c7e8047
111 changed files with 761 additions and 770 deletions
+7 -7
View File
@@ -692,10 +692,10 @@ func TestMakeRequest_InvalidURL(t *testing.T) {
// TestCertKeyInfo tests extraction of key algorithm and size from certificates.
func TestCertKeyInfo(t *testing.T) {
tests := []struct {
name string
genKey func() interface{}
expectedAlg string
minBitSize int
name string
genKey func() interface{}
expectedAlg string
minBitSize int
}{
{
name: "ECDSA P-256",
@@ -1503,9 +1503,9 @@ func TestValidateHTTPSScheme(t *testing.T) {
wantErrSub: "plaintext http://",
},
{
name: "bare host missing scheme falls through to unsupported",
serverURL: "localhost:8443",
wantErr: true,
name: "bare host missing scheme falls through to unsupported",
serverURL: "localhost:8443",
wantErr: true,
// url.Parse treats "localhost:8443" as scheme=localhost,
// opaque=8443 — exercises the default arm (unsupported scheme)
// rather than the empty-scheme arm. Both are fail-closed, which
+9 -9
View File
@@ -34,16 +34,16 @@ import (
"github.com/shankar0123/certctl/internal/connector/target/apache"
"github.com/shankar0123/certctl/internal/connector/target/caddy"
"github.com/shankar0123/certctl/internal/connector/target/envoy"
pf "github.com/shankar0123/certctl/internal/connector/target/postfix"
sshconn "github.com/shankar0123/certctl/internal/connector/target/ssh"
"github.com/shankar0123/certctl/internal/connector/target/f5"
jks "github.com/shankar0123/certctl/internal/connector/target/javakeystore"
k8s "github.com/shankar0123/certctl/internal/connector/target/k8ssecret"
wcs "github.com/shankar0123/certctl/internal/connector/target/wincertstore"
"github.com/shankar0123/certctl/internal/connector/target/haproxy"
"github.com/shankar0123/certctl/internal/connector/target/iis"
jks "github.com/shankar0123/certctl/internal/connector/target/javakeystore"
k8s "github.com/shankar0123/certctl/internal/connector/target/k8ssecret"
"github.com/shankar0123/certctl/internal/connector/target/nginx"
pf "github.com/shankar0123/certctl/internal/connector/target/postfix"
sshconn "github.com/shankar0123/certctl/internal/connector/target/ssh"
"github.com/shankar0123/certctl/internal/connector/target/traefik"
wcs "github.com/shankar0123/certctl/internal/connector/target/wincertstore"
)
// AgentConfig represents the agent-side configuration.
@@ -80,10 +80,10 @@ type Agent struct {
client *http.Client
// Configuration
heartbeatInterval time.Duration
pollInterval time.Duration
discoveryInterval time.Duration
consecutiveFailures int
heartbeatInterval time.Duration
pollInterval time.Duration
discoveryInterval time.Duration
consecutiveFailures int
// I-004: terminal retirement signal. retiredSignal is closed exactly once
// (guarded by retiredOnce) when either sendHeartbeat or pollForWork
+9 -9
View File
@@ -75,8 +75,8 @@ func verifyDeployment(
// calls, issuer connector communication, or any operation that trusts the
// certificate. The verification result compares SHA-256 fingerprints only.
// See TICKET-016 for full security audit rationale.
InsecureSkipVerify: true, //nolint:gosec // verification probe; documented above + docs/tls.md L-001 table
ServerName: targetHost, // For SNI
InsecureSkipVerify: true, //nolint:gosec // verification probe; documented above + docs/tls.md L-001 table
ServerName: targetHost, // For SNI
})
if err != nil {
return nil, fmt.Errorf("failed to connect to %s: %w", address, err)
@@ -161,11 +161,11 @@ func (a *Agent) reportVerificationResult(
// Build the request payload
payload := map[string]interface{}{
"target_id": targetID,
"expected_fingerprint": result.ExpectedFingerprint,
"actual_fingerprint": result.ActualFingerprint,
"verified": result.Verified,
"error": result.Error,
"target_id": targetID,
"expected_fingerprint": result.ExpectedFingerprint,
"actual_fingerprint": result.ActualFingerprint,
"verified": result.Verified,
"error": result.Error,
}
body, err := json.Marshal(payload)
@@ -247,7 +247,7 @@ func (a *Agent) verifyAndReportDeployment(
) {
// Perform verification with configured timeout and delay
result, err := verifyDeployment(ctx, targetHost, targetPort, certPEM,
2*time.Second, // delay before probing
2*time.Second, // delay before probing
10*time.Second, // timeout for TLS connection
a.logger)
@@ -261,7 +261,7 @@ func (a *Agent) verifyAndReportDeployment(
}
// Probe failure: report error but continue
result = &VerificationResult{
Error: err.Error(),
Error: err.Error(),
VerifiedAt: time.Now().UTC(),
}
}
+3 -3
View File
@@ -114,9 +114,9 @@ func TestExtractTargetHostAndPort_InvalidJSON(t *testing.T) {
func TestExtractTargetHostAndPort_AlternativeFieldNames(t *testing.T) {
tests := []struct {
name string
config map[string]interface{}
expected string
name string
config map[string]interface{}
expected string
}{
{"host", map[string]interface{}{"host": "host1.com"}, "host1.com"},
{"hostname", map[string]interface{}{"hostname": "host2.com"}, "host2.com"},
+3 -3
View File
@@ -53,9 +53,9 @@ func TestValidateHTTPSScheme(t *testing.T) {
wantErrSub: "plaintext http://",
},
{
name: "bare host missing scheme rejected",
serverURL: "localhost:8443",
wantErr: true,
name: "bare host missing scheme rejected",
serverURL: "localhost:8443",
wantErr: true,
// url.Parse treats "localhost:8443" as scheme=localhost, opaque=8443
// — exercises the default arm (unsupported scheme) rather than the
// empty-scheme arm. Both are fail-closed, which is what we care about.
+3 -3
View File
@@ -47,9 +47,9 @@ func TestValidateHTTPSScheme(t *testing.T) {
wantErrSub: "plaintext http://",
},
{
name: "bare host missing scheme rejected",
serverURL: "localhost:8443",
wantErr: true,
name: "bare host missing scheme rejected",
serverURL: "localhost:8443",
wantErr: true,
// url.Parse treats "localhost:8443" as scheme=localhost, opaque=8443
// — exercises the default arm (unsupported scheme) rather than the
// empty-scheme arm. Both are fail-closed, which is what we care about.
+14 -14
View File
@@ -149,10 +149,10 @@ func (c *qaClient) do(method, path string, body string) (*http.Response, error)
return c.http.Do(req)
}
func (c *qaClient) get(path string) (*http.Response, error) { return c.do("GET", path, "") }
func (c *qaClient) post(path, body string) (*http.Response, error) { return c.do("POST", path, body) }
func (c *qaClient) put(path, body string) (*http.Response, error) { return c.do("PUT", path, body) }
func (c *qaClient) delete(path string) (*http.Response, error) { return c.do("DELETE", path, "") }
func (c *qaClient) get(path string) (*http.Response, error) { return c.do("GET", path, "") }
func (c *qaClient) post(path, body string) (*http.Response, error) { return c.do("POST", path, body) }
func (c *qaClient) put(path, body string) (*http.Response, error) { return c.do("PUT", path, body) }
func (c *qaClient) delete(path string) (*http.Response, error) { return c.do("DELETE", path, "") }
// statusCode makes a request and returns the HTTP status code.
func (c *qaClient) statusCode(method, path, body string) (int, error) {
@@ -228,11 +228,11 @@ type qaCert struct {
}
type qaJob struct {
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
CertificateID string `json:"certificate_id"`
AgentID *string `json:"agent_id"`
ID string `json:"id"`
Type string `json:"type"`
Status string `json:"status"`
CertificateID string `json:"certificate_id"`
AgentID *string `json:"agent_id"`
}
type qaIssuer struct {
@@ -261,15 +261,15 @@ type qaAgent struct {
}
type qaNotification struct {
ID string `json:"id"`
Read bool `json:"read"`
ID string `json:"id"`
Read bool `json:"read"`
}
type qaStats struct {
TotalCertificates int `json:"total_certificates"`
ActiveCertificates int `json:"active_certificates"`
TotalCertificates int `json:"total_certificates"`
ActiveCertificates int `json:"active_certificates"`
ExpiringCertificates int `json:"expiring_certificates"`
TotalAgents int `json:"total_agents"`
TotalAgents int `json:"total_agents"`
}
type qaMetrics struct {
@@ -56,8 +56,8 @@ func adversarialPathInputs() []struct {
{"null_byte_trailing", "mc-001\x00"},
{"null_byte_embedded", "mc-\x00-001"},
{"long_id_10k", strings.Repeat("A", 10000)},
{"unicode_homoglyph_hyphen", "mc\u2010001"}, // U+2010 HYPHEN
{"unicode_homoglyph_fullwidth", "mc\uFF0D001"}, // U+FF0D FULLWIDTH HYPHEN-MINUS
{"unicode_homoglyph_hyphen", "mc\u2010001"}, // U+2010 HYPHEN
{"unicode_homoglyph_fullwidth", "mc\uFF0D001"}, // U+FF0D FULLWIDTH HYPHEN-MINUS
{"control_char_newline", "mc-001\n"},
{"control_char_tab", "mc\t001"},
{"control_char_bell", "mc\x07001"},
+2 -2
View File
@@ -1,10 +1,10 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"errors"
"context"
"encoding/json"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"net/http"
"strconv"
"strings"
+2 -2
View File
@@ -28,8 +28,8 @@ type MockAgentService struct {
// I-004: soft-retirement hooks. Tests that don't set these receive nil
// results and nil errors, which mirrors the safest default (no-op) for
// unrelated suites that mock only the legacy surface.
RetireAgentFn func(agentID, actor string, force bool, reason string) (*service.AgentRetirementResult, error)
ListRetiredAgentsFn func(page, perPage int) ([]domain.Agent, int64, error)
RetireAgentFn func(agentID, actor string, force bool, reason string) (*service.AgentRetirementResult, error)
ListRetiredAgentsFn func(page, perPage int) ([]domain.Agent, int64, error)
}
func (m *MockAgentService) ListAgents(_ context.Context, page, perPage int) ([]domain.Agent, int64, error) {
@@ -56,10 +56,10 @@ func TestRetireAgentHandler_Success_200(t *testing.T) {
}
var body struct {
RetiredAt time.Time `json:"retired_at"`
AlreadyRetired bool `json:"already_retired"`
Cascade bool `json:"cascade"`
Counts domain.AgentDependencyCounts `json:"counts"`
RetiredAt time.Time `json:"retired_at"`
AlreadyRetired bool `json:"already_retired"`
Cascade bool `json:"cascade"`
Counts domain.AgentDependencyCounts `json:"counts"`
}
if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
t.Fatalf("decode 200 body: %v", err)
@@ -273,10 +273,10 @@ func TestRetireAgentHandler_ForceCascade_200(t *testing.T) {
}
var body struct {
RetiredAt time.Time `json:"retired_at"`
AlreadyRetired bool `json:"already_retired"`
Cascade bool `json:"cascade"`
Counts domain.AgentDependencyCounts `json:"counts"`
RetiredAt time.Time `json:"retired_at"`
AlreadyRetired bool `json:"already_retired"`
Cascade bool `json:"cascade"`
Counts domain.AgentDependencyCounts `json:"counts"`
}
if err := json.NewDecoder(w.Body).Decode(&body); err != nil {
t.Fatalf("decode force-cascade 200 body: %v", err)
+1 -1
View File
@@ -1,10 +1,10 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"encoding/json"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"log/slog"
"net/http"
"strconv"
+3 -3
View File
@@ -9,14 +9,14 @@ import (
"testing"
"time"
"github.com/shankar0123/certctl/internal/domain"
"github.com/shankar0123/certctl/internal/api/middleware"
"github.com/shankar0123/certctl/internal/domain"
)
// mockAuditService implements AuditService for testing.
type mockAuditService struct {
listFunc func(page, perPage int) ([]domain.AuditEvent, int64, error)
getFunc func(id string) (*domain.AuditEvent, error)
listFunc func(page, perPage int) ([]domain.AuditEvent, int64, error)
getFunc func(id string) (*domain.AuditEvent, error)
}
func (m *mockAuditService) ListAuditEvents(_ context.Context, page, perPage int) ([]domain.AuditEvent, int64, error) {
@@ -86,10 +86,10 @@ func TestBulkRenew_PartialFailure_ReportsBoth(t *testing.T) {
svc := &mockBulkRenewalService{
BulkRenewFn: func(ctx context.Context, criteria domain.BulkRenewalCriteria, actor string) (*domain.BulkRenewalResult, error) {
return &domain.BulkRenewalResult{
TotalMatched: 3,
TotalMatched: 3,
TotalEnqueued: 2,
TotalSkipped: 0,
TotalFailed: 1,
TotalSkipped: 0,
TotalFailed: 1,
Errors: []domain.BulkOperationError{
{CertificateID: "mc-failed", Error: "renewal job enqueue failed: db timeout"},
},
+6 -6
View File
@@ -98,12 +98,12 @@ func generateTestCertPEM(t *testing.T) string {
t.Fatalf("failed to generate key: %v", err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "Test CA"},
NotBefore: time.Now().Add(-1 * time.Hour),
NotAfter: time.Now().Add(24 * time.Hour),
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
IsCA: true,
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "Test CA"},
NotBefore: time.Now().Add(-1 * time.Hour),
NotAfter: time.Now().Add(24 * time.Hour),
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
IsCA: true,
BasicConstraintsValid: true,
}
certDER, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
+2 -2
View File
@@ -1,11 +1,11 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"errors"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"log/slog"
"net/http"
"strings"
+6 -6
View File
@@ -59,12 +59,12 @@ func (h *HealthCheckHandler) ListHealthChecks(w http.ResponseWriter, r *http.Req
}
filter := &repository.HealthCheckFilter{
Status: status,
CertificateID: certificateID,
NetworkScanTargetID: networkScanTargetID,
Enabled: enabledFilter,
Page: page,
PerPage: perPage,
Status: status,
CertificateID: certificateID,
NetworkScanTargetID: networkScanTargetID,
Enabled: enabledFilter,
Page: page,
PerPage: perPage,
}
checks, total, err := h.service.List(r.Context(), filter)
+2 -2
View File
@@ -1,10 +1,10 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"errors"
"context"
"encoding/json"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"log/slog"
"net/http"
"strconv"
+1 -1
View File
@@ -1,10 +1,10 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"encoding/json"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"io"
"net/http"
"strconv"
+2 -2
View File
@@ -1,9 +1,9 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"errors"
"context"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"net/http"
"strconv"
"strings"
+2 -2
View File
@@ -1,10 +1,10 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"errors"
"context"
"encoding/json"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"net/http"
"strconv"
"strings"
+3 -3
View File
@@ -237,9 +237,9 @@ func TestCreateProfile_Success(t *testing.T) {
}
body := map[string]interface{}{
"name": "New Profile",
"name": "New Profile",
"max_ttl_seconds": 86400,
"allowed_ekus": []string{"serverAuth"},
"allowed_ekus": []string{"serverAuth"},
}
bodyBytes, _ := json.Marshal(body)
@@ -331,7 +331,7 @@ func TestUpdateProfile_Success(t *testing.T) {
}
body := map[string]interface{}{
"name": "Updated Profile",
"name": "Updated Profile",
"max_ttl_seconds": 172800,
}
bodyBytes, _ := json.Marshal(body)
+2 -2
View File
@@ -1,10 +1,10 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"errors"
"context"
"encoding/json"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"net/http"
"strconv"
"strings"
+1 -1
View File
@@ -1,10 +1,10 @@
package handler
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"encoding/json"
"errors"
"github.com/shankar0123/certctl/internal/repository"
"net/http"
"strconv"
"strings"
@@ -26,11 +26,11 @@ import (
// MockRenewalPolicyService is a mock implementation of RenewalPolicyService.
type MockRenewalPolicyService struct {
ListRenewalPoliciesFn func(page, perPage int) ([]domain.RenewalPolicy, int64, error)
GetRenewalPolicyFn func(id string) (*domain.RenewalPolicy, error)
CreateRenewalPolicyFn func(rp domain.RenewalPolicy) (*domain.RenewalPolicy, error)
UpdateRenewalPolicyFn func(id string, rp domain.RenewalPolicy) (*domain.RenewalPolicy, error)
DeleteRenewalPolicyFn func(id string) error
ListRenewalPoliciesFn func(page, perPage int) ([]domain.RenewalPolicy, int64, error)
GetRenewalPolicyFn func(id string) (*domain.RenewalPolicy, error)
CreateRenewalPolicyFn func(rp domain.RenewalPolicy) (*domain.RenewalPolicy, error)
UpdateRenewalPolicyFn func(id string, rp domain.RenewalPolicy) (*domain.RenewalPolicy, error)
DeleteRenewalPolicyFn func(id string) error
}
func (m *MockRenewalPolicyService) ListRenewalPolicies(_ context.Context, page, perPage int) ([]domain.RenewalPolicy, int64, error) {
@@ -199,11 +199,11 @@ func TestCreateRenewalPolicy_Success(t *testing.T) {
}
body := map[string]interface{}{
"name": "New Policy",
"renewal_window_days": 30,
"max_retries": 3,
"name": "New Policy",
"renewal_window_days": 30,
"max_retries": 3,
"retry_interval_seconds": 3600,
"auto_renew": true,
"auto_renew": true,
}
bodyBytes, _ := json.Marshal(body)
@@ -221,8 +221,8 @@ func TestCreateRenewalPolicy_Success(t *testing.T) {
func TestCreateRenewalPolicy_MissingName(t *testing.T) {
body := map[string]interface{}{
"renewal_window_days": 30,
"max_retries": 3,
"renewal_window_days": 30,
"max_retries": 3,
"retry_interval_seconds": 3600,
}
bodyBytes, _ := json.Marshal(body)
@@ -261,9 +261,9 @@ func TestCreateRenewalPolicy_DuplicateName(t *testing.T) {
}
body := map[string]interface{}{
"name": "Duplicate",
"renewal_window_days": 30,
"max_retries": 3,
"name": "Duplicate",
"renewal_window_days": 30,
"max_retries": 3,
"retry_interval_seconds": 3600,
}
bodyBytes, _ := json.Marshal(body)
@@ -308,11 +308,11 @@ func TestUpdateRenewalPolicy_Success(t *testing.T) {
}
body := map[string]interface{}{
"name": "Updated Policy",
"renewal_window_days": 45,
"max_retries": 5,
"name": "Updated Policy",
"renewal_window_days": 45,
"max_retries": 5,
"retry_interval_seconds": 1800,
"auto_renew": true,
"auto_renew": true,
}
bodyBytes, _ := json.Marshal(body)
@@ -336,9 +336,9 @@ func TestUpdateRenewalPolicy_NotFound(t *testing.T) {
}
body := map[string]interface{}{
"name": "Updated",
"renewal_window_days": 30,
"max_retries": 3,
"name": "Updated",
"renewal_window_days": 30,
"max_retries": 3,
"retry_interval_seconds": 3600,
}
bodyBytes, _ := json.Marshal(body)
+6 -6
View File
@@ -346,8 +346,8 @@ func TestCursorPagedResponse_EmptyNextCursor(t *testing.T) {
func TestFilterFields_SingleObject(t *testing.T) {
data := map[string]interface{}{
"id": "cert-123",
"name": "My Cert",
"id": "cert-123",
"name": "My Cert",
"expiry": "2025-01-01",
"status": "active",
}
@@ -379,8 +379,8 @@ func TestFilterFields_SingleObject(t *testing.T) {
func TestFilterFields_EmptyFields(t *testing.T) {
// Empty fields list should return data unchanged
data := map[string]interface{}{
"id": "cert-123",
"name": "My Cert",
"id": "cert-123",
"name": "My Cert",
}
result := filterFields(data, []string{})
@@ -398,8 +398,8 @@ func TestFilterFields_EmptyFields(t *testing.T) {
func TestFilterFields_NoMatchingFields(t *testing.T) {
data := map[string]interface{}{
"id": "cert-123",
"name": "My Cert",
"id": "cert-123",
"name": "My Cert",
}
result := filterFields(data, []string{"nonexistent", "also-not-there"})
+1 -1
View File
@@ -333,7 +333,7 @@ func TestValidateCSRPEM_InvalidInputs(t *testing.T) {
// TestValidatePolicyType_ValidTypes tests valid policy types.
func TestValidatePolicyType_ValidTypes(t *testing.T) {
validTypes := []struct {
name string
name string
ptype interface{}
}{
{
+2 -2
View File
@@ -97,8 +97,8 @@ func (h VerificationHandler) VerifyDeployment(w http.ResponseWriter, r *http.Req
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"job_id": jobID,
"verified": req.Verified,
"job_id": jobID,
"verified": req.Verified,
"verified_at": result.VerifiedAt,
})
}
+5 -5
View File
@@ -221,11 +221,11 @@ func NewAuditServiceAdapter(recordFn func(ctx context.Context, actor string, act
// RecordAPICall implements AuditRecorder by translating API call data into an audit event.
func (a *AuditServiceAdapter) RecordAPICall(ctx context.Context, method, path, actor string, bodyHash string, status int, latencyMs int64) error {
details := map[string]interface{}{
"method": method,
"path": path,
"body_hash": bodyHash,
"status": status,
"latency_ms": latencyMs,
"method": method,
"path": path,
"body_hash": bodyHash,
"status": status,
"latency_ms": latencyMs,
}
action := fmt.Sprintf("api_%s", strings.ToLower(method))
+4 -4
View File
@@ -15,10 +15,10 @@ import (
// mockAuditRecorder captures RecordAPICall invocations for testing.
type mockAuditRecorder struct {
mu sync.Mutex
calls []auditCall
err error // if non-nil, RecordAPICall returns this
block chan struct{} // if non-nil, RecordAPICall blocks on receive before returning
mu sync.Mutex
calls []auditCall
err error // if non-nil, RecordAPICall returns this
block chan struct{} // if non-nil, RecordAPICall blocks on receive before returning
}
type auditCall struct {
+6 -6
View File
@@ -40,9 +40,9 @@ func TestNewCORS_NilOriginsDeniesAll(t *testing.T) {
// TestNewCORS_M013_ContractDocumentedInOrder pins the documented dispatch
// order so a refactor cannot silently invert the cases:
//
// 1. len(AllowedOrigins) == 0 → deny (no CORS headers)
// 2. AllowedOrigins == ["*"] → allow all (Access-Control-Allow-Origin: *)
// 3. else → exact-match allowlist with Vary: Origin
// 1. len(AllowedOrigins) == 0 → deny (no CORS headers)
// 2. AllowedOrigins == ["*"] → allow all (Access-Control-Allow-Origin: *)
// 3. else → exact-match allowlist with Vary: Origin
//
// If a refactor accidentally falls through to the allow-all branch when
// AllowedOrigins is empty, this test fails on case 1.
@@ -293,9 +293,9 @@ func TestNewCORS_MultipleOrigins(t *testing.T) {
}))
tests := []struct {
origin string
shouldAllow bool
description string
origin string
shouldAllow bool
description string
}{
{"https://app.example.com", true, "first origin in list"},
{"https://admin.example.com", true, "second origin in list"},
+5 -5
View File
@@ -290,11 +290,11 @@ func NewRateLimiter(cfg RateLimitConfig) func(http.Handler) http.Handler {
}
limiter := &keyedRateLimiter{
ipRate: cfg.RPS,
ipBurst: float64(cfg.BurstSize),
userRate: perUserRPS,
userBurst: perUserBurst,
buckets: make(map[string]*tokenBucket),
ipRate: cfg.RPS,
ipBurst: float64(cfg.BurstSize),
userRate: perUserRPS,
userBurst: perUserBurst,
buckets: make(map[string]*tokenBucket),
}
return func(next http.Handler) http.Handler {
+4 -3
View File
@@ -990,9 +990,9 @@ func TestGetLogLevel_AllLevels(t *testing.T) {
{"info", slog.LevelInfo},
{"warn", slog.LevelWarn},
{"error", slog.LevelError},
{"unknown", slog.LevelInfo}, // default fallback
{"", slog.LevelInfo}, // empty string
{"DEBUG", slog.LevelInfo}, // case-sensitive, no match → default
{"unknown", slog.LevelInfo}, // default fallback
{"", slog.LevelInfo}, // empty string
{"DEBUG", slog.LevelInfo}, // case-sensitive, no match → default
}
for _, tt := range tests {
t.Run(tt.level, func(t *testing.T) {
@@ -1091,6 +1091,7 @@ func TestGetEnvBool(t *testing.T) {
})
}
}
// I-003: Job timeout reaper configuration tests
func TestConfig_Scheduler_JobTimeoutDefaults(t *testing.T) {
clearCertctlEnv(t)
@@ -20,10 +20,10 @@ import (
// mockSMClient is a mock implementation of SMClient for testing.
type mockSMClient struct {
secrets map[string]string // secret name -> secret value
secretMetadata map[string]SecretMetadata // secret name -> metadata
listError error
getErrors map[string]error // secret name -> error
secrets map[string]string // secret name -> secret value
secretMetadata map[string]SecretMetadata // secret name -> metadata
listError error
getErrors map[string]error // secret name -> error
}
func newMockSMClient() *mockSMClient {
@@ -369,4 +369,3 @@ func TestSource_Discover_AgentIDAndSourcePath(t *testing.T) {
t.Errorf("expected source path 'aws-sm://eu-west-1/my-secret', got %s", report.Certificates[0].SourcePath)
}
}
+11 -11
View File
@@ -69,10 +69,10 @@ type certificateListResponse struct {
Value []struct {
ID string `json:"id"`
Attributes struct {
Enabled int64 `json:"enabled"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
Exp int64 `json:"exp"`
Enabled int64 `json:"enabled"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
Exp int64 `json:"exp"`
} `json:"attributes,omitempty"`
Tags map[string]string `json:"tags,omitempty"`
} `json:"value"`
@@ -84,10 +84,10 @@ type certificateBundle struct {
ID string `json:"id"`
CER string `json:"cer"`
Attributes struct {
Enabled int64 `json:"enabled"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
Exp int64 `json:"exp"`
Enabled int64 `json:"enabled"`
Created int64 `json:"created"`
Updated int64 `json:"updated"`
Exp int64 `json:"exp"`
} `json:"attributes,omitempty"`
}
@@ -170,10 +170,10 @@ func (s *Source) Discover(ctx context.Context) (*domain.DiscoveryReport, error)
s.logger.Info("starting Azure Key Vault discovery", "vault_url", s.config.VaultURL)
report := &domain.DiscoveryReport{
AgentID: "cloud-azure-kv",
Directories: []string{fmt.Sprintf("azure-kv://%s/", s.config.VaultURL)},
AgentID: "cloud-azure-kv",
Directories: []string{fmt.Sprintf("azure-kv://%s/", s.config.VaultURL)},
Certificates: []domain.DiscoveredCertEntry{},
Errors: []string{},
Errors: []string{},
}
startTime := time.Now()
@@ -82,7 +82,7 @@ func generateTestCertificate(cn string, expire time.Duration) (*x509.Certificate
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
},
DNSNames: []string{"example.com", "*.example.com"},
DNSNames: []string{"example.com", "*.example.com"},
EmailAddresses: []string{"test@example.com"},
}
+10 -10
View File
@@ -798,9 +798,9 @@ func TestValidateConfig_DNSPersistIssuerDomainRequired(t *testing.T) {
c := New(nil, testLogger())
cfg, _ := json.Marshal(map[string]string{
"directory_url": srv.URL,
"email": "test@example.com",
"challenge_type": "dns-persist-01",
"directory_url": srv.URL,
"email": "test@example.com",
"challenge_type": "dns-persist-01",
"dns_present_script": "/tmp/script.sh",
// Missing dns_persist_issuer_domain
})
@@ -870,9 +870,9 @@ func TestValidateConfig_DNS01WithPresentScript(t *testing.T) {
c := New(nil, testLogger())
cfg, _ := json.Marshal(map[string]string{
"directory_url": srv.URL,
"email": "test@example.com",
"challenge_type": "dns-01",
"directory_url": srv.URL,
"email": "test@example.com",
"challenge_type": "dns-01",
"dns_present_script": "/bin/sh",
"dns_cleanup_script": "/bin/sh",
})
@@ -897,10 +897,10 @@ func TestValidateConfig_DNSPersist01WithAllFields(t *testing.T) {
c := New(nil, testLogger())
cfg, _ := json.Marshal(map[string]string{
"directory_url": srv.URL,
"email": "test@example.com",
"challenge_type": "dns-persist-01",
"dns_present_script": "/bin/sh",
"directory_url": srv.URL,
"email": "test@example.com",
"challenge_type": "dns-persist-01",
"dns_present_script": "/bin/sh",
"dns_persist_issuer_domain": "letsencrypt.org",
})
+4 -4
View File
@@ -74,8 +74,8 @@ func TestGetRenewalInfo_NotFound(t *testing.T) {
if r.URL.Path == "/directory" && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"newOrder": "/acme/new-order",
"newAccount": "/acme/new-account",
"newOrder": "/acme/new-order",
"newAccount": "/acme/new-account",
})
return
}
@@ -115,8 +115,8 @@ func TestGetRenewalInfo_ServerError(t *testing.T) {
if r.URL.Path == "/directory" && r.Method == http.MethodGet {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"newOrder": "/acme/new-order",
"newAccount": "/acme/new-account",
"newOrder": "/acme/new-order",
"newAccount": "/acme/new-account",
})
return
}
@@ -106,7 +106,7 @@ type pebbleMockServer struct {
idSeq int64
// Behavior toggles for failure-mode tests.
failNewAccount bool
rateLimitedOrder int32 // atomic counter; non-zero ⇒ first N orders return 429
rateLimitedOrder int32 // atomic counter; non-zero ⇒ first N orders return 429
finalizeReturns string // "" (default), "processing-stuck", "invalid"
authzPending bool // when true, new authzs start as "pending" and only flip to "valid" after the challenge endpoint is POSTed
challengeType string // when set, the per-authz challenge type emitted (default "http-01")
@@ -990,12 +990,12 @@ func TestPebbleMock_ContextCancel_DuringIssuance(t *testing.T) {
// ─────────────────────────────────────────────────────────────────────────────
type mockDNSSolver struct {
mu sync.Mutex
presented map[string]string // domain → keyAuth (or recordValue)
cleanedUp map[string]bool
presentErr error
cleanErr error
presentDelay time.Duration
mu sync.Mutex
presented map[string]string // domain → keyAuth (or recordValue)
cleanedUp map[string]bool
presentErr error
cleanErr error
presentDelay time.Duration
}
func newMockDNSSolver() *mockDNSSolver {
@@ -249,4 +249,3 @@ func signJWS(key *ecdsa.PrivateKey, kid, nonce, targetURL string, payload []byte
return json.Marshal(jws)
}
@@ -105,9 +105,9 @@ type GetCertificateOutput struct {
// RevokeCertificateInput represents the request to revoke a certificate.
type RevokeCertificateInput struct {
CAArn string
CertificateSerial string
RevocationReason string
CAArn string
CertificateSerial string
RevocationReason string
}
// GetCACertificateInput represents the request to retrieve the CA certificate.
@@ -395,14 +395,14 @@ func mapRevocationReason(reason *string) string {
}
reasonMap := map[string]string{
"unspecified": "UNSPECIFIED",
"keyCompromise": "KEY_COMPROMISE",
"caCompromise": "CERTIFICATE_AUTHORITY_COMPROMISE",
"affiliationChanged": "AFFILIATION_CHANGED",
"superseded": "SUPERSEDED",
"cessationOfOperation": "CESSATION_OF_OPERATION",
"certificateHold": "CERTIFICATE_HOLD",
"privilegeWithdrawn": "PRIVILEGE_WITHDRAWN",
"unspecified": "UNSPECIFIED",
"keyCompromise": "KEY_COMPROMISE",
"caCompromise": "CERTIFICATE_AUTHORITY_COMPROMISE",
"affiliationChanged": "AFFILIATION_CHANGED",
"superseded": "SUPERSEDED",
"cessationOfOperation": "CESSATION_OF_OPERATION",
"certificateHold": "CERTIFICATE_HOLD",
"privilegeWithdrawn": "PRIVILEGE_WITHDRAWN",
}
if mapped, ok := reasonMap[*reason]; ok {
@@ -22,15 +22,15 @@ import (
// mockACMPCAClient implements the ACMPCAClient interface for testing.
type mockACMPCAClient struct {
issueCertificateErr error
getCertificateErr error
revokeCertificateErr error
getCACertificateErr error
issuedCertPEM string
issuedChainPEM string
caCertPEM string
caCertChainPEM string
lastIssueCertificateInput *awsacmpca.IssueCertificateInput
issueCertificateErr error
getCertificateErr error
revokeCertificateErr error
getCACertificateErr error
issuedCertPEM string
issuedChainPEM string
caCertPEM string
caCertChainPEM string
lastIssueCertificateInput *awsacmpca.IssueCertificateInput
lastRevokeCertificateInput *awsacmpca.RevokeCertificateInput
}
@@ -90,9 +90,9 @@ func New(config *Config, logger *slog.Logger) *Connector {
// orderRequest is the JSON body for DigiCert certificate order submission.
type orderRequest struct {
Certificate orderCert `json:"certificate"`
Organization orderOrg `json:"organization"`
ValidityYears int `json:"validity_years"`
Certificate orderCert `json:"certificate"`
Organization orderOrg `json:"organization"`
ValidityYears int `json:"validity_years"`
}
type orderCert struct {
+1 -1
View File
@@ -162,7 +162,7 @@ func (c *Connector) IssueCertificate(ctx context.Context, request issuer.Issuanc
csrBase64 := base64.StdEncoding.EncodeToString(csrBlock.Bytes)
enrollReq := map[string]interface{}{
"certificate_request": csrBase64,
"certificate_request": csrBase64,
"certificate_authority_name": c.config.CAName,
}
@@ -175,7 +175,7 @@ func TestEJBCAConnector(t *testing.T) {
w.Header().Set("Content-Type", "application/json")
respData := map[string]interface{}{
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate_chain": []string{base64.StdEncoding.EncodeToString(chainBlock.Bytes)},
"serial_number": "123456",
}
@@ -242,7 +242,7 @@ func TestEJBCAConnector(t *testing.T) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
respData := map[string]interface{}{
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate_chain": []string{},
"serial_number": "789012",
}
@@ -314,7 +314,7 @@ func TestEJBCAConnector(t *testing.T) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
respData := map[string]interface{}{
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate_chain": []string{},
"serial_number": "123456",
}
@@ -356,7 +356,7 @@ func TestEJBCAConnector(t *testing.T) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
respData := map[string]interface{}{
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate": base64.StdEncoding.EncodeToString(certBlock.Bytes),
"certificate_chain": []string{},
"serial_number": "654321",
}
+3 -3
View File
@@ -89,9 +89,9 @@ func NewWithHTTPClient(config *Config, logger *slog.Logger, client *http.Client)
// enrollmentRequest is the JSON body for Entrust enrollment submission.
type enrollmentRequest struct {
CSR string `json:"csr"`
ProfileId string `json:"profileId,omitempty"`
SubjectAltNames []san `json:"subjectAltNames,omitempty"`
CSR string `json:"csr"`
ProfileId string `json:"profileId,omitempty"`
SubjectAltNames []san `json:"subjectAltNames,omitempty"`
CertificateAuthority string `json:"certificateAuthority,omitempty"`
}
@@ -107,9 +107,9 @@ func NewWithHTTPClient(config *Config, logger *slog.Logger, client *http.Client)
// certificateRequest is the JSON body for GlobalSign certificate order submission.
type certificateRequest struct {
CSR string `json:"csr"`
CSR string `json:"csr"`
SubjectDN subjectDNRequest `json:"subject_dn"`
SAN sanRequest `json:"san,omitempty"`
SAN sanRequest `json:"san,omitempty"`
}
type subjectDNRequest struct {
@@ -93,10 +93,10 @@ type Connector struct {
httpClient *http.Client
// OAuth2 token caching
mu sync.Mutex
tokenCache *cachedToken
saKey *serviceAccountKey
rsaKey *rsa.PrivateKey
mu sync.Mutex
tokenCache *cachedToken
saKey *serviceAccountKey
rsaKey *rsa.PrivateKey
}
// New creates a new Google CAS connector with the given configuration and logger.
+3 -3
View File
@@ -479,9 +479,9 @@ func (c *Connector) parseCertificate(certPEM []byte) (*x509.Certificate, string,
// Format: [{"serial": "...", "revoked_at": "...", "reason_code": ...}, ...]
func (c *Connector) marshalRevokedSerials(revokedCerts []issuer.RevokedCertEntry) ([]byte, error) {
type RevokedEntry struct {
Serial string `json:"serial"`
RevokedAt string `json:"revoked_at"`
ReasonCode int `json:"reason_code"`
Serial string `json:"serial"`
RevokedAt string `json:"revoked_at"`
ReasonCode int `json:"reason_code"`
}
entries := make([]RevokedEntry, len(revokedCerts))
@@ -643,7 +643,7 @@ func generateTestCSR(cn string) (*x509.CertificateRequest, string, error) {
}
csrTemplate := x509.CertificateRequest{
Subject: subject,
Subject: subject,
DNSNames: []string{cn, "www." + cn},
}
+11 -11
View File
@@ -168,9 +168,9 @@ type signRequest struct {
// signResponse is the JSON response from the step-ca /sign endpoint.
type signResponse struct {
ServerPEM certificateChain `json:"serverPEM,omitempty"`
CaPEM certificateChain `json:"caPEM,omitempty"`
CertChainPEM []certBlock `json:"certChainPEM,omitempty"`
ServerPEM certificateChain `json:"serverPEM,omitempty"`
CaPEM certificateChain `json:"caPEM,omitempty"`
CertChainPEM []certBlock `json:"certChainPEM,omitempty"`
}
type certificateChain struct {
@@ -380,14 +380,14 @@ func (c *Connector) generateProvisionerToken(subject string, sans []string) (str
// step-ca expects: aud = <ca-url>/1.0/sign (the sign endpoint audience)
claims := map[string]interface{}{
"sub": subject,
"iss": c.config.ProvisionerName,
"aud": c.config.CAURL + "/1.0/sign",
"nbf": now.Unix(),
"iat": now.Unix(),
"exp": now.Add(5 * time.Minute).Unix(),
"jti": generateJTI(),
"sha": kid, // step-ca uses this to look up the provisioner by key fingerprint
"sub": subject,
"iss": c.config.ProvisionerName,
"aud": c.config.CAURL + "/1.0/sign",
"nbf": now.Unix(),
"iat": now.Unix(),
"exp": now.Add(5 * time.Minute).Unix(),
"jti": generateJTI(),
"sha": kid, // step-ca uses this to look up the provisioner by key fingerprint
}
if len(sans) > 0 {
@@ -1574,9 +1574,9 @@ func TestLoadProvisionerKey_FileNotReadable(t *testing.T) {
// Test with a provisioner key path that can't be read
config := stepca.Config{
CAURL: srv.URL,
ProvisionerName: "test-provisioner",
ProvisionerKeyPath: "/root/.ssh/no_such_key", // Permission denied or doesn't exist
CAURL: srv.URL,
ProvisionerName: "test-provisioner",
ProvisionerKeyPath: "/root/.ssh/no_such_key", // Permission denied or doesn't exist
ProvisionerPassword: "password",
}
@@ -1770,4 +1770,3 @@ func TestIntegration_FullLifecycle(t *testing.T) {
t.Errorf("Expected status 'completed', got '%s'", status.Status)
}
}
+3 -3
View File
@@ -86,9 +86,9 @@ func New(config *Config, logger *slog.Logger) *Connector {
// vaultResponse is the standard Vault API response wrapper.
type vaultResponse struct {
Data json.RawMessage `json:"data"`
Errors []string `json:"errors,omitempty"`
Warnings []string `json:"warnings,omitempty"`
Data json.RawMessage `json:"data"`
Errors []string `json:"errors,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
// signData holds the data returned from the /sign endpoint.
@@ -88,11 +88,11 @@ func TestEmail_ValidateConfig_MissingPort(t *testing.T) {
func TestEmail_ValidateConfig_MissingFromAddress(t *testing.T) {
cfg := &Config{
SMTPHost: "smtp.example.com",
SMTPPort: 587,
Username: "user",
Password: "pass",
UseTLS: true,
SMTPHost: "smtp.example.com",
SMTPPort: 587,
Username: "user",
Password: "pass",
UseTLS: true,
}
rawConfig, _ := json.Marshal(cfg)
+8 -8
View File
@@ -19,11 +19,11 @@ import (
// to a directory that Envoy watches via its SDS (Secret Discovery Service)
// file-based configuration or static filename references in the bootstrap config.
type Config struct {
CertDir string `json:"cert_dir"` // Directory where Envoy watches for cert files (required)
CertFilename string `json:"cert_filename"` // Filename for certificate (default: cert.pem)
KeyFilename string `json:"key_filename"` // Filename for private key (default: key.pem)
ChainFilename string `json:"chain_filename"` // Optional filename for chain (if set, chain written separately)
SDSConfig bool `json:"sds_config"` // If true, write an SDS discovery JSON file for file-based SDS
CertDir string `json:"cert_dir"` // Directory where Envoy watches for cert files (required)
CertFilename string `json:"cert_filename"` // Filename for certificate (default: cert.pem)
KeyFilename string `json:"key_filename"` // Filename for private key (default: key.pem)
ChainFilename string `json:"chain_filename"` // Optional filename for chain (if set, chain written separately)
SDSConfig bool `json:"sds_config"` // If true, write an SDS discovery JSON file for file-based SDS
}
// SDSResource represents an Envoy SDS tls_certificate resource for file-based SDS.
@@ -34,9 +34,9 @@ type SDSResource struct {
// SDSTLSCertificate represents a single SDS tls_certificate entry.
type SDSTLSCertificate struct {
Type string `json:"@type"`
Name string `json:"name"`
TLSCertificate TLSCertificate `json:"tls_certificate"`
Type string `json:"@type"`
Name string `json:"name"`
TLSCertificate TLSCertificate `json:"tls_certificate"`
}
// TLSCertificate contains the file paths for cert and key in Envoy's SDS format.
+11 -11
View File
@@ -457,13 +457,13 @@ func (c *Connector) DeployCertificate(ctx context.Context, request target.Deploy
Message: "Certificate uploaded and SSL profile updated via iControl REST",
DeployedAt: time.Now(),
Metadata: map[string]string{
"host": c.config.Host,
"partition": c.config.Partition,
"ssl_profile": c.config.SSLProfile,
"cert_object_name": certName,
"key_object_name": keyName,
"host": c.config.Host,
"partition": c.config.Partition,
"ssl_profile": c.config.SSLProfile,
"cert_object_name": certName,
"key_object_name": keyName,
"chain_object_name": chainName,
"duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()),
"duration_ms": fmt.Sprintf("%d", deploymentDuration.Milliseconds()),
},
}, nil
}
@@ -561,12 +561,12 @@ func (c *Connector) ValidateDeployment(ctx context.Context, request target.Valid
Message: fmt.Sprintf("SSL profile %q has cert %q configured", c.config.SSLProfile, profile.Cert),
ValidatedAt: time.Now(),
Metadata: map[string]string{
"host": c.config.Host,
"ssl_profile": c.config.SSLProfile,
"current_cert": profile.Cert,
"current_key": profile.Key,
"host": c.config.Host,
"ssl_profile": c.config.SSLProfile,
"current_cert": profile.Cert,
"current_key": profile.Key,
"current_chain": profile.Chain,
"duration_ms": fmt.Sprintf("%d", validationDuration.Milliseconds()),
"duration_ms": fmt.Sprintf("%d", validationDuration.Milliseconds()),
},
}, nil
}
+8 -8
View File
@@ -25,14 +25,14 @@ type mockF5Client struct {
calls []mockCall
// Configurable responses per method
authenticateErr error
authenticateCount int // tracks number of Authenticate calls
uploadFileErr error
uploadFileErrOn string // only error when filename contains this substring
installCertErr error
installCertErrOn string
installKeyErr error
createTransactionID string
authenticateErr error
authenticateCount int // tracks number of Authenticate calls
uploadFileErr error
uploadFileErrOn string // only error when filename contains this substring
installCertErr error
installCertErrOn string
installKeyErr error
createTransactionID string
createTransactionErr error
commitTransactionErr error
updateSSLProfileErr error
+3 -3
View File
@@ -59,9 +59,9 @@ func newWinRMExecutor(cfg *WinRMConfig) (*winrmExecutor, error) {
port,
cfg.UseHTTPS,
cfg.Insecure,
nil, // CA cert
nil, // Client cert
nil, // Client key
nil, // CA cert
nil, // Client cert
nil, // Client key
timeout,
)
@@ -263,10 +263,10 @@ func (c *Connector) DeployCertificate(ctx context.Context, request target.Deploy
Message: fmt.Sprintf("Certificate imported to %s (alias: %s, thumbprint: %s)", c.config.KeystorePath, c.config.Alias, thumbprint),
DeployedAt: time.Now(),
Metadata: map[string]string{
"thumbprint": thumbprint,
"alias": c.config.Alias,
"keystore_type": c.config.KeystoreType,
"keystore_path": c.config.KeystorePath,
"thumbprint": thumbprint,
"alias": c.config.Alias,
"keystore_type": c.config.KeystoreType,
"keystore_path": c.config.KeystorePath,
},
}, nil
}
@@ -240,7 +240,7 @@ func TestDeployCertificate_Success(t *testing.T) {
mock := &mockExecutor{
responses: []mockResponse{
{Output: "", Err: nil}, // keytool -delete (alias may not exist)
{Output: "", Err: nil}, // keytool -delete (alias may not exist)
{Output: "Import command completed", Err: nil}, // keytool -importkeystore
},
}
@@ -355,8 +355,8 @@ func TestDeployCertificate_WithReload(t *testing.T) {
mock := &mockExecutor{
responses: []mockResponse{
// No existing keystore → delete skipped → import is call 0, reload is call 1
{Output: "Imported", Err: nil}, // import
{Output: "restarted", Err: nil}, // reload
{Output: "Imported", Err: nil}, // import
{Output: "restarted", Err: nil}, // reload
},
}
c := NewWithExecutor(&Config{
@@ -391,8 +391,8 @@ func TestDeployCertificate_ReloadFailed_NonFatal(t *testing.T) {
mock := &mockExecutor{
responses: []mockResponse{
{Output: "", Err: nil}, // delete
{Output: "Imported", Err: nil}, // import
{Output: "", Err: nil}, // delete
{Output: "Imported", Err: nil}, // import
{Output: "Failed to restart", Err: fmt.Errorf("exit 1")}, // reload fails
},
}
@@ -21,9 +21,9 @@ import (
// Supports in-cluster auth by default (ServiceAccount token auto-mounted) or
// out-of-cluster auth via kubeconfig file.
type Config struct {
Namespace string `json:"namespace"` // Required. Kubernetes namespace.
SecretName string `json:"secret_name"` // Required. Name of the kubernetes.io/tls Secret.
Labels map[string]string `json:"labels,omitempty"` // Optional. Additional labels to add to the Secret.
Namespace string `json:"namespace"` // Required. Kubernetes namespace.
SecretName string `json:"secret_name"` // Required. Name of the kubernetes.io/tls Secret.
Labels map[string]string `json:"labels,omitempty"` // Optional. Additional labels to add to the Secret.
KubeconfigPath string `json:"kubeconfig_path,omitempty"` // Optional. Path to kubeconfig for out-of-cluster auth.
}
@@ -93,7 +93,7 @@ func (m *mockK8sClient) DeleteSecret(ctx context.Context, namespace, name string
func TestValidateConfig_Success_MinimalConfig(t *testing.T) {
cfg := map[string]interface{}{
"namespace": "default",
"namespace": "default",
"secret_name": "my-cert",
}
@@ -644,4 +644,3 @@ func contains(s, substr string) bool {
}
return false
}
+3 -3
View File
@@ -411,9 +411,9 @@ func (c *realSSHClient) Connect(ctx context.Context) error {
}
sshConfig := &ssh.ClientConfig{
User: c.config.User,
Auth: authMethods,
Timeout: time.Duration(c.config.Timeout) * time.Second,
User: c.config.User,
Auth: authMethods,
Timeout: time.Duration(c.config.Timeout) * time.Second,
// InsecureIgnoreHostKey is used intentionally: certctl deploys to known
// infrastructure (the operator explicitly configures each target host).
// This is the same security rationale as network scanner's InsecureSkipVerify
@@ -42,15 +42,15 @@ type fakeSSHServer struct {
user string
password string
wg sync.WaitGroup
mu sync.Mutex
closed bool
wg sync.WaitGroup
mu sync.Mutex
closed bool
// Optional behaviour toggles for failure-mode tests.
rejectAuth bool // reject all auth attempts (auth failure path)
dropOnHandshake bool // close conn before SSH NewServerConn returns (handshake failure)
failExec bool // exec sessions return non-zero exit (Execute error path)
failSFTP bool // refuse sftp subsystem (SFTP failure path)
rejectAuth bool // reject all auth attempts (auth failure path)
dropOnHandshake bool // close conn before SSH NewServerConn returns (handshake failure)
failExec bool // exec sessions return non-zero exit (Execute error path)
failSFTP bool // refuse sftp subsystem (SFTP failure path)
}
// startFakeSSHServer binds a fresh server on a random local port and returns
@@ -310,4 +310,3 @@ func (c *Connector) ValidateDeployment(ctx context.Context, request target.Valid
// Ensure Connector implements target.Connector.
var _ target.Connector = (*Connector)(nil)
@@ -26,10 +26,10 @@ func testLogger() *slog.Logger {
// mockExecutor records PowerShell scripts and returns configurable responses.
type mockExecutor struct {
scripts []string
responses []string
errors []error
callIndex int
scripts []string
responses []string
errors []error
callIndex int
}
func (m *mockExecutor) Execute(ctx context.Context, script string) (string, error) {
+8 -8
View File
@@ -4,14 +4,14 @@ import "testing"
func TestCertificateStatus_Constants(t *testing.T) {
tests := map[string]CertificateStatus{
"Pending": CertificateStatusPending,
"Active": CertificateStatusActive,
"Expiring": CertificateStatusExpiring,
"Expired": CertificateStatusExpired,
"RenewalInProgress": CertificateStatusRenewalInProgress,
"Failed": CertificateStatusFailed,
"Revoked": CertificateStatusRevoked,
"Archived": CertificateStatusArchived,
"Pending": CertificateStatusPending,
"Active": CertificateStatusActive,
"Expiring": CertificateStatusExpiring,
"Expired": CertificateStatusExpired,
"RenewalInProgress": CertificateStatusRenewalInProgress,
"Failed": CertificateStatusFailed,
"Revoked": CertificateStatusRevoked,
"Archived": CertificateStatusArchived,
}
for expected, got := range tests {
if string(got) != expected {
+29 -29
View File
@@ -11,7 +11,7 @@ type Issuer struct {
Name string `json:"name"`
Type IssuerType `json:"type"`
Config json.RawMessage `json:"config"`
EncryptedConfig []byte `json:"-"` // AES-GCM encrypted full config (never exposed via API)
EncryptedConfig []byte `json:"-"` // AES-GCM encrypted full config (never exposed via API)
Enabled bool `json:"enabled"`
LastTestedAt *time.Time `json:"last_tested_at,omitempty"`
TestStatus string `json:"test_status,omitempty"`
@@ -27,13 +27,13 @@ type DeploymentTarget struct {
Type TargetType `json:"type"`
AgentID string `json:"agent_id"`
Config json.RawMessage `json:"config"`
EncryptedConfig []byte `json:"-"` // AES-GCM encrypted full config (never exposed via API)
EncryptedConfig []byte `json:"-"` // AES-GCM encrypted full config (never exposed via API)
Enabled bool `json:"enabled"`
LastTestedAt *time.Time `json:"last_tested_at,omitempty"`
TestStatus string `json:"test_status,omitempty"`
Source string `json:"source,omitempty"`
RetiredAt *time.Time `json:"retired_at,omitempty"` // I-004: soft-retirement timestamp (nil = active)
RetiredReason *string `json:"retired_reason,omitempty"` // I-004: reason captured at cascade retirement
RetiredAt *time.Time `json:"retired_at,omitempty"` // I-004: soft-retirement timestamp (nil = active)
RetiredReason *string `json:"retired_reason,omitempty"` // I-004: reason captured at cascade retirement
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
@@ -65,11 +65,11 @@ type Agent struct {
// docs/architecture.md ER diagram (which documents DB shape, not API
// shape) and coverage-gap-audit-2026-04-24-v5/unified-audit.md
// cat-s5-apikey_leak for the full closure rationale.
APIKeyHash string `json:"-"`
OS string `json:"os"`
Architecture string `json:"architecture"`
IPAddress string `json:"ip_address"`
Version string `json:"version"`
APIKeyHash string `json:"-"`
OS string `json:"os"`
Architecture string `json:"architecture"`
IPAddress string `json:"ip_address"`
Version string `json:"version"`
// I-004: soft-retirement fields. An agent with RetiredAt != nil is the
// canonical "retired" state. The Status column remains as before (Online
// / Offline / Degraded) and is preserved at retirement time as the
@@ -115,9 +115,9 @@ func (a *Agent) IsRetired() bool { return a != nil && a.RetiredAt != nil }
// any non-zero count blocks a default retire with HTTP 409 and requires an
// explicit ?force=true&reason=... escape hatch from the operator.
type AgentDependencyCounts struct {
ActiveTargets int `json:"active_targets"` // deployment_targets.agent_id=id AND retired_at IS NULL
ActiveTargets int `json:"active_targets"` // deployment_targets.agent_id=id AND retired_at IS NULL
ActiveCertificates int `json:"active_certificates"` // certificates currently deployed via one of this agent's active targets
PendingJobs int `json:"pending_jobs"` // jobs.agent_id=id AND status IN (Pending, AwaitingCSR, AwaitingApproval, Running)
PendingJobs int `json:"pending_jobs"` // jobs.agent_id=id AND status IN (Pending, AwaitingCSR, AwaitingApproval, Running)
}
// HasDependencies reports whether any preflight counter is non-zero.
@@ -180,14 +180,14 @@ const (
type IssuerType string
const (
IssuerTypeACME IssuerType = "ACME"
IssuerTypeGenericCA IssuerType = "GenericCA"
IssuerTypeStepCA IssuerType = "StepCA"
IssuerTypeOpenSSL IssuerType = "OpenSSL"
IssuerTypeVault IssuerType = "VaultPKI"
IssuerTypeDigiCert IssuerType = "DigiCert"
IssuerTypeSectigo IssuerType = "Sectigo"
IssuerTypeGoogleCAS IssuerType = "GoogleCAS"
IssuerTypeACME IssuerType = "ACME"
IssuerTypeGenericCA IssuerType = "GenericCA"
IssuerTypeStepCA IssuerType = "StepCA"
IssuerTypeOpenSSL IssuerType = "OpenSSL"
IssuerTypeVault IssuerType = "VaultPKI"
IssuerTypeDigiCert IssuerType = "DigiCert"
IssuerTypeSectigo IssuerType = "Sectigo"
IssuerTypeGoogleCAS IssuerType = "GoogleCAS"
IssuerTypeAWSACMPCA IssuerType = "AWSACMPCA"
IssuerTypeEntrust IssuerType = "Entrust"
IssuerTypeGlobalSign IssuerType = "GlobalSign"
@@ -198,16 +198,16 @@ const (
type TargetType string
const (
TargetTypeNGINX TargetType = "NGINX"
TargetTypeApache TargetType = "Apache"
TargetTypeHAProxy TargetType = "HAProxy"
TargetTypeF5 TargetType = "F5"
TargetTypeIIS TargetType = "IIS"
TargetTypeTraefik TargetType = "Traefik"
TargetTypeCaddy TargetType = "Caddy"
TargetTypeEnvoy TargetType = "Envoy"
TargetTypePostfix TargetType = "Postfix"
TargetTypeDovecot TargetType = "Dovecot"
TargetTypeNGINX TargetType = "NGINX"
TargetTypeApache TargetType = "Apache"
TargetTypeHAProxy TargetType = "HAProxy"
TargetTypeF5 TargetType = "F5"
TargetTypeIIS TargetType = "IIS"
TargetTypeTraefik TargetType = "Traefik"
TargetTypeCaddy TargetType = "Caddy"
TargetTypeEnvoy TargetType = "Envoy"
TargetTypePostfix TargetType = "Postfix"
TargetTypeDovecot TargetType = "Dovecot"
TargetTypeSSH TargetType = "SSH"
TargetTypeWinCertStore TargetType = "WinCertStore"
TargetTypeJavaKeystore TargetType = "JavaKeystore"
+35 -35
View File
@@ -24,34 +24,34 @@ func IsValidHealthStatus(s string) bool {
// EndpointHealthCheck represents a monitored TLS endpoint.
type EndpointHealthCheck struct {
ID string `json:"id"`
Endpoint string `json:"endpoint"`
CertificateID *string `json:"certificate_id,omitempty"`
NetworkScanTargetID *string `json:"network_scan_target_id,omitempty"`
ExpectedFingerprint string `json:"expected_fingerprint"`
ObservedFingerprint string `json:"observed_fingerprint"`
Status HealthStatus `json:"status"`
ConsecutiveFailures int `json:"consecutive_failures"`
ResponseTimeMs int `json:"response_time_ms"`
TLSVersion string `json:"tls_version"`
CipherSuite string `json:"cipher_suite"`
CertSubject string `json:"cert_subject"`
CertIssuer string `json:"cert_issuer"`
CertExpiry *time.Time `json:"cert_expiry,omitempty"`
LastCheckedAt *time.Time `json:"last_checked_at,omitempty"`
LastSuccessAt *time.Time `json:"last_success_at,omitempty"`
LastFailureAt *time.Time `json:"last_failure_at,omitempty"`
LastTransitionAt *time.Time `json:"last_transition_at,omitempty"`
FailureReason string `json:"failure_reason"`
DegradedThreshold int `json:"degraded_threshold"`
DownThreshold int `json:"down_threshold"`
CheckIntervalSecs int `json:"check_interval_seconds"`
Enabled bool `json:"enabled"`
Acknowledged bool `json:"acknowledged"`
AcknowledgedBy string `json:"acknowledged_by,omitempty"`
AcknowledgedAt *time.Time `json:"acknowledged_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID string `json:"id"`
Endpoint string `json:"endpoint"`
CertificateID *string `json:"certificate_id,omitempty"`
NetworkScanTargetID *string `json:"network_scan_target_id,omitempty"`
ExpectedFingerprint string `json:"expected_fingerprint"`
ObservedFingerprint string `json:"observed_fingerprint"`
Status HealthStatus `json:"status"`
ConsecutiveFailures int `json:"consecutive_failures"`
ResponseTimeMs int `json:"response_time_ms"`
TLSVersion string `json:"tls_version"`
CipherSuite string `json:"cipher_suite"`
CertSubject string `json:"cert_subject"`
CertIssuer string `json:"cert_issuer"`
CertExpiry *time.Time `json:"cert_expiry,omitempty"`
LastCheckedAt *time.Time `json:"last_checked_at,omitempty"`
LastSuccessAt *time.Time `json:"last_success_at,omitempty"`
LastFailureAt *time.Time `json:"last_failure_at,omitempty"`
LastTransitionAt *time.Time `json:"last_transition_at,omitempty"`
FailureReason string `json:"failure_reason"`
DegradedThreshold int `json:"degraded_threshold"`
DownThreshold int `json:"down_threshold"`
CheckIntervalSecs int `json:"check_interval_seconds"`
Enabled bool `json:"enabled"`
Acknowledged bool `json:"acknowledged"`
AcknowledgedBy string `json:"acknowledged_by,omitempty"`
AcknowledgedAt *time.Time `json:"acknowledged_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// TransitionStatus computes the new health status based on the probe result.
@@ -89,13 +89,13 @@ func (h *EndpointHealthCheck) TransitionStatus(probeSuccess bool, observedFinger
// HealthHistoryEntry represents a single probe record.
type HealthHistoryEntry struct {
ID string `json:"id"`
HealthCheckID string `json:"health_check_id"`
Status string `json:"status"`
ResponseTimeMs int `json:"response_time_ms"`
Fingerprint string `json:"fingerprint"`
FailureReason string `json:"failure_reason"`
CheckedAt time.Time `json:"checked_at"`
ID string `json:"id"`
HealthCheckID string `json:"health_check_id"`
Status string `json:"status"`
ResponseTimeMs int `json:"response_time_ms"`
Fingerprint string `json:"fingerprint"`
FailureReason string `json:"failure_reason"`
CheckedAt time.Time `json:"checked_at"`
}
// HealthCheckSummary contains aggregate counts by status.
+17 -17
View File
@@ -7,23 +7,23 @@ import (
// Job represents a unit of work in the certificate control plane.
type Job struct {
ID string `json:"id"`
Type JobType `json:"type"`
CertificateID string `json:"certificate_id"`
TargetID *string `json:"target_id,omitempty"`
AgentID *string `json:"agent_id,omitempty"`
Status JobStatus `json:"status"`
Attempts int `json:"attempts"`
MaxAttempts int `json:"max_attempts"`
LastError *string `json:"last_error,omitempty"`
ScheduledAt time.Time `json:"scheduled_at"`
StartedAt *time.Time `json:"started_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
VerificationStatus VerificationStatus `json:"verification_status"`
VerifiedAt *time.Time `json:"verified_at,omitempty"`
VerificationError *string `json:"verification_error,omitempty"`
VerificationFp *string `json:"verification_fingerprint,omitempty"`
ID string `json:"id"`
Type JobType `json:"type"`
CertificateID string `json:"certificate_id"`
TargetID *string `json:"target_id,omitempty"`
AgentID *string `json:"agent_id,omitempty"`
Status JobStatus `json:"status"`
Attempts int `json:"attempts"`
MaxAttempts int `json:"max_attempts"`
LastError *string `json:"last_error,omitempty"`
ScheduledAt time.Time `json:"scheduled_at"`
StartedAt *time.Time `json:"started_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
VerificationStatus VerificationStatus `json:"verification_status"`
VerifiedAt *time.Time `json:"verified_at,omitempty"`
VerificationError *string `json:"verification_error,omitempty"`
VerificationFp *string `json:"verification_fingerprint,omitempty"`
}
// JobType represents the classification of work to be performed.
+9 -9
View File
@@ -104,15 +104,15 @@ func TestNotificationEvent_RetryFields(t *testing.T) {
next := time.Now().Add(2 * time.Minute)
lastErr := "connection refused"
event := &NotificationEvent{
ID: "notif-retry-001",
Type: NotificationTypeExpirationWarning,
Channel: NotificationChannelWebhook,
Recipient: "https://hooks.example.com/certs",
Message: "retry me",
Status: string(NotificationStatusFailed),
RetryCount: 3,
NextRetryAt: &next,
LastError: &lastErr,
ID: "notif-retry-001",
Type: NotificationTypeExpirationWarning,
Channel: NotificationChannelWebhook,
Recipient: "https://hooks.example.com/certs",
Message: "retry me",
Status: string(NotificationStatusFailed),
RetryCount: 3,
NextRetryAt: &next,
LastError: &lastErr,
}
if event.RetryCount != 3 {
+4 -4
View File
@@ -12,10 +12,10 @@ import "time"
type OCSPResponseCacheEntry struct {
IssuerID string `json:"issuer_id"`
SerialHex string `json:"serial_hex"`
ResponseDER []byte `json:"-"` // raw DER, omitted from admin JSON to keep responses lean
CertStatus string `json:"cert_status"` // "good" | "revoked" | "unknown"
RevocationReason int `json:"revocation_reason,omitempty"` // only set when CertStatus == "revoked"
RevokedAt time.Time `json:"revoked_at,omitempty"` // only set when CertStatus == "revoked"
ResponseDER []byte `json:"-"` // raw DER, omitted from admin JSON to keep responses lean
CertStatus string `json:"cert_status"` // "good" | "revoked" | "unknown"
RevocationReason int `json:"revocation_reason,omitempty"` // only set when CertStatus == "revoked"
RevokedAt time.Time `json:"revoked_at,omitempty"` // only set when CertStatus == "revoked"
ThisUpdate time.Time `json:"this_update"`
NextUpdate time.Time `json:"next_update"`
GeneratedAt time.Time `json:"generated_at"`
+6 -6
View File
@@ -21,12 +21,12 @@ type PolicyRule struct {
type PolicyType string
const (
PolicyTypeAllowedIssuers PolicyType = "AllowedIssuers"
PolicyTypeAllowedDomains PolicyType = "AllowedDomains"
PolicyTypeRequiredMetadata PolicyType = "RequiredMetadata"
PolicyTypeAllowedEnvironments PolicyType = "AllowedEnvironments"
PolicyTypeRenewalLeadTime PolicyType = "RenewalLeadTime"
PolicyTypeCertificateLifetime PolicyType = "CertificateLifetime"
PolicyTypeAllowedIssuers PolicyType = "AllowedIssuers"
PolicyTypeAllowedDomains PolicyType = "AllowedDomains"
PolicyTypeRequiredMetadata PolicyType = "RequiredMetadata"
PolicyTypeAllowedEnvironments PolicyType = "AllowedEnvironments"
PolicyTypeRenewalLeadTime PolicyType = "RenewalLeadTime"
PolicyTypeCertificateLifetime PolicyType = "CertificateLifetime"
)
// PolicyViolation records an instance of a certificate violating a policy rule.
+10 -10
View File
@@ -704,17 +704,17 @@ func TestM20EnhancedQueryAPI(t *testing.T) {
// Setup: Create a certificate for testing
now := time.Now()
cert := &domain.ManagedCertificate{
ID: "mc-m20-test-1",
Name: "M20 Test Cert",
CommonName: "m20.example.com",
Environment: "production",
Status: domain.CertificateStatusActive,
IssuerID: "iss-local",
OwnerID: "owner-ops",
TeamID: "team-platform",
ID: "mc-m20-test-1",
Name: "M20 Test Cert",
CommonName: "m20.example.com",
Environment: "production",
Status: domain.CertificateStatusActive,
IssuerID: "iss-local",
OwnerID: "owner-ops",
TeamID: "team-platform",
CertificateProfileID: "prof-standard",
CreatedAt: now,
UpdatedAt: now,
CreatedAt: now,
UpdatedAt: now,
}
certRepo.certs["mc-m20-test-1"] = cert
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"time"
"github.com/google/uuid"
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"time"
"github.com/shankar0123/certctl/internal/domain"
+5 -5
View File
@@ -52,11 +52,11 @@ func TestWrapPingError_AuthFailureGuidance(t *testing.T) {
// Contract elements — the operator-facing string is what we ship.
wantSubstrings := []string{
"SQLSTATE 28P01", // operators grep on this
"POSTGRES_PASSWORD", // names the variable that traps
"first boot", // the mechanism in plain language
"down -v", // destructive remediation
"ALTER ROLE", // non-destructive remediation
"SQLSTATE 28P01", // operators grep on this
"POSTGRES_PASSWORD", // names the variable that traps
"first boot", // the mechanism in plain language
"down -v", // destructive remediation
"ALTER ROLE", // non-destructive remediation
}
for _, s := range wantSubstrings {
if !strings.Contains(got, s) {
@@ -235,8 +235,8 @@ func TestHealthCheckRepository_ListDueForCheck(t *testing.T) {
ctx := context.Background()
now := time.Now().UTC().Truncate(time.Microsecond)
pastDue := now.Add(-10 * time.Minute) // > 300s ago, enabled → due
recent := now.Add(-30 * time.Second) // < 300s ago, enabled → not due
pastDue := now.Add(-10 * time.Minute) // > 300s ago, enabled → due
recent := now.Add(-30 * time.Second) // < 300s ago, enabled → not due
// (a) enabled + null last_checked_at — NULLS FIRST puts this first
a := newHealthCheck("hc-due-a", "a.example.com:443", domain.HealthStatusUnknown, true)
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"github.com/google/uuid"
"github.com/shankar0123/certctl/internal/domain"
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"time"
"github.com/google/uuid"
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"time"
"github.com/lib/pq"
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"github.com/google/uuid"
"github.com/shankar0123/certctl/internal/domain"
+14 -14
View File
@@ -319,7 +319,7 @@ func TestCertificateRepository_GetExpiringCertificates(t *testing.T) {
ID: tc.id, Name: tc.id, CommonName: tc.id + ".example.com",
SANs: []string{}, OwnerID: ownerID, TeamID: teamID,
IssuerID: issuerID, RenewalPolicyID: policyID,
Status: domain.CertificateStatusActive,
Status: domain.CertificateStatusActive,
ExpiresAt: tc.expires, Tags: map[string]string{},
CreatedAt: now, UpdatedAt: now,
}
@@ -780,7 +780,7 @@ func TestJobRepository_CRUD(t *testing.T) {
ID: "mc-job-test", Name: "job-test", CommonName: "job.example.com",
SANs: []string{}, OwnerID: ownerID, TeamID: teamID,
IssuerID: issuerID, RenewalPolicyID: policyID,
Status: domain.CertificateStatusActive,
Status: domain.CertificateStatusActive,
ExpiresAt: now.Add(30 * 24 * time.Hour), Tags: map[string]string{},
CreatedAt: now, UpdatedAt: now,
}
@@ -871,7 +871,7 @@ func TestRevocationRepository_CRUD(t *testing.T) {
ID: "mc-rev-test", Name: "rev-test", CommonName: "rev.example.com",
SANs: []string{}, OwnerID: ownerID, TeamID: teamID,
IssuerID: issuerID, RenewalPolicyID: policyID,
Status: domain.CertificateStatusRevoked,
Status: domain.CertificateStatusRevoked,
ExpiresAt: now.Add(30 * 24 * time.Hour), Tags: map[string]string{},
CreatedAt: now, UpdatedAt: now,
}
@@ -1250,12 +1250,12 @@ func TestProfileRepository_CRUD(t *testing.T) {
{Algorithm: "RSA", MinSize: 2048},
{Algorithm: "ECDSA", MinSize: 256},
},
MaxTTLSeconds: 86400,
AllowedEKUs: []string{"serverAuth"},
AllowShortLived: false,
Enabled: true,
CreatedAt: now,
UpdatedAt: now,
MaxTTLSeconds: 86400,
AllowedEKUs: []string{"serverAuth"},
AllowShortLived: false,
Enabled: true,
CreatedAt: now,
UpdatedAt: now,
}
if err := repo.Create(ctx, profile); err != nil {
@@ -1306,7 +1306,7 @@ func TestNotificationRepository_CRUD(t *testing.T) {
ID: "mc-notif-test", Name: "notif-test", CommonName: "notif.example.com",
SANs: []string{}, OwnerID: ownerID, TeamID: teamID,
IssuerID: issuerID, RenewalPolicyID: policyID,
Status: domain.CertificateStatusActive,
Status: domain.CertificateStatusActive,
ExpiresAt: now.Add(30 * 24 * time.Hour), Tags: map[string]string{},
CreatedAt: now, UpdatedAt: now,
}
@@ -1432,7 +1432,7 @@ func TestDiscoveryRepository_DiscoveredCertCRUD(t *testing.T) {
ID: "mc-linked-cert", Name: "linked-cert", CommonName: "linked.example.com",
SANs: []string{}, OwnerID: ownerID, TeamID: teamID,
IssuerID: issuerID, RenewalPolicyID: policyID,
Status: domain.CertificateStatusActive,
Status: domain.CertificateStatusActive,
ExpiresAt: now.Add(90 * 24 * time.Hour), Tags: map[string]string{},
CreatedAt: now, UpdatedAt: now,
}
@@ -1447,7 +1447,7 @@ func TestDiscoveryRepository_DiscoveredCertCRUD(t *testing.T) {
NotBefore: &notBefore, NotAfter: &notAfter, KeyAlgorithm: "RSA", KeySize: 2048,
IsCA: false, PEMData: "---PEM---", SourcePath: "/etc/ssl/certs/disc.pem",
SourceFormat: "PEM", AgentID: "agent-dcert-test",
Status: domain.DiscoveryStatusUnmanaged,
Status: domain.DiscoveryStatusUnmanaged,
FirstSeenAt: now, LastSeenAt: now, CreatedAt: now, UpdatedAt: now,
}
@@ -1577,8 +1577,8 @@ func TestNetworkScanRepository_CRUD(t *testing.T) {
target := &domain.NetworkScanTarget{
ID: "ns-test-1", Name: "Internal Network",
CIDRs: []string{"10.0.0.0/24", "192.168.1.0/24"},
Ports: []int64{443, 8443},
CIDRs: []string{"10.0.0.0/24", "192.168.1.0/24"},
Ports: []int64{443, 8443},
Enabled: true, ScanIntervalHours: 6, TimeoutMs: 5000,
CreatedAt: now, UpdatedAt: now,
}
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"github.com/shankar0123/certctl/internal/domain"
)
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"github.com/google/uuid"
"github.com/shankar0123/certctl/internal/domain"
+1 -1
View File
@@ -1,10 +1,10 @@
package postgres
import (
"github.com/shankar0123/certctl/internal/repository"
"context"
"database/sql"
"fmt"
"github.com/shankar0123/certctl/internal/repository"
"github.com/google/uuid"
"github.com/shankar0123/certctl/internal/domain"
+8 -9
View File
@@ -11,14 +11,14 @@ import (
// mockRenewalService is a mock implementation for testing.
type mockRenewalService struct {
mu sync.Mutex
callCount int
callTimes []time.Time
expireCallCount int
expireCallTimes []time.Time
slowDelay time.Duration
shouldError bool
blockCh chan struct{} // if non-nil, blocks until closed (ignores context)
mu sync.Mutex
callCount int
callTimes []time.Time
expireCallCount int
expireCallTimes []time.Time
slowDelay time.Duration
shouldError bool
blockCh chan struct{} // if non-nil, blocks until closed (ignores context)
}
func (m *mockRenewalService) CheckExpiringCertificates(ctx context.Context) error {
@@ -138,7 +138,6 @@ func (m *mockJobService) RetryFailedJobs(ctx context.Context, maxRetries int) er
return nil
}
// ReapTimedOutJobs is the scheduler-driven counterpart to ProcessPendingJobs that
// covers coverage gap I-003: JobService.ReapTimedOutJobs (via JobReaperService interface)
// had no runtime caller prior to the jobTimeoutLoop being wired.
+10 -10
View File
@@ -12,16 +12,16 @@ import (
// mockAgentGroupRepo is a test implementation of AgentGroupRepository
type mockAgentGroupRepo struct {
groups map[string]*domain.AgentGroup
members map[string][]*domain.Agent
CreateErr error
UpdateErr error
DeleteErr error
GetErr error
ListErr error
ListMembersErr error
AddMemberErr error
RemoveMemberErr error
groups map[string]*domain.AgentGroup
members map[string][]*domain.Agent
CreateErr error
UpdateErr error
DeleteErr error
GetErr error
ListErr error
ListMembersErr error
AddMemberErr error
RemoveMemberErr error
}
func newMockAgentGroupRepository() *mockAgentGroupRepo {
+3 -3
View File
@@ -109,9 +109,9 @@ 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,
ID: "j-1",
AgentID: strPtr("a-1"),
Status: domain.JobStatusRunning,
}
err := svc.UpdateJobStatus(context.Background(), "a-1", "j-1", "Completed", "")
if err != nil {
-1
View File
@@ -10,7 +10,6 @@ import (
"github.com/shankar0123/certctl/internal/domain"
)
func TestRegisterAgent(t *testing.T) {
ctx := context.Background()
agentRepo := &mockAgentRepo{
+22 -22
View File
@@ -73,29 +73,29 @@ var credentialKeys = map[string]bool{
// Note `ip_address` is debatable — useful for forensics but flagged by
// GDPR Art. 32 — defaulting to redact, operators can audit + adjust.
var piiKeys = map[string]bool{
"email": true,
"email_address": true,
"phone": true,
"phone_number": true,
"telephone": true,
"ssn": true,
"email": true,
"email_address": true,
"phone": true,
"phone_number": true,
"telephone": true,
"ssn": true,
"social_security": true,
"dob": true,
"date_of_birth": true,
"name": true,
"full_name": true,
"first_name": true,
"last_name": true,
"surname": true,
"address": true,
"street": true,
"street_address": true,
"city": true,
"postal_code": true,
"zip": true,
"zipcode": true,
"ip": true,
"ip_address": true,
"dob": true,
"date_of_birth": true,
"name": true,
"full_name": true,
"first_name": true,
"last_name": true,
"surname": true,
"address": true,
"street": true,
"street_address": true,
"city": true,
"postal_code": true,
"zip": true,
"zipcode": true,
"ip": true,
"ip_address": true,
}
// RedactDetailsForAudit walks a details map and returns a NEW map with
+8 -8
View File
@@ -30,8 +30,8 @@ func TestRedactDetailsForAudit_CredentialKeys(t *testing.T) {
for _, key := range cases {
t.Run(key, func(t *testing.T) {
in := map[string]interface{}{
key: "sensitive-value-do-not-leak",
"non_sensitive_id": "ok-public-id",
key: "sensitive-value-do-not-leak",
"non_sensitive_id": "ok-public-id",
}
out := RedactDetailsForAudit(in)
if out[key] != "[REDACTED:CREDENTIAL]" {
@@ -70,7 +70,7 @@ func TestRedactDetailsForAudit_NestedMap(t *testing.T) {
in := map[string]interface{}{
"resource_id": "iss-prod",
"config": map[string]interface{}{
"endpoint": "https://acme.example.com",
"endpoint": "https://acme.example.com",
"eab_secret": "do-not-leak-this-secret",
"contact": map[string]interface{}{
"email": "ops@example.com",
@@ -159,8 +159,8 @@ func TestRedactDetailsForAudit_NoRedactionPath(t *testing.T) {
// Maps with no sensitive keys should NOT have a redacted_keys array
// — clutter-free for the common case.
in := map[string]interface{}{
"action": "create_certificate",
"cert_id": "mc-prod-001",
"action": "create_certificate",
"cert_id": "mc-prod-001",
"latency_ms": float64(42),
}
out := RedactDetailsForAudit(in)
@@ -171,7 +171,7 @@ func TestRedactDetailsForAudit_NoRedactionPath(t *testing.T) {
func TestRedactDetailsForAudit_DoesNotMutateInput(t *testing.T) {
in := map[string]interface{}{
"api_key": "secret-do-not-leak",
"api_key": "secret-do-not-leak",
"resource": "iss-prod",
}
_ = RedactDetailsForAudit(in)
@@ -197,8 +197,8 @@ func TestRedactDetailsForAudit_JSONRoundTrip(t *testing.T) {
// The redacted map MUST round-trip through json.Marshal (the
// AuditService persistence path). Catches type-assertion regressions.
in := map[string]interface{}{
"reason": "compromised-key",
"api_key": "leak-me",
"reason": "compromised-key",
"api_key": "leak-me",
"contacts": []interface{}{
map[string]interface{}{"email": "ops@example.com"},
},
+2 -2
View File
@@ -67,8 +67,8 @@ func TestBulkReassign_HappyPath(t *testing.T) {
func TestBulkReassign_SkipsAlreadyOwned(t *testing.T) {
svc, certRepo, ownerRepo, _ := newBulkReassignmentTestService()
addOwner(ownerRepo, "o-bob")
addOwnedCert(certRepo, "mc-1", "o-bob", "") // already owned by target
addOwnedCert(certRepo, "mc-2", "o-alice", "") // needs reassign
addOwnedCert(certRepo, "mc-1", "o-bob", "") // already owned by target
addOwnedCert(certRepo, "mc-2", "o-alice", "") // needs reassign
res, err := svc.BulkReassign(context.Background(),
domain.BulkReassignmentRequest{
+14 -14
View File
@@ -32,11 +32,11 @@ func newBulkRevocationTestService() (*BulkRevocationService, *mockCertRepo, *moc
func addTestCert(repo *mockCertRepo, id, status, issuerID string) {
cert := &domain.ManagedCertificate{
ID: id,
ID: id,
CommonName: id + ".example.com",
Status: domain.CertificateStatus(status),
IssuerID: issuerID,
ExpiresAt: time.Now().AddDate(0, 6, 0),
Status: domain.CertificateStatus(status),
IssuerID: issuerID,
ExpiresAt: time.Now().AddDate(0, 6, 0),
}
repo.AddCert(cert)
// Add a version with serial number (needed by RevokeCertificateWithActor)
@@ -54,13 +54,13 @@ func addTestCert(repo *mockCertRepo, id, status, issuerID string) {
func addTestCertWithProfile(repo *mockCertRepo, id, status, issuerID, profileID, ownerID string) {
cert := &domain.ManagedCertificate{
ID: id,
CommonName: id + ".example.com",
Status: domain.CertificateStatus(status),
IssuerID: issuerID,
ID: id,
CommonName: id + ".example.com",
Status: domain.CertificateStatus(status),
IssuerID: issuerID,
CertificateProfileID: profileID,
OwnerID: ownerID,
ExpiresAt: time.Now().AddDate(0, 6, 0),
OwnerID: ownerID,
ExpiresAt: time.Now().AddDate(0, 6, 0),
}
repo.AddCert(cert)
repo.Versions[id] = []*domain.CertificateVersion{
@@ -272,11 +272,11 @@ func TestBulkRevoke_PartialFailure(t *testing.T) {
addTestCert(certRepo, "mc-1", "Active", "iss-local")
// mc-2 is active but has NO version — RevokeCertificateWithActor will fail on GetLatestVersion
cert2 := &domain.ManagedCertificate{
ID: "mc-2",
ID: "mc-2",
CommonName: "mc-2.example.com",
Status: domain.CertificateStatusActive,
IssuerID: "iss-local",
ExpiresAt: time.Now().AddDate(0, 6, 0),
Status: domain.CertificateStatusActive,
IssuerID: "iss-local",
ExpiresAt: time.Now().AddDate(0, 6, 0),
}
certRepo.AddCert(cert2)
// Don't add versions for mc-2 so GetLatestVersion returns errNotFound
+11 -11
View File
@@ -266,17 +266,17 @@ func TestDeploymentService_ProcessDeploymentJob_Success(t *testing.T) {
// Add agent with recent heartbeat
now := time.Now()
agent := &domain.Agent{
ID: "agent-1",
Name: "Test Agent",
Hostname: "agent.example.com",
Status: domain.AgentStatusOnline,
LastHeartbeatAt: &now,
RegisteredAt: time.Now(),
APIKeyHash: "hash-1",
OS: "linux",
Architecture: "amd64",
IPAddress: "192.168.1.1",
Version: "1.0.0",
ID: "agent-1",
Name: "Test Agent",
Hostname: "agent.example.com",
Status: domain.AgentStatusOnline,
LastHeartbeatAt: &now,
RegisteredAt: time.Now(),
APIKeyHash: "hash-1",
OS: "linux",
Architecture: "amd64",
IPAddress: "192.168.1.1",
Version: "1.0.0",
}
agentRepo.AddAgent(agent)
+14 -14
View File
@@ -31,20 +31,20 @@ type HTMLEmailSender interface {
// DigestData holds the aggregated data for a digest email.
type DigestData struct {
GeneratedAt time.Time `json:"generated_at"`
TotalCertificates int64 `json:"total_certificates"`
ExpiringCertificates int64 `json:"expiring_certificates"`
ExpiredCertificates int64 `json:"expired_certificates"`
RevokedCertificates int64 `json:"revoked_certificates"`
ActiveAgents int64 `json:"active_agents"`
OfflineAgents int64 `json:"offline_agents"`
TotalAgents int64 `json:"total_agents"`
PendingJobs int64 `json:"pending_jobs"`
FailedJobs int64 `json:"failed_jobs"`
CompletedJobs int64 `json:"completed_jobs"`
ExpiringCerts []DigestCertEntry `json:"expiring_certs"`
RecentFailures []DigestJobEntry `json:"recent_failures"`
StatusCounts []DigestStatusCount `json:"status_counts"`
GeneratedAt time.Time `json:"generated_at"`
TotalCertificates int64 `json:"total_certificates"`
ExpiringCertificates int64 `json:"expiring_certificates"`
ExpiredCertificates int64 `json:"expired_certificates"`
RevokedCertificates int64 `json:"revoked_certificates"`
ActiveAgents int64 `json:"active_agents"`
OfflineAgents int64 `json:"offline_agents"`
TotalAgents int64 `json:"total_agents"`
PendingJobs int64 `json:"pending_jobs"`
FailedJobs int64 `json:"failed_jobs"`
CompletedJobs int64 `json:"completed_jobs"`
ExpiringCerts []DigestCertEntry `json:"expiring_certs"`
RecentFailures []DigestJobEntry `json:"recent_failures"`
StatusCounts []DigestStatusCount `json:"status_counts"`
}
// DigestCertEntry represents a certificate entry in the digest.
+54 -54
View File
@@ -12,17 +12,17 @@ import (
// mockDiscoveryRepo is a test implementation of DiscoveryRepository
type mockDiscoveryRepo struct {
Scans map[string]*domain.DiscoveryScan
Discovered map[string]*domain.DiscoveredCertificate
CreateScanErr error
GetScanErr error
ListScansErr error
Scans map[string]*domain.DiscoveryScan
Discovered map[string]*domain.DiscoveredCertificate
CreateScanErr error
GetScanErr error
ListScansErr error
CreateDiscoveredErr error
GetDiscoveredErr error
ListDiscoveredErr error
UpdateStatusErr error
GetDiscoveredErr error
ListDiscoveredErr error
UpdateStatusErr error
GetByFingerprintErr error
CountByStatusErr error
CountByStatusErr error
}
func newMockDiscoveryRepository() *mockDiscoveryRepo {
@@ -268,20 +268,20 @@ func TestListDiscovered_Success(t *testing.T) {
now := time.Now()
cert1 := &domain.DiscoveredCertificate{
ID: "dcert-1",
AgentID: "agent-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
ID: "dcert-1",
AgentID: "agent-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
}
cert2 := &domain.DiscoveredCertificate{
ID: "dcert-2",
AgentID: "agent-1",
CommonName: "api.example.com",
Status: domain.DiscoveryStatusManaged,
CreatedAt: now,
UpdatedAt: now,
ID: "dcert-2",
AgentID: "agent-1",
CommonName: "api.example.com",
Status: domain.DiscoveryStatusManaged,
CreatedAt: now,
UpdatedAt: now,
}
discoveryRepo.Discovered[cert1.ID] = cert1
discoveryRepo.Discovered[cert2.ID] = cert2
@@ -304,20 +304,20 @@ func TestListDiscovered_WithStatusFilter(t *testing.T) {
now := time.Now()
cert1 := &domain.DiscoveredCertificate{
ID: "dcert-1",
AgentID: "agent-1",
ID: "dcert-1",
AgentID: "agent-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
}
cert2 := &domain.DiscoveredCertificate{
ID: "dcert-2",
AgentID: "agent-1",
ID: "dcert-2",
AgentID: "agent-1",
CommonName: "api.example.com",
Status: domain.DiscoveryStatusManaged,
CreatedAt: now,
UpdatedAt: now,
Status: domain.DiscoveryStatusManaged,
CreatedAt: now,
UpdatedAt: now,
}
discoveryRepo.Discovered[cert1.ID] = cert1
discoveryRepo.Discovered[cert2.ID] = cert2
@@ -340,11 +340,11 @@ func TestGetDiscovered_Success(t *testing.T) {
now := time.Now()
cert := &domain.DiscoveredCertificate{
ID: "dcert-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
ID: "dcert-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
}
discoveryRepo.Discovered[cert.ID] = cert
@@ -363,12 +363,12 @@ func TestClaimDiscovered_Success(t *testing.T) {
now := time.Now()
discoveredCert := &domain.DiscoveredCertificate{
ID: "dcert-1",
CommonName: "example.com",
FingerprintSHA256: "abc123",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
ID: "dcert-1",
CommonName: "example.com",
FingerprintSHA256: "abc123",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
}
discoveryRepo.Discovered[discoveredCert.ID] = discoveredCert
@@ -415,11 +415,11 @@ func TestClaimDiscovered_MissingManagedCertID(t *testing.T) {
now := time.Now()
cert := &domain.DiscoveredCertificate{
ID: "dcert-1",
ID: "dcert-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
}
discoveryRepo.Discovered[cert.ID] = cert
@@ -434,11 +434,11 @@ func TestClaimDiscovered_ManagedCertNotFound(t *testing.T) {
now := time.Now()
cert := &domain.DiscoveredCertificate{
ID: "dcert-1",
ID: "dcert-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
}
discoveryRepo.Discovered[cert.ID] = cert
@@ -456,11 +456,11 @@ func TestDismissDiscovered_Success(t *testing.T) {
now := time.Now()
cert := &domain.DiscoveredCertificate{
ID: "dcert-1",
ID: "dcert-1",
CommonName: "example.com",
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
Status: domain.DiscoveryStatusUnmanaged,
CreatedAt: now,
UpdatedAt: now,
}
discoveryRepo.Discovered[cert.ID] = cert
+3 -3
View File
@@ -71,10 +71,10 @@ func TestExportPEM_Success(t *testing.T) {
Status: domain.CertificateStatusActive,
},
&domain.CertificateVersion{
ID: "cv-1",
ID: "cv-1",
CertificateID: "mc-test-1",
SerialNumber: "abc123",
PEMChain: fullPEM,
SerialNumber: "abc123",
PEMChain: fullPEM,
},
)
auditSvc := &AuditService{auditRepo: &mockAuditRepo{}}
+16 -16
View File
@@ -14,19 +14,19 @@ import (
// mockHealthCheckRepo implements the HealthCheckRepository interface for testing.
type mockHealthCheckRepo struct {
checks map[string]*domain.EndpointHealthCheck
history []*domain.HealthHistoryEntry
createErr error
getErr error
updateErr error
deleteErr error
listErr error
listDueErr error
getHistoryErr error
recordHistoryErr error
purgeHistoryErr error
getSummaryErr error
getSummaryResult *domain.HealthCheckSummary
checks map[string]*domain.EndpointHealthCheck
history []*domain.HealthHistoryEntry
createErr error
getErr error
updateErr error
deleteErr error
listErr error
listDueErr error
getHistoryErr error
recordHistoryErr error
purgeHistoryErr error
getSummaryErr error
getSummaryResult *domain.HealthCheckSummary
}
func newMockHealthCheckRepo() *mockHealthCheckRepo {
@@ -151,9 +151,9 @@ func TestHealthCheckService_Create_Success(t *testing.T) {
svc := NewHealthCheckService(repo, nil, logger, 10, 5*time.Second, 30*24*time.Hour, false)
check := &domain.EndpointHealthCheck{
Endpoint: "example.com:443",
Status: domain.HealthStatusUnknown,
Enabled: true,
Endpoint: "example.com:443",
Status: domain.HealthStatusUnknown,
Enabled: true,
CheckIntervalSecs: 300,
}
+1 -1
View File
@@ -1,12 +1,12 @@
package service
import (
"time"
"context"
"errors"
"fmt"
"log/slog"
"strings"
"time"
"github.com/shankar0123/certctl/internal/domain"
"github.com/shankar0123/certctl/internal/repository"

Some files were not shown because too many files have changed in this diff Show More