@@ -44,6 +44,7 @@ import (
44
44
"github.com/coder/coder/v2/coderd/schedule"
45
45
"github.com/coder/coder/v2/coderd/schedule/cron"
46
46
"github.com/coder/coder/v2/coderd/telemetry"
47
+ "github.com/coder/coder/v2/coderd/usage"
47
48
"github.com/coder/coder/v2/coderd/wspubsub"
48
49
"github.com/coder/coder/v2/codersdk"
49
50
"github.com/coder/coder/v2/codersdk/agentsdk"
@@ -67,6 +68,13 @@ func testUserQuietHoursScheduleStore() *atomic.Pointer[schedule.UserQuietHoursSc
67
68
return ptr
68
69
}
69
70
71
+ func testUsageInserter () * atomic.Pointer [usage.Inserter ] {
72
+ ptr := & atomic.Pointer [usage.Inserter ]{}
73
+ inserter := usage .NewAGPLInserter ()
74
+ ptr .Store (& inserter )
75
+ return ptr
76
+ }
77
+
70
78
func TestAcquireJob_LongPoll (t * testing.T ) {
71
79
t .Parallel ()
72
80
//nolint:dogsled
@@ -2672,7 +2680,10 @@ func TestCompleteJob(t *testing.T) {
2672
2680
t .Run (tc .name , func (t * testing.T ) {
2673
2681
t .Parallel ()
2674
2682
2675
- srv , db , _ , pd := setup (t , false , & overrides {})
2683
+ fakeUsageInserter , usageInserterPtr := newFakeUsageInserter ()
2684
+ srv , db , _ , pd := setup (t , false , & overrides {
2685
+ usageInserter : usageInserterPtr ,
2686
+ })
2676
2687
2677
2688
importJobID := uuid .New ()
2678
2689
tvID := uuid .New ()
@@ -2741,6 +2752,10 @@ func TestCompleteJob(t *testing.T) {
2741
2752
require .NoError (t , err )
2742
2753
require .True (t , version .HasAITask .Valid ) // We ALWAYS expect a value to be set, therefore not nil, i.e. valid = true.
2743
2754
require .Equal (t , tc .expected , version .HasAITask .Bool )
2755
+
2756
+ // We never expect a usage event to be collected for
2757
+ // template imports.
2758
+ require .Empty (t , fakeUsageInserter .collectedEvents )
2744
2759
})
2745
2760
}
2746
2761
})
@@ -2750,9 +2765,9 @@ func TestCompleteJob(t *testing.T) {
2750
2765
// will be set as well in that case.
2751
2766
t .Run ("WorkspaceBuild" , func (t * testing.T ) {
2752
2767
type testcase struct {
2753
- name string
2754
- input * proto.CompletedJob_WorkspaceBuild
2755
- expected bool
2768
+ name string
2769
+ input * proto.CompletedJob_WorkspaceBuild
2770
+ expectedHasAiTask bool
2756
2771
}
2757
2772
2758
2773
sidebarAppID := uuid .NewString ()
@@ -2762,7 +2777,7 @@ func TestCompleteJob(t *testing.T) {
2762
2777
input : & proto.CompletedJob_WorkspaceBuild {
2763
2778
// No AiTasks defined.
2764
2779
},
2765
- expected : false ,
2780
+ expectedHasAiTask : false ,
2766
2781
},
2767
2782
{
2768
2783
name : "has_ai_task is set to true" ,
@@ -2792,7 +2807,7 @@ func TestCompleteJob(t *testing.T) {
2792
2807
},
2793
2808
},
2794
2809
},
2795
- expected : true ,
2810
+ expectedHasAiTask : true ,
2796
2811
},
2797
2812
// Checks regression for https://github.com/coder/coder/issues/18776
2798
2813
{
@@ -2808,13 +2823,16 @@ func TestCompleteJob(t *testing.T) {
2808
2823
},
2809
2824
},
2810
2825
},
2811
- expected : false ,
2826
+ expectedHasAiTask : false ,
2812
2827
},
2813
2828
} {
2814
2829
t .Run (tc .name , func (t * testing.T ) {
2815
2830
t .Parallel ()
2816
2831
2817
- srv , db , _ , pd := setup (t , false , & overrides {})
2832
+ fakeUsageInserter , usageInserterPtr := newFakeUsageInserter ()
2833
+ srv , db , _ , pd := setup (t , false , & overrides {
2834
+ usageInserter : usageInserterPtr ,
2835
+ })
2818
2836
2819
2837
importJobID := uuid .New ()
2820
2838
tvID := uuid .New ()
@@ -2899,10 +2917,19 @@ func TestCompleteJob(t *testing.T) {
2899
2917
build , err = db .GetWorkspaceBuildByID (ctx , build .ID )
2900
2918
require .NoError (t , err )
2901
2919
require .True (t , build .HasAITask .Valid ) // We ALWAYS expect a value to be set, therefore not nil, i.e. valid = true.
2902
- require .Equal (t , tc .expected , build .HasAITask .Bool )
2920
+ require .Equal (t , tc .expectedHasAiTask , build .HasAITask .Bool )
2903
2921
2904
- if tc .expected {
2922
+ if tc .expectedHasAiTask {
2905
2923
require .Equal (t , sidebarAppID , build .AITaskSidebarAppID .UUID .String ())
2924
+
2925
+ // Check that a usage event was collected.
2926
+ require .Len (t , fakeUsageInserter .collectedEvents , 1 )
2927
+ require .Equal (t , usage.DCManagedAgentsV1 {
2928
+ Count : 1 ,
2929
+ }, fakeUsageInserter .collectedEvents [0 ])
2930
+ } else {
2931
+ // Check that no usage event was collected.
2932
+ require .Empty (t , fakeUsageInserter .collectedEvents )
2906
2933
}
2907
2934
})
2908
2935
}
@@ -3835,6 +3862,7 @@ type overrides struct {
3835
3862
externalAuthConfigs []* externalauth.Config
3836
3863
templateScheduleStore * atomic.Pointer [schedule.TemplateScheduleStore ]
3837
3864
userQuietHoursScheduleStore * atomic.Pointer [schedule.UserQuietHoursScheduleStore ]
3865
+ usageInserter * atomic.Pointer [usage.Inserter ]
3838
3866
clock * quartz.Mock
3839
3867
acquireJobLongPollDuration time.Duration
3840
3868
heartbeatFn func (ctx context.Context ) error
@@ -3855,6 +3883,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
3855
3883
var externalAuthConfigs []* externalauth.Config
3856
3884
tss := testTemplateScheduleStore ()
3857
3885
uqhss := testUserQuietHoursScheduleStore ()
3886
+ usageInserter := testUsageInserter ()
3858
3887
clock := quartz .NewReal ()
3859
3888
pollDur := time .Duration (0 )
3860
3889
if ov == nil {
@@ -3892,6 +3921,15 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
3892
3921
require .True (t , swapped )
3893
3922
}
3894
3923
}
3924
+ if ov .usageInserter != nil {
3925
+ tUsageInserter := usageInserter .Load ()
3926
+ // keep the initial test value if the override hasn't set the atomic pointer.
3927
+ usageInserter = ov .usageInserter
3928
+ if usageInserter .Load () == nil {
3929
+ swapped := usageInserter .CompareAndSwap (nil , tUsageInserter )
3930
+ require .True (t , swapped )
3931
+ }
3932
+ }
3895
3933
if ov .clock != nil {
3896
3934
clock = ov .clock
3897
3935
}
@@ -3947,6 +3985,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
3947
3985
auditPtr ,
3948
3986
tss ,
3949
3987
uqhss ,
3988
+ usageInserter ,
3950
3989
deploymentValues ,
3951
3990
provisionerdserver.Options {
3952
3991
ExternalAuthConfigs : externalAuthConfigs ,
@@ -4061,3 +4100,22 @@ func (s *fakeStream) cancel() {
4061
4100
s .canceled = true
4062
4101
s .c .Broadcast ()
4063
4102
}
4103
+
4104
+ type fakeUsageInserter struct {
4105
+ collectedEvents []usage.Event
4106
+ }
4107
+
4108
+ var _ usage.Inserter = & fakeUsageInserter {}
4109
+
4110
+ func newFakeUsageInserter () (* fakeUsageInserter , * atomic.Pointer [usage.Inserter ]) {
4111
+ ptr := & atomic.Pointer [usage.Inserter ]{}
4112
+ fake := & fakeUsageInserter {}
4113
+ var inserter usage.Inserter = fake
4114
+ ptr .Store (& inserter )
4115
+ return fake , ptr
4116
+ }
4117
+
4118
+ func (f * fakeUsageInserter ) InsertDiscreteUsageEvent (_ context.Context , _ database.Store , event usage.DiscreteEvent ) error {
4119
+ f .collectedEvents = append (f .collectedEvents , event )
4120
+ return nil
4121
+ }
0 commit comments