package main import ( "io" "log" "os" "sync" "testing" "git.sr.ht/~michalr/go-satel" "github.com/stretchr/testify/assert" ) func TestSatelEventTypeFiltering(t *testing.T) { testEvents := make(chan satel.Event) receivedEvents := make([]satel.Event, 0) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() for e := range FilterByTypeOrIndex(testEvents, &wg, []SatelChangeType{{satel.ArmedPartition}, {satel.PartitionFireAlarm}}, []int{}) { receivedEvents = append(receivedEvents, e) } }() testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, true) testEvents <- makeTestSatelEvent(satel.DoorOpened, 2, true) testEvents <- makeTestSatelEvent(satel.PartitionAlarm, 3, true) testEvents <- makeTestSatelEvent(satel.PartitionFireAlarm, 4, true) testEvents <- makeTestSatelEvent(satel.TroublePart1, 5, true) testEvents <- makeTestSatelEvent(satel.ZoneTamper, 6, true) close(testEvents) wg.Wait() assert.Len(t, receivedEvents, 2) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 1, true)) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.PartitionFireAlarm, 4, true)) } func TestSatelEventTypeFiltering_NoAllowedEventTypesMeansAllAreAllowed(t *testing.T) { testEvents := make(chan satel.Event) receivedEvents := make([]satel.Event, 0) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() for e := range FilterByTypeOrIndex(testEvents, &wg, []SatelChangeType{}, []int{}) { receivedEvents = append(receivedEvents, e) } }() for index, ct := range SUPPORTED_CHANGE_TYPES { testEvents <- makeTestSatelEvent(ct, index, true) } close(testEvents) wg.Wait() assert.Len(t, receivedEvents, len(SUPPORTED_CHANGE_TYPES)) for index, ct := range SUPPORTED_CHANGE_TYPES { assert.Contains(t, receivedEvents, makeTestSatelEvent(ct, index, true)) } } func TestSatelIndexFiltering(t *testing.T) { testEvents := make(chan satel.Event) receivedEvents := make([]satel.Event, 0) wg := sync.WaitGroup{} wg.Add(1) go func() { defer wg.Done() for e := range FilterByTypeOrIndex(testEvents, &wg, []SatelChangeType{}, []int{1, 3}) { receivedEvents = append(receivedEvents, e) } }() testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, true) testEvents <- makeTestSatelEvent(satel.DoorOpened, 2, true) testEvents <- makeTestSatelEvent(satel.PartitionAlarm, 3, true) testEvents <- makeTestSatelEvent(satel.PartitionFireAlarm, 4, true) testEvents <- makeTestSatelEvent(satel.TroublePart1, 5, true) testEvents <- makeTestSatelEvent(satel.ZoneTamper, 6, true) close(testEvents) wg.Wait() assert.Len(t, receivedEvents, 2) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 1, true)) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.PartitionAlarm, 3, true)) } func TestSatelIndexFiltering_NoAllowedEventTypesMeansAllAreAllowed(t *testing.T) { testEvents := make(chan satel.Event) receivedEvents := make([]satel.Event, 0) wg := sync.WaitGroup{} myReasonableMaxIndex := 100 // I wanted to use math.MaxInt at first, but it's kind of a waste of time here wg.Add(1) go func() { defer wg.Done() for e := range FilterByTypeOrIndex(testEvents, &wg, []SatelChangeType{}, []int{}) { receivedEvents = append(receivedEvents, e) } }() for i := 0; i < myReasonableMaxIndex; i++ { testEvents <- makeTestSatelEvent(satel.ArmedPartition, i, true) } close(testEvents) wg.Wait() assert.Len(t, receivedEvents, myReasonableMaxIndex) for i := 0; i < myReasonableMaxIndex; i++ { assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, i, true)) } } func TestSatelLastSeenFiltering(t *testing.T) { f, err := os.CreateTemp("", "TestSatelLastSeenFiltering") assert.NoError(t, err) tempFileName := f.Name() assert.NoError(t, f.Close()) defer os.Remove(f.Name()) testEvents := make(chan satel.Event) receivedEvents := make([]satel.Event, 0) wg := sync.WaitGroup{} fakeLog := log.New(io.Discard, "", log.Ltime) ds := MakeDataStore(fakeLog, tempFileName) wg.Add(1) go func() { defer wg.Done() for e := range FilterByLastSeen(testEvents, &wg, &ds, fakeLog) { receivedEvents = append(receivedEvents, e) } }() testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 2, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, false) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 2, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, false) close(testEvents) wg.Wait() assert.Len(t, receivedEvents, 3) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 1, true)) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 2, true)) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 1, false)) } func TestSatelLastSeenFilteringWithPersistence(t *testing.T) { f, err := os.CreateTemp("", "TestSatelLastSeenFilteringWithPersistence") assert.NoError(t, err) tempFileName := f.Name() assert.NoError(t, f.Close()) defer os.Remove(f.Name()) testEvents := make(chan satel.Event) receivedEvents := make([]satel.Event, 0) wg := sync.WaitGroup{} fakeLog := log.New(io.Discard, "", log.Ltime) ds := MakeDataStore(fakeLog, tempFileName) wg.Add(1) go func() { defer wg.Done() for e := range FilterByLastSeen(testEvents, &wg, &ds, fakeLog) { receivedEvents = append(receivedEvents, e) } }() testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 2, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, false) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 2, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, false) close(testEvents) wg.Wait() assert.Len(t, receivedEvents, 3) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 1, true)) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 2, true)) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 1, false)) testEvents = make(chan satel.Event) receivedEvents = make([]satel.Event, 0) ds = MakeDataStore(fakeLog, tempFileName) wg.Add(1) go func() { defer wg.Done() for e := range FilterByLastSeen(testEvents, &wg, &ds, fakeLog) { receivedEvents = append(receivedEvents, e) } }() receivedEvents = make([]satel.Event, 0) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, false) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, false) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 1, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 2, true) testEvents <- makeTestSatelEvent(satel.ArmedPartition, 2, true) close(testEvents) wg.Wait() assert.Len(t, receivedEvents, 1) assert.Contains(t, receivedEvents, makeTestSatelEvent(satel.ArmedPartition, 1, true)) } type MockSleeper struct { ch *chan<- interface{} callCount int } func (self *MockSleeper) Sleep(ch chan<- interface{}) { if self.ch == nil { self.ch = &ch } self.callCount += 1 } func TestThrottle(t *testing.T) { testEvents := make(chan GenericMessage) receivedEvents := make([]GenericMessage, 0) wg := sync.WaitGroup{} fakeLog := log.New(io.Discard, "", log.Ltime) mockSleeper := MockSleeper{nil, 0} var ( tplMessageTest1 = satel.BasicEventElement{Type: satel.ArmedPartition, Index: 1, Value: true} tplMessageTest2 = satel.BasicEventElement{Type: satel.ZoneViolation, Index: 2, Value: true} tplMessageTest3 = satel.BasicEventElement{Type: satel.ArmedPartition, Index: 1, Value: false} tplMessageTest4 = satel.BasicEventElement{Type: satel.ZoneViolation, Index: 2, Value: false} ) wg.Add(1) go func() { defer wg.Done() for e := range Throttle(testEvents, &wg, &mockSleeper, fakeLog) { receivedEvents = append(receivedEvents, e) } }() testEvents <- GenericMessage{[]satel.BasicEventElement{tplMessageTest1}} testEvents <- GenericMessage{[]satel.BasicEventElement{tplMessageTest2}} testEvents <- GenericMessage{[]satel.BasicEventElement{tplMessageTest3}} *mockSleeper.ch <- nil testEvents <- GenericMessage{[]satel.BasicEventElement{tplMessageTest4}} close(testEvents) wg.Wait() assert.Equal(t, 2, mockSleeper.callCount) assert.Len(t, receivedEvents, 2) assert.Contains(t, receivedEvents[0].Messages, tplMessageTest2) assert.Contains(t, receivedEvents[0].Messages, tplMessageTest3) assert.Len(t, receivedEvents[0].Messages, 2) assert.Contains(t, receivedEvents[1].Messages, tplMessageTest4) assert.Len(t, receivedEvents[1].Messages, 1) } func makeMassiveEvent(element satel.BasicEventElement, numElements int) GenericMessage { retval := GenericMessage{make([]satel.BasicEventElement, 0)} for i := 0; i < numElements; i++ { retval.Messages = append(retval.Messages, element) } return retval } func TestThrottle_ManyMessagesInOneEvent(t *testing.T) { testEvents := make(chan GenericMessage) receivedEvents := make([]GenericMessage, 0) wg := sync.WaitGroup{} fakeLog := log.New(io.Discard, "", log.Ltime) mockSleeper := MockSleeper{nil, 0} var ( tplMessageTest1 = satel.BasicEventElement{Type: satel.ArmedPartition, Index: 1, Value: true} tplMessageTest2 = satel.BasicEventElement{Type: satel.ZoneViolation, Index: 2, Value: true} tplMessageTest3 = satel.BasicEventElement{Type: satel.ArmedPartition, Index: 1, Value: false} tplMessageTest4 = satel.BasicEventElement{Type: satel.ZoneViolation, Index: 2, Value: false} ) wg.Add(1) go func() { defer wg.Done() for e := range Throttle(testEvents, &wg, &mockSleeper, fakeLog) { receivedEvents = append(receivedEvents, e) } }() testEvents <- makeMassiveEvent(tplMessageTest1, 100) testEvents <- makeMassiveEvent(tplMessageTest2, 100) testEvents <- makeMassiveEvent(tplMessageTest3, 100) *mockSleeper.ch <- nil testEvents <- makeMassiveEvent(tplMessageTest4, 100) close(testEvents) wg.Wait() assert.Equal(t, 2, mockSleeper.callCount) assert.Len(t, receivedEvents, 2) assert.Contains(t, receivedEvents[0].Messages, tplMessageTest2) assert.Contains(t, receivedEvents[0].Messages, tplMessageTest3) assert.Len(t, receivedEvents[0].Messages, 2) assert.Contains(t, receivedEvents[1].Messages, tplMessageTest4) assert.Len(t, receivedEvents[1].Messages, 1) } type SyncMockFilter struct { SyncFilterImpl collected []GenericMessage } func (self *SyncMockFilter) Call(msg GenericMessage) { self.collected = append(self.collected, msg) self.CallNext(msg) } func TestSyncCollect(t *testing.T) { testEvents := make(chan GenericMessage) wg := sync.WaitGroup{} tested := CollectFromChannel{} mock := &SyncMockFilter{} mock2 := &SyncMockFilter{} tested.Then(mock).Then(mock2) tested.Collect(testEvents, &wg) testEvents <- makeGenericMessage(satel.ArmedPartition, 1, true) testEvents <- makeGenericMessage(satel.DoorOpened, 2, true) testEvents <- makeGenericMessage(satel.PartitionAlarm, 3, true) testEvents <- makeGenericMessage(satel.PartitionFireAlarm, 4, true) testEvents <- makeGenericMessage(satel.TroublePart1, 5, true) testEvents <- makeGenericMessage(satel.ZoneTamper, 6, true) close(testEvents) wg.Wait() assert.Len(t, mock.collected, 6) assert.Contains(t, mock.collected, makeGenericMessage(satel.ArmedPartition, 1, true)) assert.Contains(t, mock.collected, makeGenericMessage(satel.DoorOpened, 2, true)) assert.Contains(t, mock.collected, makeGenericMessage(satel.PartitionAlarm, 3, true)) assert.Contains(t, mock.collected, makeGenericMessage(satel.PartitionFireAlarm, 4, true)) assert.Contains(t, mock.collected, makeGenericMessage(satel.TroublePart1, 5, true)) assert.Contains(t, mock.collected, makeGenericMessage(satel.ZoneTamper, 6, true)) assert.Len(t, mock2.collected, 6) assert.Contains(t, mock2.collected, makeGenericMessage(satel.ArmedPartition, 1, true)) assert.Contains(t, mock2.collected, makeGenericMessage(satel.DoorOpened, 2, true)) assert.Contains(t, mock2.collected, makeGenericMessage(satel.PartitionAlarm, 3, true)) assert.Contains(t, mock2.collected, makeGenericMessage(satel.PartitionFireAlarm, 4, true)) assert.Contains(t, mock2.collected, makeGenericMessage(satel.TroublePart1, 5, true)) assert.Contains(t, mock2.collected, makeGenericMessage(satel.ZoneTamper, 6, true)) }