@@ -49,16 +49,20 @@ func newMetricsCache(t *testing.T, log slog.Logger, clock quartz.Clock, interval
4949
5050func TestCache_TemplateWorkspaceOwners (t * testing.T ) {
5151t .Parallel ()
52- var ()
5352
5453var (
55- log = testutil .Logger (t )
56- clock = quartz .NewReal ()
57- cache ,db = newMetricsCache (t ,log ,clock , metricscache.Intervals {
58- TemplateBuildTimes :testutil .IntervalFast ,
59- },false )
54+ ctx = testutil .Context (t ,testutil .WaitShort )
55+ log = testutil .Logger (t )
56+ clock = quartz .NewMock (t )
6057)
6158
59+ trapTickerFunc := clock .Trap ().TickerFunc ("metricscache" )
60+ defer trapTickerFunc .Close ()
61+
62+ cache ,db := newMetricsCache (t ,log ,clock , metricscache.Intervals {
63+ TemplateBuildTimes :time .Minute ,
64+ },false )
65+
6266org := dbgen .Organization (t ,db , database.Organization {})
6367user1 := dbgen .User (t ,db , database.User {})
6468user2 := dbgen .User (t ,db , database.User {})
@@ -67,38 +71,38 @@ func TestCache_TemplateWorkspaceOwners(t *testing.T) {
6771Provisioner :database .ProvisionerTypeEcho ,
6872CreatedBy :user1 .ID ,
6973})
70- require .Eventuallyf (t ,func ()bool {
71- count ,ok := cache .TemplateWorkspaceOwners (template .ID )
72- return ok && count == 0
73- },testutil .WaitShort ,testutil .IntervalMedium ,
74- "TemplateWorkspaceOwners never populated 0 owners" ,
75- )
74+
75+ // Wait for both ticker functions to be created (template build times and deployment stats)
76+ trapTickerFunc .MustWait (ctx ).MustRelease (ctx )
77+ trapTickerFunc .MustWait (ctx ).MustRelease (ctx )
78+
79+ clock .Advance (time .Minute ).MustWait (ctx )
80+
81+ count ,ok := cache .TemplateWorkspaceOwners (template .ID )
82+ require .True (t ,ok ,"TemplateWorkspaceOwners should be populated" )
83+ require .Equal (t ,0 ,count ,"should have 0 owners initially" )
7684
7785dbgen .Workspace (t ,db , database.WorkspaceTable {
7886OrganizationID :org .ID ,
7987TemplateID :template .ID ,
8088OwnerID :user1 .ID ,
8189})
8290
83- require .Eventuallyf (t ,func ()bool {
84- count ,_ := cache .TemplateWorkspaceOwners (template .ID )
85- return count == 1
86- },testutil .WaitShort ,testutil .IntervalMedium ,
87- "TemplateWorkspaceOwners never populated 1 owner" ,
88- )
91+ clock .Advance (time .Minute ).MustWait (ctx )
92+
93+ count ,_ = cache .TemplateWorkspaceOwners (template .ID )
94+ require .Equal (t ,1 ,count ,"should have 1 owner after adding workspace" )
8995
9096workspace2 := dbgen .Workspace (t ,db , database.WorkspaceTable {
9197OrganizationID :org .ID ,
9298TemplateID :template .ID ,
9399OwnerID :user2 .ID ,
94100})
95101
96- require .Eventuallyf (t ,func ()bool {
97- count ,_ := cache .TemplateWorkspaceOwners (template .ID )
98- return count == 2
99- },testutil .WaitShort ,testutil .IntervalMedium ,
100- "TemplateWorkspaceOwners never populated 2 owners" ,
101- )
102+ clock .Advance (time .Minute ).MustWait (ctx )
103+
104+ count ,_ = cache .TemplateWorkspaceOwners (template .ID )
105+ require .Equal (t ,2 ,count ,"should have 2 owners after adding second workspace" )
102106
103107// 3rd workspace should not be counted since we have the same owner as workspace2.
104108dbgen .Workspace (t ,db , database.WorkspaceTable {
@@ -112,12 +116,10 @@ func TestCache_TemplateWorkspaceOwners(t *testing.T) {
112116Deleted :true ,
113117})
114118
115- require .Eventuallyf (t ,func ()bool {
116- count ,_ := cache .TemplateWorkspaceOwners (template .ID )
117- return count == 1
118- },testutil .WaitShort ,testutil .IntervalMedium ,
119- "TemplateWorkspaceOwners never populated 1 owner after delete" ,
120- )
119+ clock .Advance (time .Minute ).MustWait (ctx )
120+
121+ count ,_ = cache .TemplateWorkspaceOwners (template .ID )
122+ require .Equal (t ,1 ,count ,"should have 1 owner after deleting workspace" )
121123}
122124
123125func clockTime (t time.Time ,hour ,minute ,sec int ) time.Time {
@@ -206,15 +208,20 @@ func TestCache_BuildTime(t *testing.T) {
206208t .Parallel ()
207209
208210var (
209- log = testutil .Logger (t )
210- clock = quartz .NewMock (t )
211- cache ,db = newMetricsCache (t ,log ,clock , metricscache.Intervals {
212- TemplateBuildTimes :testutil .IntervalFast ,
213- },false )
211+ ctx = testutil .Context (t ,testutil .WaitShort )
212+ log = testutil .Logger (t )
213+ clock = quartz .NewMock (t )
214214)
215215
216216clock .Set (someDay )
217217
218+ trapTickerFunc := clock .Trap ().TickerFunc ("metricscache" )
219+
220+ defer trapTickerFunc .Close ()
221+ cache ,db := newMetricsCache (t ,log ,clock , metricscache.Intervals {
222+ TemplateBuildTimes :time .Minute ,
223+ },false )
224+
218225org := dbgen .Organization (t ,db , database.Organization {})
219226user := dbgen .User (t ,db , database.User {})
220227
@@ -257,17 +264,19 @@ func TestCache_BuildTime(t *testing.T) {
257264})
258265}
259266
267+ // Wait for both ticker functions to be created (template build times and deployment stats)
268+ trapTickerFunc .MustWait (ctx ).MustRelease (ctx )
269+ trapTickerFunc .MustWait (ctx ).MustRelease (ctx )
270+
271+ clock .Advance (time .Minute ).MustWait (ctx )
272+
260273if tt .want .loads {
261274wantTransition := codersdk .WorkspaceTransition (tt .args .transition )
262- require .Eventuallyf (t ,func ()bool {
263- stats := cache .TemplateBuildTimeStats (template .ID )
264- ts := stats [wantTransition ]
265- return ts .P50 != nil && * ts .P50 == tt .want .buildTimeMs
266- },testutil .WaitLong ,testutil .IntervalMedium ,
267- "P50 never reached expected value for %v" ,wantTransition ,
268- )
269-
270275gotStats := cache .TemplateBuildTimeStats (template .ID )
276+ ts := gotStats [wantTransition ]
277+ require .NotNil (t ,ts .P50 ,"P50 should be set for %v" ,wantTransition )
278+ require .Equal (t ,tt .want .buildTimeMs ,* ts .P50 ,"P50 should match expected value for %v" ,wantTransition )
279+
271280for transition ,ts := range gotStats {
272281if transition == wantTransition {
273282// Checked above
@@ -276,14 +285,8 @@ func TestCache_BuildTime(t *testing.T) {
276285require .Empty (t ,ts ,"%v" ,transition )
277286}
278287}else {
279- var stats codersdk.TemplateBuildTimeStats
280- require .Never (t ,func ()bool {
281- stats = cache .TemplateBuildTimeStats (template .ID )
282- requireBuildTimeStatsEmpty (t ,stats )
283- return t .Failed ()
284- },testutil .WaitShort / 2 ,testutil .IntervalMedium ,
285- "BuildTimeStats populated" ,stats ,
286- )
288+ stats := cache .TemplateBuildTimeStats (template .ID )
289+ requireBuildTimeStatsEmpty (t ,stats )
287290}
288291})
289292}
@@ -293,13 +296,18 @@ func TestCache_DeploymentStats(t *testing.T) {
293296t .Parallel ()
294297
295298var (
296- log = testutil .Logger (t )
297- clock = quartz .NewMock (t )
298- cache ,db = newMetricsCache (t ,log ,clock , metricscache.Intervals {
299- DeploymentStats :testutil .IntervalFast ,
300- },false )
299+ ctx = testutil .Context (t ,testutil .WaitShort )
300+ log = testutil .Logger (t )
301+ clock = quartz .NewMock (t )
301302)
302303
304+ tickerTrap := clock .Trap ().TickerFunc ("metricscache" )
305+ defer tickerTrap .Close ()
306+
307+ cache ,db := newMetricsCache (t ,log ,clock , metricscache.Intervals {
308+ DeploymentStats :time .Minute ,
309+ },false )
310+
303311err := db .InsertWorkspaceAgentStats (context .Background (), database.InsertWorkspaceAgentStatsParams {
304312ID : []uuid.UUID {uuid .New ()},
305313CreatedAt : []time.Time {clock .Now ()},
@@ -323,11 +331,13 @@ func TestCache_DeploymentStats(t *testing.T) {
323331})
324332require .NoError (t ,err )
325333
326- var stat codersdk.DeploymentStats
327- require .Eventually (t ,func ()bool {
328- var ok bool
329- stat ,ok = cache .DeploymentStats ()
330- return ok
331- },testutil .WaitLong ,testutil .IntervalMedium )
334+ // Wait for both ticker functions to be created (template build times and deployment stats)
335+ tickerTrap .MustWait (ctx ).MustRelease (ctx )
336+ tickerTrap .MustWait (ctx ).MustRelease (ctx )
337+
338+ clock .Advance (time .Minute ).MustWait (ctx )
339+
340+ stat ,ok := cache .DeploymentStats ()
341+ require .True (t ,ok ,"cache should be populated after refresh" )
332342require .Equal (t ,int64 (1 ),stat .SessionCount .VSCode )
333343}