2024-02-16 13:40:37 -05:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package billing
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
)
func TestRepository_New ( t * testing . T ) {
conn , _ := db . TestSetup ( t , "postgres" )
rw := db . New ( conn )
type args struct {
r db . Reader
w db . Writer
}
tests := [] struct {
name string
args args
want * Repository
wantIsErr errors . Code
wantErrMsg string
}{
{
name : "valid" ,
args : args {
r : rw ,
},
want : & Repository {
reader : rw ,
},
},
{
name : "nil-reader" ,
args : args {
r : nil ,
},
want : nil ,
wantIsErr : errors . InvalidParameter ,
wantErrMsg : "billing.NewRepository: nil db reader: parameter violation: error #100" ,
},
}
for _ , tt := range tests {
tt := tt
t . Run ( tt . name , func ( t * testing . T ) {
assert := assert . New ( t )
2024-02-20 21:36:05 +00:00
got , err := NewRepository ( context . Background (), tt . args . r )
2024-02-16 13:40:37 -05:00
if tt . wantIsErr != 0 {
assert . Truef ( errors . Match ( errors . T ( tt . wantIsErr ), err ), "Unexpected error %s" , err )
assert . Equal ( tt . wantErrMsg , err . Error ())
return
}
assert . NoError ( err )
assert . NotNil ( got )
assert . Equal ( tt . want , got )
})
}
}
func TestRepository_MonthlyActiveUsers ( t * testing . T ) {
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
2024-02-21 13:28:44 -05:00
TestGenerateActiveUsers ( t , conn )
2024-02-16 13:40:37 -05:00
today := time . Now (). UTC ()
2024-05-31 17:08:57 -04:00
// Some time calculations are impacted when using the current day vs. the
// start of the month. For example, if...
// today -> May 30th
// today.AddDate(0, -3, 0).Month() -> March
// February was expected here, but we get March. This seems to be a
// rounding thing since February 30th is not a valid date. Instead, the
// start of the month is used to ensure the correct months are calculated.
monthStart := time . Date ( today . Year (), today . Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
threeMonthsAgo := time . Date ( monthStart . AddDate ( 0 , - 3 , 0 ). Year (), monthStart . AddDate ( 0 , - 3 , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
oneMonthAgo := time . Date ( monthStart . AddDate ( 0 , - 1 , 0 ). Year (), monthStart . AddDate ( 0 , - 1 , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
2024-02-16 13:40:37 -05:00
midMonth := time . Date ( today . Year (), today . Month (), 15 , 0 , 0 , 0 , 0 , time . UTC )
t . Run ( "valid-no-options" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
2024-02-16 13:40:37 -05:00
activeUsers , err := repo . MonthlyActiveUsers ( ctx )
assert . NoError ( t , err )
require . Len ( t , activeUsers , 2 )
// check counts for the last two months
2024-02-23 09:39:23 -05:00
require . Equal ( t , uint32 ( 0 ), activeUsers [ 0 ]. ActiveUsersCount )
require . Equal ( t , uint32 ( 6 ), activeUsers [ 1 ]. ActiveUsersCount )
2024-02-16 13:40:37 -05:00
// assert start and end times are correct
// the current month (contains the hour)
assert . Equal ( t , time . Date ( today . Year (), today . Month (), 1 , 0 , 0 , 0 , 0 , time . UTC ), activeUsers [ 0 ]. StartTime )
assert . Equal ( t , time . Date ( today . Year (), today . Month (), today . Day (), today . Hour (), 0 , 0 , 0 , time . UTC ), activeUsers [ 0 ]. EndTime )
// the previous month
assert . Equal ( t , time . Date ( today . Year (), today . Month () - 1 , 1 , 0 , 0 , 0 , 0 , time . UTC ), activeUsers [ 1 ]. StartTime )
assert . Equal ( t , time . Date ( today . Year (), today . Month (), 1 , 0 , 0 , 0 , 0 , time . UTC ), activeUsers [ 1 ]. EndTime )
})
t . Run ( "valid-with-start-time" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
2024-02-16 13:40:37 -05:00
activeUsers , err := repo . MonthlyActiveUsers ( ctx , WithStartTime ( & threeMonthsAgo ))
assert . NoError ( t , err )
require . Len ( t , activeUsers , 4 )
for i := 0 ; i < 4 ; i ++ {
// check counts for the last four months
2024-05-06 09:03:01 -04:00
expectedStartTime := time . Date ( today . Year (), today . Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
2024-02-16 13:40:37 -05:00
if i == 0 {
2024-02-23 09:39:23 -05:00
assert . Equal ( t , uint32 ( 0 ), activeUsers [ i ]. ActiveUsersCount )
2024-02-16 13:40:37 -05:00
// the current month (contains the hour)
2024-05-06 09:03:01 -04:00
assert . Equal ( t , expectedStartTime , activeUsers [ i ]. StartTime )
2024-02-16 13:40:37 -05:00
assert . Equal ( t , time . Date ( today . Year (), today . Month (), today . Day (), today . Hour (), 0 , 0 , 0 , time . UTC ), activeUsers [ i ]. EndTime )
} else {
// create a sliding window of dates to assert start and end times are correct
2024-05-06 09:03:01 -04:00
// need to subtract month by month to prevent errors around month boundaries
expectedStartTime = time . Date ( expectedStartTime . AddDate ( 0 , - i , 0 ). Year (), expectedStartTime . AddDate ( 0 , - i , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
expectedEndTime := time . Date ( expectedStartTime . AddDate ( 0 , 1 , 0 ). Year (), expectedStartTime . AddDate ( 0 , 1 , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
2024-02-23 09:39:23 -05:00
assert . Equal ( t , uint32 ( 6 ), activeUsers [ i ]. ActiveUsersCount )
2024-02-16 13:40:37 -05:00
assert . Equal ( t , expectedStartTime , activeUsers [ i ]. StartTime )
assert . Equal ( t , expectedEndTime , activeUsers [ i ]. EndTime )
}
}
})
t . Run ( "valid-with-start-and-end-time" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
2024-02-16 13:40:37 -05:00
activeUsers , err := repo . MonthlyActiveUsers ( ctx , WithStartTime ( & threeMonthsAgo ), WithEndTime ( & oneMonthAgo ))
assert . NoError ( t , err )
2024-02-22 20:34:00 +00:00
require . Len ( t , activeUsers , 2 )
2024-05-31 17:08:57 -04:00
expectedStartTime := time . Date ( monthStart . AddDate ( 0 , - 2 , 0 ). Year (), monthStart . AddDate ( 0 , - 2 , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
expectedEndTime := time . Date ( monthStart . AddDate ( 0 , - 1 , 0 ). Year (), monthStart . AddDate ( 0 , - 1 , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
2024-02-23 09:39:23 -05:00
require . Equal ( t , uint32 ( 6 ), activeUsers [ 0 ]. ActiveUsersCount )
2024-02-21 13:28:44 -05:00
assert . Equal ( t , expectedStartTime , activeUsers [ 0 ]. StartTime )
assert . Equal ( t , expectedEndTime , activeUsers [ 0 ]. EndTime )
2024-02-22 20:34:00 +00:00
2024-05-31 17:08:57 -04:00
expectedStartTime = time . Date ( monthStart . AddDate ( 0 , - 3 , 0 ). Year (), monthStart . AddDate ( 0 , - 3 , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
expectedEndTime = time . Date ( monthStart . AddDate ( 0 , - 2 , 0 ). Year (), monthStart . AddDate ( 0 , - 2 , 0 ). Month (), 1 , 0 , 0 , 0 , 0 , time . UTC )
2024-02-23 09:39:23 -05:00
require . Equal ( t , uint32 ( 6 ), activeUsers [ 1 ]. ActiveUsersCount )
2024-02-22 20:34:00 +00:00
assert . Equal ( t , expectedStartTime , activeUsers [ 1 ]. StartTime )
assert . Equal ( t , expectedEndTime , activeUsers [ 1 ]. EndTime )
2024-02-16 13:40:37 -05:00
})
t . Run ( "invalid-end-time-without-start-time" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
_ , err := repo . MonthlyActiveUsers ( ctx , WithEndTime ( & oneMonthAgo ))
2024-02-16 13:40:37 -05:00
assert . Error ( t , err )
assert . Equal ( t , "billing.Repository.MonthlyActiveUsers: end time set without start time: parameter violation: error #100" , err . Error ())
})
t . Run ( "invalid-end-time-before-start-time" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
_ , err := repo . MonthlyActiveUsers ( ctx , WithStartTime ( & oneMonthAgo ), WithEndTime ( & threeMonthsAgo ))
2024-02-16 13:40:37 -05:00
assert . Error ( t , err )
assert . Equal ( t , "billing.Repository.MonthlyActiveUsers: start time is not before end time: parameter violation: error #100" , err . Error ())
})
t . Run ( "invalid-start-time-equals-end-time" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
_ , err := repo . MonthlyActiveUsers ( ctx , WithStartTime ( & oneMonthAgo ), WithEndTime ( & oneMonthAgo ))
2024-02-16 13:40:37 -05:00
assert . Error ( t , err )
assert . Equal ( t , "billing.Repository.MonthlyActiveUsers: start time is not before end time: parameter violation: error #100" , err . Error ())
})
t . Run ( "invalid-start-time-not-first-day-of-month" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
_ , err := repo . MonthlyActiveUsers ( ctx , WithStartTime ( & midMonth ))
2024-02-16 13:40:37 -05:00
assert . Error ( t , err )
assert . Equal ( t , "billing.Repository.MonthlyActiveUsers: start time must be the first day of the month at midnight UTC: parameter violation: error #100" , err . Error ())
})
t . Run ( "invalid-end-time-not-first-day-of-month" , func ( t * testing . T ) {
2024-02-21 13:28:44 -05:00
repo := TestRepo ( t , conn )
_ , err := repo . MonthlyActiveUsers ( ctx , WithStartTime ( & oneMonthAgo ), WithEndTime ( & midMonth ))
2024-02-16 13:40:37 -05:00
assert . Error ( t , err )
assert . Equal ( t , "billing.Repository.MonthlyActiveUsers: end time must be the first day of the month at midnight UTC: parameter violation: error #100" , err . Error ())
})
}