@ -26,18 +26,18 @@ import (
"github.com/stretchr/testify/require"
)
func TestAlertRuleInfo ( t * testing . T ) {
func TestAlertRule ( t * testing . T ) {
type evalResponse struct {
success bool
droppedEval * e valuation
droppedEval * E valuation
}
t . Run ( "when rule evaluation is not stopped" , func ( t * testing . T ) {
t . Run ( "update should send to updateCh" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r := blankRuleForTests ( context . Background ( ) )
resultCh := make ( chan bool )
go func ( ) {
resultCh <- r . update ( r uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false } )
resultCh <- r . Update ( R uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false } )
} ( )
select {
case <- r . updateCh :
@ -47,22 +47,22 @@ func TestAlertRuleInfo(t *testing.T) {
}
} )
t . Run ( "update should drop any concurrent sending to updateCh" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
version1 := r uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false }
version2 := r uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false }
r := blankRuleForTests ( context . Background ( ) )
version1 := R uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false }
version2 := R uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false }
wg := sync . WaitGroup { }
wg . Add ( 1 )
go func ( ) {
wg . Done ( )
r . u pdate( version1 )
r . U pdate( version1 )
wg . Done ( )
} ( )
wg . Wait ( )
wg . Add ( 2 ) // one when time1 is sent, another when go-routine for time2 has started
go func ( ) {
wg . Done ( )
r . u pdate( version2 )
r . U pdate( version2 )
} ( )
wg . Wait ( ) // at this point tick 1 has already been dropped
select {
@ -73,16 +73,16 @@ func TestAlertRuleInfo(t *testing.T) {
}
} )
t . Run ( "eval should send to evalCh" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r := blankRuleForTests ( context . Background ( ) )
expected := time . Now ( )
resultCh := make ( chan evalResponse )
data := & e valuation{
data := & E valuation{
scheduledAt : expected ,
rule : models . AlertRuleGen ( ) ( ) ,
folderTitle : util . GenerateShortUID ( ) ,
}
go func ( ) {
result , dropped := r . e val( data )
result , dropped := r . E val( data )
resultCh <- evalResponse { result , dropped }
} ( )
select {
@ -96,17 +96,17 @@ func TestAlertRuleInfo(t *testing.T) {
}
} )
t . Run ( "eval should drop any concurrent sending to evalCh" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r := blankRuleForTests ( context . Background ( ) )
time1 := time . UnixMilli ( rand . Int63n ( math . MaxInt64 ) )
time2 := time . UnixMilli ( rand . Int63n ( math . MaxInt64 ) )
resultCh1 := make ( chan evalResponse )
resultCh2 := make ( chan evalResponse )
data := & e valuation{
data := & E valuation{
scheduledAt : time1 ,
rule : models . AlertRuleGen ( ) ( ) ,
folderTitle : util . GenerateShortUID ( ) ,
}
data2 := & e valuation{
data2 := & E valuation{
scheduledAt : time2 ,
rule : data . rule ,
folderTitle : data . folderTitle ,
@ -115,7 +115,7 @@ func TestAlertRuleInfo(t *testing.T) {
wg . Add ( 1 )
go func ( ) {
wg . Done ( )
result , dropped := r . e val( data )
result , dropped := r . E val( data )
wg . Done ( )
resultCh1 <- evalResponse { result , dropped }
} ( )
@ -123,7 +123,7 @@ func TestAlertRuleInfo(t *testing.T) {
wg . Add ( 2 ) // one when time1 is sent, another when go-routine for time2 has started
go func ( ) {
wg . Done ( )
result , dropped := r . e val( data2 )
result , dropped := r . E val( data2 )
resultCh2 <- evalResponse { result , dropped }
} ( )
wg . Wait ( ) // at this point tick 1 has already been dropped
@ -142,19 +142,19 @@ func TestAlertRuleInfo(t *testing.T) {
}
} )
t . Run ( "eval should exit when context is cancelled" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r := blankRuleForTests ( context . Background ( ) )
resultCh := make ( chan evalResponse )
data := & e valuation{
data := & E valuation{
scheduledAt : time . Now ( ) ,
rule : models . AlertRuleGen ( ) ( ) ,
folderTitle : util . GenerateShortUID ( ) ,
}
go func ( ) {
result , dropped := r . e val( data )
result , dropped := r . E val( data )
resultCh <- evalResponse { result , dropped }
} ( )
runtime . Gosched ( )
r . s top( nil )
r . S top( nil )
select {
case result := <- resultCh :
require . False ( t , result . success )
@ -166,37 +166,37 @@ func TestAlertRuleInfo(t *testing.T) {
} )
t . Run ( "when rule evaluation is stopped" , func ( t * testing . T ) {
t . Run ( "Update should do nothing" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r . s top( errRuleDeleted )
r := blankRuleForTests ( context . Background ( ) )
r . S top( errRuleDeleted )
require . ErrorIs ( t , r . ctx . Err ( ) , errRuleDeleted )
require . False ( t , r . update ( r uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false } ) )
require . False ( t , r . Update ( R uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false } ) )
} )
t . Run ( "eval should do nothing" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r . s top( nil )
data := & e valuation{
r := blankRuleForTests ( context . Background ( ) )
r . S top( nil )
data := & E valuation{
scheduledAt : time . Now ( ) ,
rule : models . AlertRuleGen ( ) ( ) ,
folderTitle : util . GenerateShortUID ( ) ,
}
success , dropped := r . e val( data )
success , dropped := r . E val( data )
require . False ( t , success )
require . Nilf ( t , dropped , "expected no dropped evaluations but got one" )
} )
t . Run ( "stop should do nothing" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r . s top( nil )
r . s top( nil )
r := blankRuleForTests ( context . Background ( ) )
r . S top( nil )
r . S top( nil )
} )
t . Run ( "stop should do nothing if parent context stopped" , func ( t * testing . T ) {
ctx , cancelFn := context . WithCancel ( context . Background ( ) )
r := blankRuleInfo ForTests ( ctx )
r := blankRuleForTests ( ctx )
cancelFn ( )
r . s top( nil )
r . S top( nil )
} )
} )
t . Run ( "should be thread-safe" , func ( t * testing . T ) {
r := blankRuleInfo ForTests ( context . Background ( ) )
r := blankRuleForTests ( context . Background ( ) )
wg := sync . WaitGroup { }
go func ( ) {
for {
@ -221,15 +221,15 @@ func TestAlertRuleInfo(t *testing.T) {
}
switch rand . Intn ( max ) + 1 {
case 1 :
r . update ( r uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false } )
r . Update ( R uleVersionAndPauseStatus{ fingerprint ( rand . Uint64 ( ) ) , false } )
case 2 :
r . eval ( & e valuation{
r . Eval ( & E valuation{
scheduledAt : time . Now ( ) ,
rule : models . AlertRuleGen ( ) ( ) ,
folderTitle : util . GenerateShortUID ( ) ,
} )
case 3 :
r . s top( nil )
r . S top( nil )
}
}
wg . Done ( )
@ -240,9 +240,8 @@ func TestAlertRuleInfo(t *testing.T) {
} )
}
func blankRuleInfoForTests ( ctx context . Context ) * alertRuleInfo {
factory := newRuleFactory ( nil , false , 0 , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil )
return factory . new ( context . Background ( ) )
func blankRuleForTests ( ctx context . Context ) * alertRule {
return newAlertRule ( context . Background ( ) , nil , false , 0 , nil , nil , nil , nil , nil , nil , nil , nil , nil , nil )
}
func TestRuleRoutine ( t * testing . T ) {
@ -279,16 +278,16 @@ func TestRuleRoutine(t *testing.T) {
t . Cleanup ( cancel )
ruleInfo := factory . new ( ctx )
go func ( ) {
_ = ruleInfo . r un( rule . GetKey ( ) )
_ = ruleInfo . R un( rule . GetKey ( ) )
} ( )
expectedTime := time . UnixMicro ( rand . Int63 ( ) )
ruleInfo . evalCh <- & e valuation{
ruleInfo . Eval ( & E valuation{
scheduledAt : expectedTime ,
rule : rule ,
folderTitle : folderTitle ,
}
} )
actualTime := waitForTimeChannel ( t , evalAppliedChan )
require . Equal ( t , expectedTime , actualTime )
@ -428,7 +427,7 @@ func TestRuleRoutine(t *testing.T) {
ctx , cancel := context . WithCancel ( context . Background ( ) )
ruleInfo := factory . new ( ctx )
go func ( ) {
err := ruleInfo . r un( models . AlertRuleKey { } )
err := ruleInfo . R un( models . AlertRuleKey { } )
stoppedChan <- err
} ( )
@ -448,11 +447,11 @@ func TestRuleRoutine(t *testing.T) {
factory := ruleFactoryFromScheduler ( sch )
ruleInfo := factory . new ( context . Background ( ) )
go func ( ) {
err := ruleInfo . r un( rule . GetKey ( ) )
err := ruleInfo . R un( rule . GetKey ( ) )
stoppedChan <- err
} ( )
ruleInfo . s top( errRuleDeleted )
ruleInfo . S top( errRuleDeleted )
err := waitForErrChannel ( t , stoppedChan )
require . NoError ( t , err )
@ -479,15 +478,15 @@ func TestRuleRoutine(t *testing.T) {
ruleInfo := factory . new ( ctx )
go func ( ) {
_ = ruleInfo . r un( rule . GetKey ( ) )
_ = ruleInfo . R un( rule . GetKey ( ) )
} ( )
// init evaluation loop so it got the rule version
ruleInfo . evalCh <- & e valuation{
ruleInfo . Eval ( & E valuation{
scheduledAt : sch . clock . Now ( ) ,
rule : rule ,
folderTitle : folderTitle ,
}
} )
waitForTimeChannel ( t , evalAppliedChan )
@ -519,8 +518,8 @@ func TestRuleRoutine(t *testing.T) {
require . Greaterf ( t , expectedToBeSent , 0 , "State manager was expected to return at least one state that can be expired" )
t . Run ( "should do nothing if version in channel is the same" , func ( t * testing . T ) {
ruleInfo . updateCh <- r uleVersionAndPauseStatus{ ruleFp , false }
ruleInfo . updateCh <- r uleVersionAndPauseStatus{ ruleFp , false } // second time just to make sure that previous messages were handled
ruleInfo . Update ( R uleVersionAndPauseStatus{ ruleFp , false } )
ruleInfo . Update ( R uleVersionAndPauseStatus{ ruleFp , false } ) // second time just to make sure that previous messages were handled
actualStates := sch . stateManager . GetStatesForRuleUID ( rule . OrgID , rule . UID )
require . Len ( t , actualStates , len ( states ) )
@ -529,7 +528,7 @@ func TestRuleRoutine(t *testing.T) {
} )
t . Run ( "should clear the state and expire firing alerts if version in channel is greater" , func ( t * testing . T ) {
ruleInfo . updateCh <- r uleVersionAndPauseStatus{ ruleFp + 1 , false }
ruleInfo . Update ( R uleVersionAndPauseStatus{ ruleFp + 1 , false } )
require . Eventually ( t , func ( ) bool {
return len ( sender . Calls ( ) ) > 0
@ -561,13 +560,13 @@ func TestRuleRoutine(t *testing.T) {
ruleInfo := factory . new ( ctx )
go func ( ) {
_ = ruleInfo . r un( rule . GetKey ( ) )
_ = ruleInfo . R un( rule . GetKey ( ) )
} ( )
ruleInfo . evalCh <- & e valuation{
ruleInfo . Eval ( & E valuation{
scheduledAt : sch . clock . Now ( ) ,
rule : rule ,
}
} )
waitForTimeChannel ( t , evalAppliedChan )
@ -667,13 +666,13 @@ func TestRuleRoutine(t *testing.T) {
ruleInfo := factory . new ( ctx )
go func ( ) {
_ = ruleInfo . r un( rule . GetKey ( ) )
_ = ruleInfo . R un( rule . GetKey ( ) )
} ( )
ruleInfo . evalCh <- & e valuation{
ruleInfo . Eval ( & E valuation{
scheduledAt : sch . clock . Now ( ) ,
rule : rule ,
}
} )
waitForTimeChannel ( t , evalAppliedChan )
@ -701,13 +700,13 @@ func TestRuleRoutine(t *testing.T) {
ruleInfo := factory . new ( ctx )
go func ( ) {
_ = ruleInfo . r un( rule . GetKey ( ) )
_ = ruleInfo . R un( rule . GetKey ( ) )
} ( )
ruleInfo . evalCh <- & e valuation{
ruleInfo . Eval ( & E valuation{
scheduledAt : sch . clock . Now ( ) ,
rule : rule ,
}
} )
waitForTimeChannel ( t , evalAppliedChan )