// Copyright 2024 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package conditionsutil import ( "strings" "testing" "time" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "go.pinniped.dev/internal/plog" ) func TestMergeIDPConditions(t *testing.T) { twoHoursAgo := metav1.Time{Time: time.Now().Add(-2 * time.Hour)} oneHourAgo := metav1.Time{Time: time.Now().Add(-1 * time.Hour)} testTime := metav1.Now() tests := []struct { name string newConditions []*metav1.Condition existingConditionsToUpdate *[]metav1.Condition observedGeneration int64 wantResult bool wantLogSnippets []string wantConditions []metav1.Condition }{ { name: "Adding a new condition with status=True returns false", newConditions: []*metav1.Condition{ { Type: "NewType", Status: metav1.ConditionTrue, Reason: "new reason", Message: "new message", }, }, observedGeneration: int64(999), existingConditionsToUpdate: &[]metav1.Condition{}, wantLogSnippets: []string{ `"message":"updated condition","type":"NewType","status":"True"`, }, wantConditions: []metav1.Condition{ { Type: "NewType", Status: metav1.ConditionTrue, ObservedGeneration: int64(999), LastTransitionTime: testTime, Reason: "new reason", Message: "new message", }, }, wantResult: false, }, { name: "Updating a condition status from False to True returns true", newConditions: []*metav1.Condition{ { Type: "UnchangedType", Status: metav1.ConditionTrue, Reason: "unchanged reason", Message: "unchanged message", }, { Type: "FalseToTrueType", Status: metav1.ConditionFalse, Reason: "new reason", Message: "new message", }, { Type: "NewType", Status: metav1.ConditionTrue, Reason: "new reason", Message: "new message", }, }, existingConditionsToUpdate: &[]metav1.Condition{ { Type: "UnchangedType", Status: metav1.ConditionTrue, ObservedGeneration: int64(10), LastTransitionTime: twoHoursAgo, Reason: "unchanged reason", Message: "unchanged message", }, { Type: "FalseToTrueType", Status: metav1.ConditionTrue, ObservedGeneration: int64(5), LastTransitionTime: oneHourAgo, Reason: "old reason", Message: "old message", }, }, observedGeneration: int64(100), wantLogSnippets: []string{ `"message":"updated condition","type":"UnchangedType","status":"True"`, `"message":"updated condition","type":"NewType","status":"True"`, `"message":"updated condition","type":"FalseToTrueType","status":"False"`, }, wantConditions: []metav1.Condition{ { Type: "FalseToTrueType", Status: metav1.ConditionFalse, ObservedGeneration: int64(100), LastTransitionTime: testTime, Reason: "new reason", Message: "new message", }, { Type: "NewType", Status: metav1.ConditionTrue, ObservedGeneration: int64(100), LastTransitionTime: testTime, Reason: "new reason", Message: "new message", }, { Type: "UnchangedType", Status: metav1.ConditionTrue, ObservedGeneration: int64(100), LastTransitionTime: twoHoursAgo, Reason: "unchanged reason", Message: "unchanged message", }, }, wantResult: true, }, { name: "No logs when ObservedGeneration is unchanged", newConditions: []*metav1.Condition{ { Type: "UnchangedType", Status: metav1.ConditionFalse, Reason: "unchanged reason", Message: "unchanged message", }, }, existingConditionsToUpdate: &[]metav1.Condition{ { Type: "UnchangedType", Status: metav1.ConditionFalse, ObservedGeneration: int64(10), LastTransitionTime: twoHoursAgo, Reason: "unchanged reason", Message: "unchanged message", }, }, observedGeneration: int64(10), wantConditions: []metav1.Condition{ { Type: "UnchangedType", Status: metav1.ConditionFalse, ObservedGeneration: int64(10), LastTransitionTime: twoHoursAgo, Reason: "unchanged reason", Message: "unchanged message", }, }, wantResult: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { logger, log := plog.TestLogger(t) result := MergeConditions( test.newConditions, test.existingConditionsToUpdate, test.observedGeneration, testTime, logger, ) logString := log.String() require.Equal(t, len(test.wantLogSnippets), strings.Count(logString, "\n")) for _, wantLog := range test.wantLogSnippets { require.Contains(t, logString, wantLog) } require.Equal(t, test.wantResult, result) require.Equal(t, test.wantConditions, *test.existingConditionsToUpdate) }) } } func TestHadErrorCondition(t *testing.T) { tests := []struct { name string conditions []*metav1.Condition wantResult bool }{ { name: "Returns false when all conditions have status true", conditions: []*metav1.Condition{ { Status: metav1.ConditionTrue, }, { Status: metav1.ConditionTrue, }, }, wantResult: false, }, { name: "Returns false when input is nil", conditions: nil, wantResult: false, }, { name: "Returns false when input is empty", conditions: []*metav1.Condition{}, wantResult: false, }, { name: "Returns true when any condition has status unknown", conditions: []*metav1.Condition{ { Status: metav1.ConditionTrue, }, { Status: metav1.ConditionUnknown, }, }, wantResult: true, }, { name: "Returns true when any condition has status false", conditions: []*metav1.Condition{ { Status: metav1.ConditionTrue, }, { Status: metav1.ConditionFalse, }, }, wantResult: true, }, { name: "Returns true when any condition has invalid status", conditions: []*metav1.Condition{ { Status: metav1.ConditionTrue, }, { Status: "not a valid status", }, }, wantResult: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { actual := HadErrorCondition(test.conditions) require.Equal(t, test.wantResult, actual) }) } }