Skip to content

Commit 3601042

Browse files
s35560claude
andcommitted
test(api): fix flaky evaluation tests caused by CreatedAt/RequestedAt
Server-stamped timestamps were compared against a time captured in the test setup, so the assertions failed whenever the second boundary was crossed. Assert those fields against the current time with a tolerance, then copy the actual value into expected for the full-struct comparison. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent d32706e commit 3601042

2 files changed

Lines changed: 59 additions & 19 deletions

File tree

pkg/api/api/api_grpc_test.go

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,6 +1774,8 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
17741774
expected *gwproto.GetFeatureFlagsResponse
17751775
expectedErr error
17761776
clampTest bool // when true, verify RequestedAt is clamped (not exact match with timeNow)
1777+
// when true, RequestedAt is stamped by the server and checked against the current time
1778+
expectRequestedAtNow bool
17771779
}{
17781780
{
17791781
desc: "err: environment api key not found",
@@ -1900,10 +1902,10 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
19001902
FeatureFlagsId: "",
19011903
Features: []*featureproto.Feature{},
19021904
ArchivedFeatureFlagIds: make([]string, 0),
1903-
RequestedAt: timeNow.Unix(),
19041905
ForceUpdate: false,
19051906
},
1906-
expectedErr: nil,
1907+
expectedErr: nil,
1908+
expectRequestedAtNow: true,
19071909
},
19081910
{
19091911
desc: "success: with no tag and no feature flags ID",
@@ -1936,10 +1938,10 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
19361938
multiFeatures[2],
19371939
},
19381940
ArchivedFeatureFlagIds: make([]string, 0),
1939-
RequestedAt: timeNow.Unix(),
19401941
ForceUpdate: true,
19411942
},
1942-
expectedErr: nil,
1943+
expectedErr: nil,
1944+
expectRequestedAtNow: true,
19431945
},
19441946
{
19451947
desc: "success: with no tag and with same feature flags ID",
@@ -2006,10 +2008,10 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
20062008
multiFeatures[2],
20072009
},
20082010
ArchivedFeatureFlagIds: []string{multiFeatures[4].Id},
2009-
RequestedAt: timeNow.Unix(),
20102011
ForceUpdate: false,
20112012
},
2012-
expectedErr: nil,
2013+
expectedErr: nil,
2014+
expectRequestedAtNow: true,
20132015
},
20142016
{
20152017
desc: "success: with tag and no feature flags ID",
@@ -2037,11 +2039,11 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
20372039
expected: &gwproto.GetFeatureFlagsResponse{
20382040
FeatureFlagsId: singleFeatureID,
20392041
Features: singleFeature,
2040-
RequestedAt: timeNow.Unix(),
20412042
ArchivedFeatureFlagIds: make([]string, 0),
20422043
ForceUpdate: true,
20432044
},
2044-
expectedErr: nil,
2045+
expectedErr: nil,
2046+
expectRequestedAtNow: true,
20452047
},
20462048
{
20472049
desc: "success: with tag and same feature flags ID",
@@ -2103,10 +2105,10 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
21032105
FeatureFlagsId: singleFeatureID,
21042106
Features: singleFeature,
21052107
ArchivedFeatureFlagIds: make([]string, 0),
2106-
RequestedAt: timeNow.Unix(),
21072108
ForceUpdate: true,
21082109
},
2109-
expectedErr: nil,
2110+
expectedErr: nil,
2111+
expectRequestedAtNow: true,
21102112
},
21112113
{
21122114
// With the 10-minute featureFlagDiffGracePeriod default, the
@@ -2141,10 +2143,10 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
21412143
FeatureFlagsId: singleFeatureID,
21422144
Features: []*featureproto.Feature{multiFeatures[0]},
21432145
ArchivedFeatureFlagIds: []string{multiFeatures[4].Id},
2144-
RequestedAt: timeNow.Unix(),
21452146
ForceUpdate: false,
21462147
},
2147-
expectedErr: nil,
2148+
expectedErr: nil,
2149+
expectRequestedAtNow: true,
21482150
},
21492151
{
21502152
desc: "success: diff includes features with UpdatedAt equal to RequestedAt",
@@ -2174,10 +2176,10 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
21742176
FeatureFlagsId: singleFeatureID,
21752177
Features: singleFeature,
21762178
ArchivedFeatureFlagIds: make([]string, 0),
2177-
RequestedAt: timeNow.Unix(),
21782179
ForceUpdate: false,
21792180
},
2180-
expectedErr: nil,
2181+
expectedErr: nil,
2182+
expectRequestedAtNow: true,
21812183
},
21822184
{
21832185
desc: "success: future requestedAt is clamped to now in None response",
@@ -2240,10 +2242,10 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
22402242
FeatureFlagsId: singleFeatureID,
22412243
Features: singleFeature,
22422244
ArchivedFeatureFlagIds: make([]string, 0),
2243-
RequestedAt: timeNow.Unix(),
22442245
ForceUpdate: true,
22452246
},
2246-
expectedErr: nil,
2247+
expectedErr: nil,
2248+
expectRequestedAtNow: true,
22472249
},
22482250
}
22492251
for _, p := range patterns {
@@ -2265,6 +2267,11 @@ func TestGrpcGetFeatureFlags(t *testing.T) {
22652267
assert.Less(t, actual.RequestedAt, p.input.RequestedAt,
22662268
"%s: RequestedAt should be clamped below the future input value", p.desc)
22672269
} else {
2270+
if p.expectRequestedAtNow {
2271+
require.NotNil(t, actual, "%s", p.desc)
2272+
assert.InDelta(t, time.Now().Unix(), actual.RequestedAt, 5, "%s", p.desc)
2273+
p.expected.RequestedAt = actual.RequestedAt
2274+
}
22682275
assert.Equal(t, p.expected, actual, "%s", p.desc)
22692276
}
22702277
})
@@ -2359,6 +2366,7 @@ func TestGrpcGetEvaluationsValidation(t *testing.T) {
23592366
"authorization": []string{"test-key"},
23602367
})
23612368
actual, err := gs.GetEvaluations(ctx, p.input)
2369+
assertEvaluationsCreatedAtNow(t, p.expected, actual, p.desc)
23622370
assert.Equal(t, p.expected, actual, "%s", p.desc)
23632371
assert.Equal(t, p.expectedErr, err, "%s", p.desc)
23642372
})
@@ -2410,6 +2418,7 @@ func TestGrpcGetEvaluationsZeroFeature(t *testing.T) {
24102418
"authorization": []string{"test-key"},
24112419
})
24122420
actual, err := gs.GetEvaluations(ctx, p.input)
2421+
assertEvaluationsCreatedAtNow(t, p.expected, actual, p.desc)
24132422
assert.Equal(t, p.expected, actual, "%s", p.desc)
24142423
assert.Equal(t, p.expected.State, actual.State, "%s", p.desc)
24152424
assert.Equal(t, p.expectedErr, err, "%s", p.desc)
@@ -4948,12 +4957,27 @@ func newUUID(t *testing.T) string {
49484957
return id.String()
49494958
}
49504959

4960+
// assertEvaluationsCreatedAtNow checks the server-stamped CreatedAt against the
4961+
// current time, then copies it into expected so the whole response can be compared.
4962+
func assertEvaluationsCreatedAtNow(
4963+
t *testing.T,
4964+
expected, actual *gwproto.GetEvaluationsResponse,
4965+
desc string,
4966+
) {
4967+
t.Helper()
4968+
if expected == nil || expected.Evaluations == nil || actual == nil || actual.Evaluations == nil {
4969+
return
4970+
}
4971+
assert.InDelta(t, time.Now().Unix(), actual.Evaluations.CreatedAt, 5, "%s", desc)
4972+
expected.Evaluations.CreatedAt = actual.Evaluations.CreatedAt
4973+
}
4974+
4975+
// CreatedAt is left unset because the server stamps it at request time.
49514976
func emptyUserEvaluations(t *testing.T) *featureproto.UserEvaluations {
49524977
t.Helper()
49534978
return &featureproto.UserEvaluations{
49544979
Id: "no_evaluations",
49554980
Evaluations: []*featureproto.Evaluation{},
4956-
CreatedAt: time.Now().Unix(),
49574981
ArchivedFeatureIds: []string{},
49584982
ForceUpdate: false,
49594983
}

pkg/api/api/api_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ func TestGetEvaluationsValidation(t *testing.T) {
716716
decoded := decodeSuccessResponse(t, actual.Body)
717717
err := json.Unmarshal(decoded, &respBody)
718718
assert.NoError(t, err)
719-
// FIXME: This is a flaky test. CreateAt may not be equal ocasionally.
719+
assertEvaluationsCreatedAtNowForREST(t, p.expected, &respBody, p.desc)
720720
assert.Equal(t, p.expected, &respBody, "%s", p.desc)
721721
}
722722
}
@@ -779,6 +779,7 @@ func TestGetEvaluationsZeroFeature(t *testing.T) {
779779
decoded := decodeSuccessResponse(t, actual.Body)
780780
err := json.Unmarshal(decoded, &respBody)
781781
assert.NoError(t, err)
782+
assertEvaluationsCreatedAtNowForREST(t, p.expected, &respBody, p.desc)
782783
assert.Equal(t, p.expected, &respBody, "%s", p.desc)
783784
}
784785
}
@@ -2971,13 +2972,28 @@ func TestEvaluateFeaturesForStream(t *testing.T) {
29712972
}
29722973
}
29732974

2975+
// CreatedAt is left unset because the server stamps it at request time.
29742976
func emptyUserEvaluationsForREST(t *testing.T) *featureproto.UserEvaluations {
29752977
t.Helper()
29762978
return &featureproto.UserEvaluations{
29772979
Id: "no_evaluations",
29782980
Evaluations: []*featureproto.Evaluation{},
2979-
CreatedAt: time.Now().Unix(),
29802981
ForceUpdate: false,
29812982
ArchivedFeatureIds: []string{},
29822983
}
29832984
}
2985+
2986+
// assertEvaluationsCreatedAtNowForREST checks the server-stamped CreatedAt against
2987+
// the current time, then copies it into expected so the whole response can be compared.
2988+
func assertEvaluationsCreatedAtNowForREST(
2989+
t *testing.T,
2990+
expected, actual *getEvaluationsResponse,
2991+
desc string,
2992+
) {
2993+
t.Helper()
2994+
if expected == nil || expected.Evaluations == nil || actual == nil || actual.Evaluations == nil {
2995+
return
2996+
}
2997+
assert.InDelta(t, time.Now().Unix(), actual.Evaluations.CreatedAt, 5, "%s", desc)
2998+
expected.Evaluations.CreatedAt = actual.Evaluations.CreatedAt
2999+
}

0 commit comments

Comments
 (0)