Files
free-turn-proxy/internal/session/wake_test.go

96 lines
2.8 KiB
Go

package session
import (
"context"
"testing"
"time"
"github.com/samosvalishe/free-turn-proxy/internal/config"
)
func newWakeSession(t *testing.T, window time.Duration) *Session {
t.Helper()
s, err := New(&config.Client{}, Deps{
Options: Options{Traffic: true, WakeProbeWindow: window},
})
if err != nil {
t.Fatalf("New: %v", err)
}
return s
}
// Пробуждение при живом канале не должно стоить рецикла: аллокация переживает сон, а её
// пересоздание тянет за собой поход в VK и капчу.
func TestWakeSkipsRecycleWhenTrafficFlows(t *testing.T) {
t.Parallel()
// Окно с запасом: пакет обязан лечь между снимком счётчика и концом окна, а не
// проиграть гранулярности таймера.
s := newWakeSession(t, 2*time.Second)
s.connected.Store(1)
done := make(chan bool, 1)
go func() { done <- s.wakeNeedsRecycle(context.Background()) }()
// Имитируем keepalive туннеля, идущий через relay.
time.Sleep(50 * time.Millisecond)
s.traffic.stats.AddRx(64)
select {
case need := <-done:
if need {
t.Fatal("recycle requested while traffic was flowing")
}
case <-time.After(10 * time.Second):
t.Fatal("wakeNeedsRecycle did not finish")
}
}
func TestWakeRecyclesOnSilence(t *testing.T) {
t.Parallel()
s := newWakeSession(t, 50*time.Millisecond)
s.connected.Store(1)
if !s.wakeNeedsRecycle(context.Background()) {
t.Fatal("silent channel must be recycled")
}
}
// Пробуждение во время подъёма сессии отменило бы перебор реквизитов и решение капчи.
func TestWakeSkipsRecycleWhileConnecting(t *testing.T) {
t.Parallel()
s := newWakeSession(t, 50*time.Millisecond)
start := time.Now()
if s.wakeNeedsRecycle(context.Background()) {
t.Fatal("recycle requested while session was still connecting")
}
if elapsed := time.Since(start); elapsed > 40*time.Millisecond {
t.Fatalf("probe window was waited unnecessarily (%s)", elapsed)
}
}
// Гэп-детектор ядра и пинок с платформы приходят почти одновременно: второй сигнал
// про тот же сон не должен давать второй рецикл.
func TestWakeCollapsesBurst(t *testing.T) {
t.Parallel()
s := newWakeSession(t, 50*time.Millisecond)
s.connected.Store(1)
go s.watchWake(t.Context())
s.Wake()
s.Wake()
select {
case <-s.reconnectCh:
case <-time.After(2 * time.Second):
t.Fatal("no recycle after wake on a silent channel")
}
select {
case <-s.reconnectCh:
t.Fatal("burst produced a second recycle")
case <-time.After(200 * time.Millisecond):
}
}