// SPDX-FileCopyrightText: 2026 Echolot contributors // SPDX-License-Identifier: GPL-3.0-or-later package session import "testing" func TestTrainBufferKeepsHeadAndDeclaresTruncation(t *testing.T) { s := &Session{} over := trainCap + 100 for i := 0; i < over; i++ { s.RecordTrainPacket(7, TrainEntry{Seq: uint32(i), Size: 64}) } tr, ok := s.TrainView(7) if !ok { t.Fatal("train not found") } if tr.Received != over { t.Fatalf("Received = %d, want %d — the count must include unbuffered packets", tr.Received, over) } if len(tr.Entries) != trainCap { t.Fatalf("buffered %d entries, want the cap %d", len(tr.Entries), trainCap) } if !tr.Truncated { t.Fatal("overflow must be declared, not silent") } // The head must survive: it is what a ring buffer would have lost. if tr.Entries[0].Seq != 0 || tr.Entries[trainCap-1].Seq != trainCap-1 { t.Fatalf("buffer kept seqs %d..%d, want the head 0..%d", tr.Entries[0].Seq, tr.Entries[trainCap-1].Seq, trainCap-1) } } func TestTrainEvictionDropsOldestTrain(t *testing.T) { s := &Session{} for id := uint32(0); id < maxTrains+2; id++ { s.RecordTrainPacket(id, TrainEntry{Seq: 1}) } if _, ok := s.TrainView(0); ok { t.Fatal("oldest train should have been evicted") } if _, ok := s.TrainView(1); ok { t.Fatal("second-oldest train should have been evicted") } if _, ok := s.TrainView(maxTrains + 1); !ok { t.Fatal("newest train missing") } if got := len(s.Trains()); got != maxTrains { t.Fatalf("holding %d trains, want %d", got, maxTrains) } } func TestTrainViewReturnsACopy(t *testing.T) { s := &Session{} s.RecordTrainPacket(3, TrainEntry{Seq: 10}) tr, _ := s.TrainView(3) tr.Entries[0].Seq = 99 again, _ := s.TrainView(3) if again.Entries[0].Seq != 10 { t.Fatal("TrainView leaked the internal slice") } }