internal/mtprotoedge/seq_validate_test.go
Rama main · réplica de lectura del repositorio original.
package mtprotoedge
import (
"math/rand"
"testing"
)
// referenceValidateSeq is validateSeq's old full-scan semantics (without the
// high-water fast path), used as the behavioral baseline for randomized
// differential testing: the fast path may only accept the subset that a full scan
// would also necessarily accept.
func referenceValidateSeq(cs *connState, msgID int64, seqNo int32, content bool) int {
if !content {
return 0
}
for seenMsgID, record := range cs.seen {
if !record.content {
continue
}
if seenMsgID < msgID && record.seqNo >= seqNo {
return badMsgSeqTooLow
}
if seenMsgID > msgID && record.seqNo <= seqNo {
return badMsgSeqTooHigh
}
}
return 0
}
func TestValidateSeqFastPathMatchesFullScan(t *testing.T) {
rng := rand.New(rand.NewSource(20260705))
for round := 0; round < 32; round++ {
fast := newConnState()
ref := newConnState()
for i := 0; i < 2000; i++ {
// A small value range produces out-of-order, duplicate seq and
// too_low/too_high inversions; large i also covers the eviction window.
msgID := int64(rng.Intn(3000) + 1)
seqNo := int32(rng.Intn(600))
content := rng.Intn(4) != 0
if _, ok := fast.seen[msgID]; ok {
continue // the real call chain does not reach validateSeq when seenRecord hits
}
got := fast.validateSeq(msgID, seqNo, content)
want := referenceValidateSeq(ref, msgID, seqNo, content)
if got != want {
t.Fatalf("round %d step %d: validateSeq(msg_id=%d seq=%d content=%v) = %d, want %d",
round, i, msgID, seqNo, content, got, want)
}
if got == 0 {
fast.track(msgID, seqNo, content, msgStateReceived)
ref.track(msgID, seqNo, content, msgStateReceived)
}
}
}
}
func TestValidateSeqOrderedFastPath(t *testing.T) {
cs := newConnState()
// Well-behaved client: msg_id and content seq_no strictly increase, so all should pass.
for i := 0; i < 1000; i++ {
msgID := int64(1000 + i*4)
seqNo := int32(i*2 + 1)
if code := cs.validateSeq(msgID, seqNo, true); code != 0 {
t.Fatalf("ordered message %d rejected with code %d", i, code)
}
cs.track(msgID, seqNo, true, msgStateReceived)
}
// A seq regression must still be rejected (the fast path does not let it through).
if code := cs.validateSeq(1000+1000*4, 3, true); code != badMsgSeqTooLow {
t.Fatalf("seq regression code = %d, want badMsgSeqTooLow", code)
}
// An old msg_id with a new seq must also still be rejected.
if code := cs.validateSeq(500, 5000, true); code != badMsgSeqTooHigh {
t.Fatalf("old msg_id with high seq code = %d, want badMsgSeqTooHigh", code)
}
}