Skip to content

Commit 7d1b121

Browse files
committed
test: Improve pkg/types test coverage from 78.1% to 80.3%
Add comprehensive tests for core types package: - Add ExporterReloadSummary tests (exporter_test.go): - TestExporterReloadSummary_AddResult with success/failure scenarios - TestExporterReloadResult_Fields for field validation - TestExporterReloadSummary_InitialState and order preservation tests - Add edge case validation tests (types_test.go): - TestProblemWithMetadata_NilMetadataMap for nil metadata handling - TestConditionValidation_EdgeCases for missing reason/message/transition - TestStatusValidation_EdgeCases for missing timestamp/invalid condition - TestProblemValidation_EdgeCases for missing severity/message/detected time - TestGetMetadata_NilMetadataMap for nil metadata retrieval Coverage: 78.1% -> 80.3% (exceeds 80% target) All tests pass with race detection.
1 parent 5a746bb commit 7d1b121

File tree

2 files changed

+358
-0
lines changed

2 files changed

+358
-0
lines changed

pkg/types/exporter_test.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package types
2+
3+
import (
4+
"errors"
5+
"testing"
6+
)
7+
8+
// TestExporterReloadSummary_AddResult verifies AddResult method functionality.
9+
func TestExporterReloadSummary_AddResult(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
results []ExporterReloadResult
13+
expectedTotal int
14+
expectedSuccessful int
15+
expectedFailed int
16+
expectedReloadableCount int
17+
}{
18+
{
19+
name: "empty summary",
20+
results: []ExporterReloadResult{},
21+
expectedTotal: 0,
22+
expectedSuccessful: 0,
23+
expectedFailed: 0,
24+
expectedReloadableCount: 0,
25+
},
26+
{
27+
name: "single successful reload",
28+
results: []ExporterReloadResult{
29+
{ExporterType: "prometheus", Success: true, Message: "reloaded successfully"},
30+
},
31+
expectedTotal: 1,
32+
expectedSuccessful: 1,
33+
expectedFailed: 0,
34+
expectedReloadableCount: 0,
35+
},
36+
{
37+
name: "single failed reload",
38+
results: []ExporterReloadResult{
39+
{ExporterType: "kubernetes", Success: false, Error: errors.New("config invalid"), Message: "reload failed"},
40+
},
41+
expectedTotal: 1,
42+
expectedSuccessful: 0,
43+
expectedFailed: 1,
44+
expectedReloadableCount: 0,
45+
},
46+
{
47+
name: "mixed results",
48+
results: []ExporterReloadResult{
49+
{ExporterType: "prometheus", Success: true, Message: "reloaded successfully"},
50+
{ExporterType: "kubernetes", Success: false, Error: errors.New("config invalid")},
51+
{ExporterType: "http", Success: true, Message: "reloaded successfully"},
52+
{ExporterType: "logs", Success: false, Error: errors.New("not reloadable")},
53+
},
54+
expectedTotal: 4,
55+
expectedSuccessful: 2,
56+
expectedFailed: 2,
57+
expectedReloadableCount: 0,
58+
},
59+
}
60+
61+
for _, tt := range tests {
62+
t.Run(tt.name, func(t *testing.T) {
63+
summary := &ExporterReloadSummary{
64+
ReloadableCount: tt.expectedReloadableCount,
65+
}
66+
67+
for _, result := range tt.results {
68+
summary.AddResult(result)
69+
}
70+
71+
if summary.TotalExporters != tt.expectedTotal {
72+
t.Errorf("TotalExporters = %d, want %d", summary.TotalExporters, tt.expectedTotal)
73+
}
74+
if summary.SuccessfulReloads != tt.expectedSuccessful {
75+
t.Errorf("SuccessfulReloads = %d, want %d", summary.SuccessfulReloads, tt.expectedSuccessful)
76+
}
77+
if summary.FailedReloads != tt.expectedFailed {
78+
t.Errorf("FailedReloads = %d, want %d", summary.FailedReloads, tt.expectedFailed)
79+
}
80+
if len(summary.Results) != len(tt.results) {
81+
t.Errorf("Results count = %d, want %d", len(summary.Results), len(tt.results))
82+
}
83+
})
84+
}
85+
}
86+
87+
// TestExporterReloadResult_Fields verifies ExporterReloadResult field assignments.
88+
func TestExporterReloadResult_Fields(t *testing.T) {
89+
err := errors.New("test error")
90+
result := ExporterReloadResult{
91+
ExporterType: "prometheus",
92+
Success: false,
93+
Error: err,
94+
Message: "config validation failed",
95+
}
96+
97+
if result.ExporterType != "prometheus" {
98+
t.Errorf("ExporterType = %s, want prometheus", result.ExporterType)
99+
}
100+
if result.Success {
101+
t.Error("Success should be false")
102+
}
103+
if result.Error != err {
104+
t.Errorf("Error = %v, want %v", result.Error, err)
105+
}
106+
if result.Message != "config validation failed" {
107+
t.Errorf("Message = %s, want 'config validation failed'", result.Message)
108+
}
109+
}
110+
111+
// TestExporterReloadSummary_InitialState verifies initial state of ExporterReloadSummary.
112+
func TestExporterReloadSummary_InitialState(t *testing.T) {
113+
summary := ExporterReloadSummary{}
114+
115+
if summary.TotalExporters != 0 {
116+
t.Errorf("TotalExporters = %d, want 0", summary.TotalExporters)
117+
}
118+
if summary.ReloadableCount != 0 {
119+
t.Errorf("ReloadableCount = %d, want 0", summary.ReloadableCount)
120+
}
121+
if summary.SuccessfulReloads != 0 {
122+
t.Errorf("SuccessfulReloads = %d, want 0", summary.SuccessfulReloads)
123+
}
124+
if summary.FailedReloads != 0 {
125+
t.Errorf("FailedReloads = %d, want 0", summary.FailedReloads)
126+
}
127+
if summary.Results != nil && len(summary.Results) != 0 {
128+
t.Errorf("Results should be nil or empty, got %d items", len(summary.Results))
129+
}
130+
}
131+
132+
// TestExporterReloadSummary_ResultsPreserveOrder verifies results maintain insertion order.
133+
func TestExporterReloadSummary_ResultsPreserveOrder(t *testing.T) {
134+
summary := &ExporterReloadSummary{}
135+
136+
results := []ExporterReloadResult{
137+
{ExporterType: "first"},
138+
{ExporterType: "second"},
139+
{ExporterType: "third"},
140+
}
141+
142+
for _, r := range results {
143+
summary.AddResult(r)
144+
}
145+
146+
for i, r := range results {
147+
if summary.Results[i].ExporterType != r.ExporterType {
148+
t.Errorf("Results[%d].ExporterType = %s, want %s", i, summary.Results[i].ExporterType, r.ExporterType)
149+
}
150+
}
151+
}

pkg/types/types_test.go

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,210 @@ func TestNilPointerHandling(t *testing.T) {
618618
t.Error("expected GetMetadata on nil pointer to return empty string and false")
619619
}
620620
}
621+
622+
// TestProblemWithMetadata_NilMetadataMap verifies WithMetadata handles nil Metadata map.
623+
func TestProblemWithMetadata_NilMetadataMap(t *testing.T) {
624+
// Create problem manually without initializing Metadata
625+
problem := &Problem{
626+
Type: "test-type",
627+
Resource: "test-resource",
628+
Severity: ProblemWarning,
629+
Message: "Test message",
630+
DetectedAt: time.Now(),
631+
Metadata: nil, // Explicitly nil
632+
}
633+
634+
// WithMetadata should initialize the map
635+
problem.WithMetadata("key1", "value1")
636+
637+
if problem.Metadata == nil {
638+
t.Error("expected Metadata to be initialized")
639+
}
640+
if val, ok := problem.Metadata["key1"]; !ok || val != "value1" {
641+
t.Errorf("expected Metadata[key1]=value1, got %v", problem.Metadata)
642+
}
643+
}
644+
645+
// TestConditionValidation_EdgeCases verifies Condition validation edge cases.
646+
func TestConditionValidation_EdgeCases(t *testing.T) {
647+
tests := []struct {
648+
name string
649+
condition Condition
650+
wantErr bool
651+
errSubstr string
652+
}{
653+
{
654+
name: "missing reason",
655+
condition: Condition{
656+
Type: "TestType",
657+
Status: ConditionTrue,
658+
Transition: time.Now(),
659+
Message: "Test message",
660+
},
661+
wantErr: true,
662+
errSubstr: "reason",
663+
},
664+
{
665+
name: "missing message",
666+
condition: Condition{
667+
Type: "TestType",
668+
Status: ConditionTrue,
669+
Transition: time.Now(),
670+
Reason: "TestReason",
671+
},
672+
wantErr: true,
673+
errSubstr: "message",
674+
},
675+
{
676+
name: "missing transition time",
677+
condition: Condition{
678+
Type: "TestType",
679+
Status: ConditionTrue,
680+
Reason: "TestReason",
681+
Message: "Test message",
682+
},
683+
wantErr: true,
684+
errSubstr: "transition",
685+
},
686+
}
687+
688+
for _, tt := range tests {
689+
t.Run(tt.name, func(t *testing.T) {
690+
err := tt.condition.Validate()
691+
if (err != nil) != tt.wantErr {
692+
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
693+
}
694+
if err != nil && tt.errSubstr != "" {
695+
if !stringContains(err.Error(), tt.errSubstr) {
696+
t.Errorf("Error message %q should contain %q", err.Error(), tt.errSubstr)
697+
}
698+
}
699+
})
700+
}
701+
}
702+
703+
// TestStatusValidation_EdgeCases verifies Status validation edge cases.
704+
func TestStatusValidation_EdgeCases(t *testing.T) {
705+
tests := []struct {
706+
name string
707+
status *Status
708+
wantErr bool
709+
errSubstr string
710+
}{
711+
{
712+
name: "missing timestamp",
713+
status: &Status{
714+
Source: "test-monitor",
715+
Events: []Event{},
716+
Conditions: []Condition{},
717+
},
718+
wantErr: true,
719+
errSubstr: "timestamp",
720+
},
721+
{
722+
name: "invalid condition in status",
723+
status: &Status{
724+
Source: "test-monitor",
725+
Timestamp: time.Now(),
726+
Events: []Event{},
727+
Conditions: []Condition{
728+
{Type: "TestType"}, // missing required fields
729+
},
730+
},
731+
wantErr: true,
732+
errSubstr: "condition",
733+
},
734+
}
735+
736+
for _, tt := range tests {
737+
t.Run(tt.name, func(t *testing.T) {
738+
err := tt.status.Validate()
739+
if (err != nil) != tt.wantErr {
740+
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
741+
}
742+
if err != nil && tt.errSubstr != "" {
743+
if !stringContains(err.Error(), tt.errSubstr) {
744+
t.Errorf("Error message %q should contain %q", err.Error(), tt.errSubstr)
745+
}
746+
}
747+
})
748+
}
749+
}
750+
751+
// TestProblemValidation_EdgeCases verifies Problem validation edge cases.
752+
func TestProblemValidation_EdgeCases(t *testing.T) {
753+
tests := []struct {
754+
name string
755+
problem *Problem
756+
wantErr bool
757+
errSubstr string
758+
}{
759+
{
760+
name: "missing severity",
761+
problem: &Problem{
762+
Type: "test-type",
763+
Resource: "test-resource",
764+
Message: "Test message",
765+
DetectedAt: time.Now(),
766+
},
767+
wantErr: true,
768+
errSubstr: "severity",
769+
},
770+
{
771+
name: "missing message",
772+
problem: &Problem{
773+
Type: "test-type",
774+
Resource: "test-resource",
775+
Severity: ProblemWarning,
776+
DetectedAt: time.Now(),
777+
},
778+
wantErr: true,
779+
errSubstr: "message",
780+
},
781+
{
782+
name: "missing detected time",
783+
problem: &Problem{
784+
Type: "test-type",
785+
Resource: "test-resource",
786+
Severity: ProblemWarning,
787+
Message: "Test message",
788+
},
789+
wantErr: true,
790+
errSubstr: "detected",
791+
},
792+
}
793+
794+
for _, tt := range tests {
795+
t.Run(tt.name, func(t *testing.T) {
796+
err := tt.problem.Validate()
797+
if (err != nil) != tt.wantErr {
798+
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
799+
}
800+
if err != nil && tt.errSubstr != "" {
801+
if !stringContains(err.Error(), tt.errSubstr) {
802+
t.Errorf("Error message %q should contain %q", err.Error(), tt.errSubstr)
803+
}
804+
}
805+
})
806+
}
807+
}
808+
809+
// TestGetMetadata_NilMetadataMap verifies GetMetadata handles nil Metadata map.
810+
func TestGetMetadata_NilMetadataMap(t *testing.T) {
811+
problem := &Problem{
812+
Type: "test-type",
813+
Resource: "test-resource",
814+
Severity: ProblemWarning,
815+
Message: "Test message",
816+
DetectedAt: time.Now(),
817+
Metadata: nil, // Explicitly nil
818+
}
819+
820+
val, ok := problem.GetMetadata("nonexistent")
821+
if ok {
822+
t.Error("expected ok=false for nil Metadata map")
823+
}
824+
if val != "" {
825+
t.Errorf("expected empty string, got %q", val)
826+
}
827+
}

0 commit comments

Comments
 (0)