mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-10 13:48:53 +00:00
test + docs: close 12 test gaps (~250 new tests) and expand testing guide to 34 parts
Implements all P0-P2 test gaps from docs/test-gap-prompt.md: - Deployment service tests (20), target service tests (18), scheduler tests (8) - Agent binary tests (48), CSR renewal tests (8), short-lived cert tests (7) - Domain model tests (25), context cancellation tests (9), concurrency tests (7) - Handler negative-path tests (23 across 5 files) - Frontend error handling tests (86) and API client tests (7) Expands testing-guide.md from 28 to 34 parts covering certificate export, S/MIME/EKU, OCSP/DER CRL, body size limits, Apache/HAProxy connectors, and sub-CA mode. Fixes stale profile count (4->5) and updates sign-off table. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAgentGroup_HasDynamicCriteria_True(t *testing.T) {
|
||||
tests := []AgentGroup{
|
||||
{MatchOS: "linux"},
|
||||
{MatchArchitecture: "amd64"},
|
||||
{MatchIPCIDR: "192.168.1.0/24"},
|
||||
{MatchVersion: "1.0.0"},
|
||||
{MatchOS: "linux", MatchArchitecture: "amd64"},
|
||||
}
|
||||
for i, g := range tests {
|
||||
if !g.HasDynamicCriteria() {
|
||||
t.Errorf("test %d: expected HasDynamicCriteria=true, got false", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_HasDynamicCriteria_False(t *testing.T) {
|
||||
tests := []AgentGroup{
|
||||
{},
|
||||
{Name: "test-group"},
|
||||
{Description: "some description"},
|
||||
{Name: "test-group", Description: "description", Enabled: true},
|
||||
}
|
||||
for i, g := range tests {
|
||||
if g.HasDynamicCriteria() {
|
||||
t.Errorf("test %d: expected HasDynamicCriteria=false, got true", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_AllCriteriaMatch(t *testing.T) {
|
||||
group := &AgentGroup{
|
||||
MatchOS: "linux",
|
||||
MatchArchitecture: "amd64",
|
||||
MatchVersion: "1.0.0",
|
||||
MatchIPCIDR: "192.168.1.1",
|
||||
}
|
||||
|
||||
agent := &Agent{
|
||||
OS: "linux",
|
||||
Architecture: "amd64",
|
||||
Version: "1.0.0",
|
||||
IPAddress: "192.168.1.1",
|
||||
}
|
||||
|
||||
if !group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_OSMismatch(t *testing.T) {
|
||||
group := &AgentGroup{
|
||||
MatchOS: "linux",
|
||||
}
|
||||
|
||||
agent := &Agent{
|
||||
OS: "darwin",
|
||||
}
|
||||
|
||||
if group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=false (OS mismatch), got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_ArchMismatch(t *testing.T) {
|
||||
group := &AgentGroup{
|
||||
MatchArchitecture: "amd64",
|
||||
}
|
||||
|
||||
agent := &Agent{
|
||||
Architecture: "arm64",
|
||||
}
|
||||
|
||||
if group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=false (architecture mismatch), got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_VersionMismatch(t *testing.T) {
|
||||
group := &AgentGroup{
|
||||
MatchVersion: "1.0.0",
|
||||
}
|
||||
|
||||
agent := &Agent{
|
||||
Version: "2.0.0",
|
||||
}
|
||||
|
||||
if group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=false (version mismatch), got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_IPMismatch(t *testing.T) {
|
||||
group := &AgentGroup{
|
||||
MatchIPCIDR: "192.168.1.1",
|
||||
}
|
||||
|
||||
agent := &Agent{
|
||||
IPAddress: "192.168.1.2",
|
||||
}
|
||||
|
||||
if group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=false (IP mismatch), got true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_EmptyCriteriaMatchesAll(t *testing.T) {
|
||||
group := &AgentGroup{}
|
||||
|
||||
agent := &Agent{
|
||||
OS: "linux",
|
||||
Architecture: "amd64",
|
||||
Version: "1.0.0",
|
||||
IPAddress: "192.168.1.1",
|
||||
}
|
||||
|
||||
if !group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=true (empty criteria matches all), got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_PartialCriteria(t *testing.T) {
|
||||
group := &AgentGroup{
|
||||
MatchOS: "linux",
|
||||
MatchArchitecture: "amd64",
|
||||
}
|
||||
|
||||
agent := &Agent{
|
||||
OS: "linux",
|
||||
Architecture: "amd64",
|
||||
Version: "1.0.0",
|
||||
IPAddress: "192.168.1.1",
|
||||
}
|
||||
|
||||
if !group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=true (partial criteria), got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentGroup_MatchesAgent_MultipleMatches(t *testing.T) {
|
||||
group := &AgentGroup{
|
||||
MatchOS: "linux",
|
||||
MatchArchitecture: "amd64",
|
||||
MatchVersion: "1.0.0",
|
||||
}
|
||||
|
||||
// Matching agent
|
||||
agent := &Agent{
|
||||
OS: "linux",
|
||||
Architecture: "amd64",
|
||||
Version: "1.0.0",
|
||||
}
|
||||
|
||||
if !group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=true for matching agent, got false")
|
||||
}
|
||||
|
||||
// Non-matching agent (version mismatch)
|
||||
agent.Version = "0.9.0"
|
||||
if group.MatchesAgent(agent) {
|
||||
t.Errorf("expected MatchesAgent=false for non-matching agent, got true")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package domain
|
||||
|
||||
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,
|
||||
}
|
||||
for expected, got := range tests {
|
||||
if string(got) != expected {
|
||||
t.Errorf("expected %q, got %q", expected, string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAlertThresholds(t *testing.T) {
|
||||
defaults := DefaultAlertThresholds()
|
||||
expected := []int{30, 14, 7, 0}
|
||||
if len(defaults) != len(expected) {
|
||||
t.Errorf("expected %d thresholds, got %d", len(expected), len(defaults))
|
||||
}
|
||||
for i, v := range expected {
|
||||
if i >= len(defaults) {
|
||||
break
|
||||
}
|
||||
if defaults[i] != v {
|
||||
t.Errorf("threshold[%d]: expected %d, got %d", i, v, defaults[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenewalPolicy_EffectiveAlertThresholds_Custom(t *testing.T) {
|
||||
policy := &RenewalPolicy{
|
||||
AlertThresholdsDays: []int{60, 30, 14, 7},
|
||||
}
|
||||
result := policy.EffectiveAlertThresholds()
|
||||
if len(result) != 4 {
|
||||
t.Errorf("expected 4 thresholds, got %d", len(result))
|
||||
}
|
||||
if result[0] != 60 {
|
||||
t.Errorf("expected first threshold 60, got %d", result[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenewalPolicy_EffectiveAlertThresholds_Default(t *testing.T) {
|
||||
policy := &RenewalPolicy{
|
||||
AlertThresholdsDays: []int{},
|
||||
}
|
||||
result := policy.EffectiveAlertThresholds()
|
||||
expected := DefaultAlertThresholds()
|
||||
if len(result) != len(expected) {
|
||||
t.Errorf("expected %d thresholds, got %d", len(expected), len(result))
|
||||
}
|
||||
for i, v := range expected {
|
||||
if i >= len(result) {
|
||||
break
|
||||
}
|
||||
if result[i] != v {
|
||||
t.Errorf("threshold[%d]: expected %d, got %d", i, v, result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenewalPolicy_EffectiveAlertThresholds_Nil(t *testing.T) {
|
||||
policy := &RenewalPolicy{
|
||||
AlertThresholdsDays: nil,
|
||||
}
|
||||
result := policy.EffectiveAlertThresholds()
|
||||
expected := DefaultAlertThresholds()
|
||||
if len(result) != len(expected) {
|
||||
t.Errorf("expected %d thresholds, got %d", len(expected), len(result))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestJobType_Constants(t *testing.T) {
|
||||
tests := map[string]JobType{
|
||||
"Issuance": JobTypeIssuance,
|
||||
"Renewal": JobTypeRenewal,
|
||||
"Deployment": JobTypeDeployment,
|
||||
"Validation": JobTypeValidation,
|
||||
}
|
||||
for expected, got := range tests {
|
||||
if string(got) != expected {
|
||||
t.Errorf("expected %q, got %q", expected, string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobStatus_Constants(t *testing.T) {
|
||||
tests := map[string]JobStatus{
|
||||
"Pending": JobStatusPending,
|
||||
"AwaitingCSR": JobStatusAwaitingCSR,
|
||||
"AwaitingApproval": JobStatusAwaitingApproval,
|
||||
"Running": JobStatusRunning,
|
||||
"Completed": JobStatusCompleted,
|
||||
"Failed": JobStatusFailed,
|
||||
"Cancelled": JobStatusCancelled,
|
||||
}
|
||||
for expected, got := range tests {
|
||||
if string(got) != expected {
|
||||
t.Errorf("expected %q, got %q", expected, string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNotificationType_Constants(t *testing.T) {
|
||||
tests := map[string]NotificationType{
|
||||
"ExpirationWarning": NotificationTypeExpirationWarning,
|
||||
"RenewalSuccess": NotificationTypeRenewalSuccess,
|
||||
"RenewalFailure": NotificationTypeRenewalFailure,
|
||||
"DeploymentSuccess": NotificationTypeDeploymentSuccess,
|
||||
"DeploymentFailure": NotificationTypeDeploymentFailure,
|
||||
"PolicyViolation": NotificationTypePolicyViolation,
|
||||
"Revocation": NotificationTypeRevocation,
|
||||
}
|
||||
for expected, got := range tests {
|
||||
if string(got) != expected {
|
||||
t.Errorf("expected %q, got %q", expected, string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationChannel_Constants(t *testing.T) {
|
||||
tests := map[string]NotificationChannel{
|
||||
"Email": NotificationChannelEmail,
|
||||
"Webhook": NotificationChannelWebhook,
|
||||
"Slack": NotificationChannelSlack,
|
||||
"Teams": NotificationChannelTeams,
|
||||
"PagerDuty": NotificationChannelPagerDuty,
|
||||
"OpsGenie": NotificationChannelOpsGenie,
|
||||
}
|
||||
for expected, got := range tests {
|
||||
if string(got) != expected {
|
||||
t.Errorf("expected %q, got %q", expected, string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotificationEvent_Fields(t *testing.T) {
|
||||
// This test verifies the NotificationEvent struct can be instantiated
|
||||
// with all expected fields.
|
||||
certID := "mc-123"
|
||||
errorMsg := "failed to send"
|
||||
event := &NotificationEvent{
|
||||
ID: "notif-1",
|
||||
Type: NotificationTypeExpirationWarning,
|
||||
CertificateID: &certID,
|
||||
Channel: NotificationChannelSlack,
|
||||
Recipient: "alerts@example.com",
|
||||
Message: "Certificate expiring in 30 days",
|
||||
Status: "sent",
|
||||
Error: &errorMsg,
|
||||
}
|
||||
|
||||
if event.ID != "notif-1" {
|
||||
t.Errorf("expected ID 'notif-1', got %s", event.ID)
|
||||
}
|
||||
|
||||
if event.Type != NotificationTypeExpirationWarning {
|
||||
t.Errorf("expected type ExpirationWarning, got %s", string(event.Type))
|
||||
}
|
||||
|
||||
if event.Channel != NotificationChannelSlack {
|
||||
t.Errorf("expected channel Slack, got %s", string(event.Channel))
|
||||
}
|
||||
|
||||
if event.CertificateID == nil || *event.CertificateID != "mc-123" {
|
||||
t.Errorf("expected CertificateID mc-123, got %v", event.CertificateID)
|
||||
}
|
||||
|
||||
if event.Error == nil || *event.Error != "failed to send" {
|
||||
t.Errorf("expected error 'failed to send', got %v", event.Error)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPolicyType_Constants(t *testing.T) {
|
||||
tests := map[string]PolicyType{
|
||||
"AllowedIssuers": PolicyTypeAllowedIssuers,
|
||||
"AllowedDomains": PolicyTypeAllowedDomains,
|
||||
"RequiredMetadata": PolicyTypeRequiredMetadata,
|
||||
"AllowedEnvironments": PolicyTypeAllowedEnvironments,
|
||||
"RenewalLeadTime": PolicyTypeRenewalLeadTime,
|
||||
}
|
||||
for expected, got := range tests {
|
||||
if string(got) != expected {
|
||||
t.Errorf("expected %q, got %q", expected, string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicySeverity_Constants(t *testing.T) {
|
||||
tests := map[string]PolicySeverity{
|
||||
"Warning": PolicySeverityWarning,
|
||||
"Error": PolicySeverityError,
|
||||
"Critical": PolicySeverityCritical,
|
||||
}
|
||||
for expected, got := range tests {
|
||||
if string(got) != expected {
|
||||
t.Errorf("expected %q, got %q", expected, string(got))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicyRule_Fields(t *testing.T) {
|
||||
// This test verifies the PolicyRule struct can be instantiated
|
||||
// with all expected fields.
|
||||
rule := &PolicyRule{
|
||||
ID: "rule-1",
|
||||
Name: "Allowed Issuers",
|
||||
Type: PolicyTypeAllowedIssuers,
|
||||
Enabled: true,
|
||||
}
|
||||
|
||||
if rule.ID != "rule-1" {
|
||||
t.Errorf("expected ID 'rule-1', got %s", rule.ID)
|
||||
}
|
||||
|
||||
if rule.Name != "Allowed Issuers" {
|
||||
t.Errorf("expected Name 'Allowed Issuers', got %s", rule.Name)
|
||||
}
|
||||
|
||||
if rule.Type != PolicyTypeAllowedIssuers {
|
||||
t.Errorf("expected Type AllowedIssuers, got %s", string(rule.Type))
|
||||
}
|
||||
|
||||
if !rule.Enabled {
|
||||
t.Errorf("expected Enabled=true, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicyViolation_Fields(t *testing.T) {
|
||||
// This test verifies the PolicyViolation struct can be instantiated
|
||||
// with all expected fields.
|
||||
violation := &PolicyViolation{
|
||||
ID: "violation-1",
|
||||
CertificateID: "mc-123",
|
||||
RuleID: "rule-1",
|
||||
Message: "Certificate issued by unauthorized CA",
|
||||
Severity: PolicySeverityCritical,
|
||||
}
|
||||
|
||||
if violation.ID != "violation-1" {
|
||||
t.Errorf("expected ID 'violation-1', got %s", violation.ID)
|
||||
}
|
||||
|
||||
if violation.CertificateID != "mc-123" {
|
||||
t.Errorf("expected CertificateID 'mc-123', got %s", violation.CertificateID)
|
||||
}
|
||||
|
||||
if violation.RuleID != "rule-1" {
|
||||
t.Errorf("expected RuleID 'rule-1', got %s", violation.RuleID)
|
||||
}
|
||||
|
||||
if violation.Severity != PolicySeverityCritical {
|
||||
t.Errorf("expected Severity Critical, got %s", string(violation.Severity))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolicySeverity_Ordering(t *testing.T) {
|
||||
// This test verifies severity ordering is correct (for potential future use
|
||||
// in ranking violations by impact).
|
||||
severities := []PolicySeverity{
|
||||
PolicySeverityWarning,
|
||||
PolicySeverityError,
|
||||
PolicySeverityCritical,
|
||||
}
|
||||
|
||||
for i, severity := range severities {
|
||||
if string(severity) == "" {
|
||||
t.Errorf("severity %d has empty string value", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user