|
|
@@ -2,10 +2,8 @@ package foodgroup
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
- "log/slog"
|
|
|
"net"
|
|
|
"net/netip"
|
|
|
- "sync"
|
|
|
"testing"
|
|
|
"time"
|
|
|
|
|
|
@@ -802,7 +800,6 @@ func TestICBMService_ChannelMsgToHost(t *testing.T) {
|
|
|
offlineMessageSaver: offlineMessageManager,
|
|
|
sessionRetriever: sessionRetriever,
|
|
|
timeNow: tc.timeNow,
|
|
|
- convoTracker: newConvoTracker(),
|
|
|
}
|
|
|
|
|
|
outputSNAC, err := svc.ChannelMsgToHost(context.Background(), tc.senderSession, tc.inputSNAC.Frame,
|
|
|
@@ -942,8 +939,6 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
name string
|
|
|
// senderScreenName is the session of the user sending the EvilRequest
|
|
|
senderSession *state.Session
|
|
|
- // msgsReceived is the # of messages received from the warned user
|
|
|
- msgsReceived int
|
|
|
// inputSNAC is the SNAC sent by the sender client
|
|
|
inputSNAC wire.SNACMessage
|
|
|
// expectOutput is the SNAC sent from the server to client
|
|
|
@@ -951,13 +946,10 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
// mockParams is the list of params sent to mocks that satisfy this
|
|
|
// method's dependencies
|
|
|
mockParams mockParams
|
|
|
- // waitForWarnMsg indicates whether to wait for session warn signal
|
|
|
- waitForWarnMsg bool
|
|
|
}{
|
|
|
{
|
|
|
name: "transmit anonymous warning from sender to recipient",
|
|
|
senderSession: newTestSession("sender-screen-name"),
|
|
|
- msgsReceived: 1,
|
|
|
inputSNAC: wire.SNACMessage{
|
|
|
Frame: wire.SNACFrame{
|
|
|
RequestID: 1234,
|
|
|
@@ -1026,12 +1018,10 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
- waitForWarnMsg: true,
|
|
|
},
|
|
|
{
|
|
|
name: "transmit non-anonymous warning from sender to recipient",
|
|
|
senderSession: newTestSession("sender-screen-name", sessOptWarning(110)),
|
|
|
- msgsReceived: 1,
|
|
|
inputSNAC: wire.SNACMessage{
|
|
|
Frame: wire.SNACFrame{
|
|
|
RequestID: 1234,
|
|
|
@@ -1108,12 +1098,10 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
- waitForWarnMsg: true,
|
|
|
},
|
|
|
{
|
|
|
name: "don't transmit non-anonymous warning from sender to recipient because sender has blocked recipient",
|
|
|
senderSession: newTestSession("sender-screen-name"),
|
|
|
- msgsReceived: 1,
|
|
|
inputSNAC: wire.SNACMessage{
|
|
|
Frame: wire.SNACFrame{
|
|
|
RequestID: 1234,
|
|
|
@@ -1154,7 +1142,6 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
{
|
|
|
name: "don't transmit non-anonymous warning from sender to recipient because recipient has blocked sender",
|
|
|
senderSession: newTestSession("sender-screen-name"),
|
|
|
- msgsReceived: 1,
|
|
|
inputSNAC: wire.SNACMessage{
|
|
|
Frame: wire.SNACFrame{
|
|
|
RequestID: 1234,
|
|
|
@@ -1195,7 +1182,6 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
{
|
|
|
name: "don't let users warn themselves",
|
|
|
senderSession: newTestSession("sender-screen-name"),
|
|
|
- msgsReceived: 1,
|
|
|
inputSNAC: wire.SNACMessage{
|
|
|
Frame: wire.SNACFrame{
|
|
|
RequestID: 1234,
|
|
|
@@ -1219,7 +1205,6 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
{
|
|
|
name: "don't transmit non-anonymous warning from sender to recipient because recipient is offline",
|
|
|
senderSession: newTestSession("sender-screen-name"),
|
|
|
- msgsReceived: 1,
|
|
|
mockParams: mockParams{
|
|
|
relationshipFetcherParams: relationshipFetcherParams{
|
|
|
relationshipParams: relationshipParams{
|
|
|
@@ -1268,7 +1253,6 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
{
|
|
|
name: "don't transmit anonymous warning from sender to recipient because recipient is offline",
|
|
|
senderSession: newTestSession("sender-screen-name"),
|
|
|
- msgsReceived: 1,
|
|
|
mockParams: mockParams{
|
|
|
relationshipFetcherParams: relationshipFetcherParams{
|
|
|
relationshipParams: relationshipParams{
|
|
|
@@ -1354,38 +1338,18 @@ func TestICBMService_EvilRequest(t *testing.T) {
|
|
|
messageRelayer: messageRelayer,
|
|
|
offlineMessageSaver: offlineMessageManager,
|
|
|
sessionRetriever: sessionRetriever,
|
|
|
- convoTracker: newConvoTracker(),
|
|
|
- snacRateLimits: wire.DefaultSNACRateLimits(),
|
|
|
}
|
|
|
|
|
|
- for i := 0; i < tc.msgsReceived; i++ {
|
|
|
- svc.convoTracker.trackConvo(time.Now(),
|
|
|
- state.NewIdentScreenName(tc.inputSNAC.Body.(wire.SNAC_0x04_0x08_ICBMEvilRequest).ScreenName),
|
|
|
- tc.senderSession.IdentScreenName())
|
|
|
- }
|
|
|
-
|
|
|
- var wg sync.WaitGroup
|
|
|
- if tc.waitForWarnMsg {
|
|
|
- wg.Add(1)
|
|
|
- go func() {
|
|
|
- defer wg.Done()
|
|
|
- for _, sess := range tc.mockParams.sessionRetrieverParams.retrieveSessionParams {
|
|
|
- <-sess.result.WarningCh()
|
|
|
- }
|
|
|
- }()
|
|
|
- }
|
|
|
outputSNAC, err := svc.EvilRequest(context.Background(), tc.senderSession, tc.inputSNAC.Frame,
|
|
|
tc.inputSNAC.Body.(wire.SNAC_0x04_0x08_ICBMEvilRequest))
|
|
|
assert.NoError(t, err)
|
|
|
assert.Equal(t, tc.expectOutput, outputSNAC)
|
|
|
-
|
|
|
- wg.Wait()
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func TestICBMService_ParameterQuery(t *testing.T) {
|
|
|
- svc := NewICBMService(nil, nil, nil, nil, nil, wire.DefaultSNACRateLimits(), slog.Default())
|
|
|
+ svc := NewICBMService(nil, nil, nil, nil, nil, wire.DefaultSNACRateLimits())
|
|
|
|
|
|
have := svc.ParameterQuery(nil, wire.SNACFrame{RequestID: 1234})
|
|
|
want := wire.SNACMessage{
|
|
|
@@ -1437,333 +1401,8 @@ func TestICBMService_ClientErr(t *testing.T) {
|
|
|
messageRelayer.EXPECT().
|
|
|
RelayToScreenName(mock.Anything, state.NewIdentScreenName("recipientScreenName"), expect)
|
|
|
|
|
|
- svc := NewICBMService(nil, messageRelayer, nil, nil, nil, wire.DefaultSNACRateLimits(), slog.Default())
|
|
|
+ svc := NewICBMService(nil, messageRelayer, nil, nil, nil, wire.DefaultSNACRateLimits())
|
|
|
|
|
|
err := svc.ClientErr(context.Background(), sess, wire.SNACFrame{RequestID: 1234}, inBody)
|
|
|
assert.NoError(t, err)
|
|
|
}
|
|
|
-
|
|
|
-func TestRingBuffer(t *testing.T) {
|
|
|
- t.Run("new ringBuffer should have zero values", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{}
|
|
|
-
|
|
|
- // Test val() on empty ringBuffer - should return zero time
|
|
|
- result := rb.val()
|
|
|
- zeroTime := time.Time{}
|
|
|
- assert.Equal(t, zeroTime, result)
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("val() should return current time", func(t *testing.T) {
|
|
|
- now := time.Now()
|
|
|
- rb := &ringBuffer{
|
|
|
- cur: 1,
|
|
|
- vals: [3]time.Time{
|
|
|
- now.Add(-2 * time.Hour),
|
|
|
- now.Add(-1 * time.Hour),
|
|
|
- now,
|
|
|
- },
|
|
|
- }
|
|
|
-
|
|
|
- result := rb.val()
|
|
|
- assert.Equal(t, rb.vals[1], result)
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("set() should store time and advance cursor", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{cur: 0}
|
|
|
- newTime := time.Now()
|
|
|
-
|
|
|
- // Set the time
|
|
|
- rb.set(newTime)
|
|
|
-
|
|
|
- // After set, cursor should advance to position 1
|
|
|
- // We can verify this by setting another time and checking that it's stored at position 1
|
|
|
- secondTime := time.Now().Add(time.Hour)
|
|
|
- rb.set(secondTime)
|
|
|
-
|
|
|
- // Now cursor should be at position 2, so val() should return the time at position 2
|
|
|
- // But since we only set 2 times, position 2 should still be the zero time
|
|
|
- zeroTime := time.Time{}
|
|
|
- assert.Equal(t, zeroTime, rb.val())
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("set() should wrap around after reaching end of array", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{cur: 2}
|
|
|
- newTime := time.Now()
|
|
|
-
|
|
|
- // Set the time at position 2
|
|
|
- rb.set(newTime)
|
|
|
-
|
|
|
- // Cursor should wrap around to position 0
|
|
|
- // We can verify this by setting another time and checking behavior
|
|
|
- rb.set(time.Now().Add(time.Hour))
|
|
|
-
|
|
|
- // Now cursor should be at position 1, so val() should return the time at position 1
|
|
|
- // But since we only set 2 times, position 1 should still be the zero time
|
|
|
- zeroTime := time.Time{}
|
|
|
- assert.Equal(t, zeroTime, rb.val())
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("set() should handle multiple insertions correctly", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{}
|
|
|
-
|
|
|
- // Insert 3 times
|
|
|
- time1 := time.Now()
|
|
|
- time2 := time.Now().Add(time.Hour)
|
|
|
- time3 := time.Now().Add(2 * time.Hour)
|
|
|
-
|
|
|
- rb.set(time1)
|
|
|
- rb.set(time2)
|
|
|
- rb.set(time3)
|
|
|
-
|
|
|
- // After 3 insertions, cursor should be at position 0
|
|
|
- // So val() should return the time at position 0
|
|
|
- // This should be time1 since it was the first time set
|
|
|
- assert.Equal(t, time1, rb.val())
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("set() should overwrite existing values in order", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{}
|
|
|
-
|
|
|
- // Set 3 times to fill the buffer
|
|
|
- rb.set(time.Now())
|
|
|
- rb.set(time.Now().Add(time.Hour))
|
|
|
- rb.set(time.Now().Add(2 * time.Hour))
|
|
|
-
|
|
|
- // After 3 sets, cursor is at position 0, val() returns first time
|
|
|
- firstTime := rb.val()
|
|
|
- assert.False(t, firstTime.IsZero())
|
|
|
-
|
|
|
- // Set a 4th time - should overwrite position 0
|
|
|
- fourthTime := time.Now().Add(3 * time.Hour)
|
|
|
- rb.set(fourthTime)
|
|
|
-
|
|
|
- // Now cursor is at position 1, val() returns second time
|
|
|
- secondTime := rb.val()
|
|
|
- assert.False(t, secondTime.IsZero())
|
|
|
-
|
|
|
- // Set a 5th time - should overwrite position 1
|
|
|
- fifthTime := time.Now().Add(4 * time.Hour)
|
|
|
- rb.set(fifthTime)
|
|
|
-
|
|
|
- // Now cursor is at position 2, val() returns third time
|
|
|
- thirdTime := rb.val()
|
|
|
- assert.False(t, thirdTime.IsZero())
|
|
|
-
|
|
|
- // Set a 6th time - should overwrite position 2
|
|
|
- sixthTime := time.Now().Add(5 * time.Hour)
|
|
|
- rb.set(sixthTime)
|
|
|
-
|
|
|
- // Now cursor wraps around to position 0, val() returns fourth time
|
|
|
- assert.Equal(t, fourthTime, rb.val())
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("val() should return correct time after multiple operations", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{}
|
|
|
-
|
|
|
- // Insert times and verify val() returns correct current time
|
|
|
- time1 := time.Now()
|
|
|
- time2 := time.Now().Add(time.Hour)
|
|
|
-
|
|
|
- rb.set(time1)
|
|
|
- rb.set(time2)
|
|
|
-
|
|
|
- // After 2 sets, cursor is at position 2
|
|
|
- // So val() should return the time at position 2
|
|
|
- // But since we only set 2 times, position 2 should still be the zero time
|
|
|
- zeroTime := time.Time{}
|
|
|
- assert.Equal(t, zeroTime, rb.val())
|
|
|
-
|
|
|
- // Set one more to wrap around
|
|
|
- time3 := time.Now().Add(2 * time.Hour)
|
|
|
- rb.set(time3)
|
|
|
-
|
|
|
- // Now cursor is at position 0, so val() should return the time at position 0
|
|
|
- // This should be time1
|
|
|
- assert.Equal(t, time1, rb.val())
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("ringBuffer should maintain circular behavior over many operations", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{}
|
|
|
-
|
|
|
- // Perform many operations to test circular behavior
|
|
|
- for i := 0; i < 10; i++ {
|
|
|
- rb.set(time.Now().Add(time.Duration(i) * time.Hour))
|
|
|
- }
|
|
|
-
|
|
|
- // After 10 operations, cursor should be at position 1 (10 % 3 = 1)
|
|
|
- // So val() should return the time at position 1
|
|
|
- // This should be the 8th time set (at position 1)
|
|
|
- // We can't compare exact times since they're set in a loop, so just verify it's not zero
|
|
|
- assert.False(t, rb.val().IsZero())
|
|
|
-
|
|
|
- // Set one more to advance cursor to position 2
|
|
|
- rb.set(time.Now().Add(10 * time.Hour))
|
|
|
-
|
|
|
- // Now cursor is at position 2, so val() should return the time at position 2
|
|
|
- // This should be the 9th time set (at position 2)
|
|
|
- assert.False(t, rb.val().IsZero())
|
|
|
-
|
|
|
- // Set one more to wrap around to position 0
|
|
|
- rb.set(time.Now().Add(11 * time.Hour))
|
|
|
-
|
|
|
- // Now cursor is at position 0, so val() should return the time at position 0
|
|
|
- // This should be the 10th time set (at position 0)
|
|
|
- assert.False(t, rb.val().IsZero())
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("ringBuffer should cycle through all positions correctly", func(t *testing.T) {
|
|
|
- rb := &ringBuffer{}
|
|
|
-
|
|
|
- // Test cycling through all 3 positions
|
|
|
- times := []time.Time{
|
|
|
- time.Now(),
|
|
|
- time.Now().Add(time.Hour),
|
|
|
- time.Now().Add(2 * time.Hour),
|
|
|
- }
|
|
|
-
|
|
|
- // Set all 3 times
|
|
|
- for _, t := range times {
|
|
|
- rb.set(t)
|
|
|
- }
|
|
|
-
|
|
|
- // After 3 sets, cursor should be at position 0
|
|
|
- // So val() should return the time at position 0
|
|
|
- assert.Equal(t, times[0], rb.val())
|
|
|
-
|
|
|
- // Set one more to advance cursor to position 1
|
|
|
- nextTime := time.Now().Add(3 * time.Hour)
|
|
|
- rb.set(nextTime)
|
|
|
-
|
|
|
- // Now cursor is at position 1, so val() should return the time at position 1
|
|
|
- // This should be the second time since it was stored at position 1
|
|
|
- assert.Equal(t, times[1], rb.val())
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-func TestConvoTracker(t *testing.T) {
|
|
|
- ct := newConvoTracker()
|
|
|
- sender := state.NewIdentScreenName("sender")
|
|
|
- recip := state.NewIdentScreenName("recipient")
|
|
|
- now := time.Now()
|
|
|
-
|
|
|
- // can't warn until a message is sent
|
|
|
- assert.False(t, ct.trackWarn(now, recip, sender))
|
|
|
-
|
|
|
- // can warn 1st time
|
|
|
- ct.trackConvo(now, sender, recip)
|
|
|
- assert.True(t, ct.trackWarn(now, recip, sender))
|
|
|
-
|
|
|
- // can't warn 2nd time until 2nd message is sent
|
|
|
- assert.False(t, ct.trackWarn(now, recip, sender))
|
|
|
-
|
|
|
- // can warn 2nd time
|
|
|
- now = now.Add(1 * time.Second)
|
|
|
- ct.trackConvo(now, sender, recip)
|
|
|
- assert.True(t, ct.trackWarn(now, recip, sender))
|
|
|
-
|
|
|
- // can't warn 3rd time until 3rd message is sent
|
|
|
- assert.False(t, ct.trackWarn(now, recip, sender))
|
|
|
-
|
|
|
- // can warn 3rd time
|
|
|
- now = now.Add(1 * time.Second)
|
|
|
- ct.trackConvo(now, sender, recip)
|
|
|
- assert.True(t, ct.trackWarn(now, recip, sender))
|
|
|
-
|
|
|
- // can't warn 4th time
|
|
|
- now = now.Add(1 * time.Second)
|
|
|
- ct.trackConvo(now, sender, recip)
|
|
|
- assert.False(t, ct.trackWarn(now, recip, sender))
|
|
|
-
|
|
|
- // let an hour pass, we should be able to warn again
|
|
|
- now = now.Add(1 * time.Hour)
|
|
|
- ct.trackConvo(now, sender, recip)
|
|
|
- assert.True(t, ct.trackWarn(now, recip, sender))
|
|
|
-}
|
|
|
-
|
|
|
-func TestICBMService_DecayWarnLevel(t *testing.T) {
|
|
|
-
|
|
|
- t.Run("happy path", func(t *testing.T) {
|
|
|
-
|
|
|
- sess := newTestSession("screen-name")
|
|
|
- warnCh := make(chan uint16)
|
|
|
-
|
|
|
- mockBuddyBroadcaster := newMockbuddyBroadcaster(t)
|
|
|
- mockBuddyBroadcaster.EXPECT().
|
|
|
- BroadcastBuddyArrived(mock.Anything, matchSession(sess.IdentScreenName())).
|
|
|
- Run(func(ctx context.Context, sess *state.Session) {
|
|
|
- warnCh <- sess.Warning()
|
|
|
- }).Return(nil)
|
|
|
-
|
|
|
- svc := ICBMService{
|
|
|
- buddyBroadcaster: mockBuddyBroadcaster,
|
|
|
- logger: slog.Default(),
|
|
|
- interval: 1 * time.Millisecond,
|
|
|
- snacRateLimits: wire.DefaultSNACRateLimits(),
|
|
|
- }
|
|
|
-
|
|
|
- ctx, cancel := context.WithCancel(context.Background())
|
|
|
- defer cancel()
|
|
|
-
|
|
|
- var wg sync.WaitGroup
|
|
|
- wg.Add(1)
|
|
|
- go func() {
|
|
|
- defer wg.Done()
|
|
|
- svc.DecayWarnLevel(ctx, sess)
|
|
|
- }()
|
|
|
-
|
|
|
- sess.IncrementWarning(100, 3)
|
|
|
- sess.NotifyWarning(ctx)
|
|
|
-
|
|
|
- assert.Equal(t, uint16(50), <-warnCh)
|
|
|
- assert.Equal(t, uint16(0), <-warnCh)
|
|
|
-
|
|
|
- sess.IncrementWarning(50, 3)
|
|
|
- sess.NotifyWarning(ctx)
|
|
|
- sess.IncrementWarning(50, 3)
|
|
|
- sess.NotifyWarning(ctx)
|
|
|
-
|
|
|
- assert.Equal(t, uint16(50), <-warnCh)
|
|
|
- assert.Equal(t, uint16(0), <-warnCh)
|
|
|
-
|
|
|
- cancel()
|
|
|
- wg.Wait()
|
|
|
- })
|
|
|
-
|
|
|
- t.Run("3% burn down clamps to 0", func(t *testing.T) {
|
|
|
-
|
|
|
- sess := newTestSession("screen-name")
|
|
|
- warnCh := make(chan uint16)
|
|
|
-
|
|
|
- mockBuddyBroadcaster := newMockbuddyBroadcaster(t)
|
|
|
- mockBuddyBroadcaster.EXPECT().
|
|
|
- BroadcastBuddyArrived(mock.Anything, matchSession(sess.IdentScreenName())).
|
|
|
- Run(func(ctx context.Context, sess *state.Session) {
|
|
|
- warnCh <- sess.Warning()
|
|
|
- }).Return(nil)
|
|
|
-
|
|
|
- svc := ICBMService{
|
|
|
- buddyBroadcaster: mockBuddyBroadcaster,
|
|
|
- logger: slog.Default(),
|
|
|
- interval: 1 * time.Millisecond,
|
|
|
- snacRateLimits: wire.DefaultSNACRateLimits(),
|
|
|
- }
|
|
|
-
|
|
|
- ctx, cancel := context.WithCancel(context.Background())
|
|
|
- defer cancel()
|
|
|
-
|
|
|
- var wg sync.WaitGroup
|
|
|
- wg.Add(1)
|
|
|
- go func() {
|
|
|
- defer wg.Done()
|
|
|
- svc.DecayWarnLevel(ctx, sess)
|
|
|
- }()
|
|
|
-
|
|
|
- sess.IncrementWarning(30, 3)
|
|
|
- sess.NotifyWarning(ctx)
|
|
|
-
|
|
|
- assert.Equal(t, uint16(0), <-warnCh)
|
|
|
-
|
|
|
- cancel()
|
|
|
- wg.Wait()
|
|
|
- })
|
|
|
-}
|