|
1 | 1 | package pathologicaleventlibrary |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "fmt" |
| 5 | + "regexp" |
4 | 6 | "testing" |
| 7 | + "time" |
5 | 8 |
|
| 9 | + v1 "github.com/openshift/api/config/v1" |
6 | 10 | "github.com/openshift/origin/pkg/monitor/monitorapi" |
7 | 11 | "github.com/stretchr/testify/assert" |
8 | 12 | ) |
9 | 13 |
|
| 14 | +func Test_OverlapMatcherUsesFirstTimestamp(t *testing.T) { |
| 15 | + // Simulate the real-world scenario: an event fires 23 times over 90 minutes. |
| 16 | + // The interval's From is set to LastTimestamp (the final occurrence), but |
| 17 | + // firstTimestamp in annotations records when the event actually started. |
| 18 | + testStart := time.Date(2026, 4, 10, 12, 0, 0, 0, time.UTC) |
| 19 | + firstTimestamp := testStart.Add(15 * time.Minute) // 12:15 |
| 20 | + lastTimestamp := testStart.Add(105 * time.Minute) // 13:45 |
| 21 | + testEnd := testStart.Add(110 * time.Minute) // 13:50 |
| 22 | + |
| 23 | + // The "test interval" that the event should overlap with (12:00 - 13:50). |
| 24 | + testInterval := monitorapi.NewInterval(monitorapi.SourceE2ETest, monitorapi.Info). |
| 25 | + Locator(monitorapi.NewLocator().NodeFromName("test")). |
| 26 | + Message(monitorapi.NewMessage().HumanMessage("test interval")). |
| 27 | + Build(testStart, testEnd) |
| 28 | + |
| 29 | + // Build a pathological event interval the way watchevents/event.go does: |
| 30 | + // From = lastTimestamp, To = lastTimestamp + 1s, firstTimestamp in annotations. |
| 31 | + pathologicalInterval := monitorapi.NewInterval(monitorapi.SourceKubeEvent, monitorapi.Warning). |
| 32 | + Locator(monitorapi.NewLocator().PodFromNames("openshift-test", "test-pod", "")). |
| 33 | + Message( |
| 34 | + monitorapi.NewMessage(). |
| 35 | + Reason("TestReason"). |
| 36 | + HumanMessage("test pathological event"). |
| 37 | + WithAnnotation(monitorapi.AnnotationCount, fmt.Sprintf("%d", 23)). |
| 38 | + WithAnnotation("firstTimestamp", firstTimestamp.Format(time.RFC3339)). |
| 39 | + WithAnnotation("lastTimestamp", lastTimestamp.Format(time.RFC3339))). |
| 40 | + Build(lastTimestamp, lastTimestamp.Add(1*time.Second)) |
| 41 | + |
| 42 | + matcher := &OverlapOtherIntervalsPathologicalEventMatcher{ |
| 43 | + delegate: &SimplePathologicalEventMatcher{ |
| 44 | + name: "TestMatcher", |
| 45 | + messageReasonRegex: regexp.MustCompile(`^TestReason$`), |
| 46 | + }, |
| 47 | + allowIfWithinIntervals: monitorapi.Intervals{testInterval}, |
| 48 | + } |
| 49 | + |
| 50 | + // Should be allowed because firstTimestamp (12:15) falls within the test interval (12:00-13:50). |
| 51 | + assert.True(t, matcher.Allows(pathologicalInterval, v1.HighlyAvailableTopologyMode), |
| 52 | + "event should be allowed when firstTimestamp falls within the overlap interval") |
| 53 | + |
| 54 | + // Now test with a test interval that ends before lastTimestamp but after firstTimestamp. |
| 55 | + // The event's firstTimestamp (12:15) is within [12:00, 13:00], and To (13:45:01) is NOT. |
| 56 | + shorterTestInterval := monitorapi.NewInterval(monitorapi.SourceE2ETest, monitorapi.Info). |
| 57 | + Locator(monitorapi.NewLocator().NodeFromName("test")). |
| 58 | + Message(monitorapi.NewMessage().HumanMessage("short test interval")). |
| 59 | + Build(testStart, testStart.Add(60*time.Minute)) // 12:00 - 13:00 |
| 60 | + |
| 61 | + matcherShort := &OverlapOtherIntervalsPathologicalEventMatcher{ |
| 62 | + delegate: &SimplePathologicalEventMatcher{ |
| 63 | + name: "TestMatcher", |
| 64 | + messageReasonRegex: regexp.MustCompile(`^TestReason$`), |
| 65 | + }, |
| 66 | + allowIfWithinIntervals: monitorapi.Intervals{shorterTestInterval}, |
| 67 | + } |
| 68 | + |
| 69 | + // Should NOT be allowed because the event's To (13:45:01) extends past the test interval's end (13:00). |
| 70 | + assert.False(t, matcherShort.Allows(pathologicalInterval, v1.HighlyAvailableTopologyMode), |
| 71 | + "event should not be allowed when its To extends beyond the overlap interval") |
| 72 | + |
| 73 | + // Test without firstTimestamp annotation — should fall back to From (lastTimestamp). |
| 74 | + noAnnotationInterval := monitorapi.NewInterval(monitorapi.SourceKubeEvent, monitorapi.Warning). |
| 75 | + Locator(monitorapi.NewLocator().PodFromNames("openshift-test", "test-pod", "")). |
| 76 | + Message( |
| 77 | + monitorapi.NewMessage(). |
| 78 | + Reason("TestReason"). |
| 79 | + HumanMessage("test pathological event"). |
| 80 | + WithAnnotation(monitorapi.AnnotationCount, fmt.Sprintf("%d", 23))). |
| 81 | + Build(lastTimestamp, lastTimestamp.Add(1*time.Second)) |
| 82 | + |
| 83 | + // Without firstTimestamp, From is lastTimestamp (13:45). The test interval [12:00, 13:50] contains it. |
| 84 | + assert.True(t, matcher.Allows(noAnnotationInterval, v1.HighlyAvailableTopologyMode), |
| 85 | + "event without firstTimestamp should fall back to From for overlap check") |
| 86 | +} |
| 87 | + |
10 | 88 | func Test_singleEventThresholdCheck_getNamespacedFailuresAndFlakes(t *testing.T) { |
11 | 89 | namespace := "openshift-etcd-operator" |
12 | 90 | samplePod := "etcd-operator-6f9b4d9d4f-4q9q8" |
|
0 commit comments