Files
notify/review_fix_regression_test.go
T

198 lines
5.7 KiB
Go
Raw Normal View History

package notify
import (
"b612.me/stario"
"context"
"errors"
netpkg "net"
"sync/atomic"
"testing"
"time"
)
type gatedDedicatedReadConn struct {
netpkg.Conn
total int
read int
reached chan struct{}
release chan struct{}
closed bool
}
func (c *gatedDedicatedReadConn) Read(p []byte) (int, error) {
n, err := c.Conn.Read(p)
c.read += n
if !c.closed && c.read >= c.total {
c.closed = true
close(c.reached)
<-c.release
}
return n, err
}
func TestControlMessageErrorPreservesTransportDetachedSentinel(t *testing.T) {
for name, decode := range map[string]func(string) error{
"bulk": bulkControlMessageError,
"stream": streamControlMessageError,
} {
t.Run(name, func(t *testing.T) {
err := decode("transport detached: stale transport generation=7")
if !errors.Is(err, errTransportDetached) {
t.Fatalf("decoded error = %v, want transport detached sentinel", err)
}
})
}
}
func TestSendDedicatedBulkAttachRequestRejectsNilDialResult(t *testing.T) {
client := &ClientCommon{}
bulk := newBulkHandle(context.Background(), newBulkRuntime("cblk"), clientFileScope(), BulkOpenRequest{
BulkID: "nil-dial",
}, 0, nil, nil, 0, nil, nil, nil, nil, nil)
if _, err := client.sendDedicatedBulkAttachRequest(context.Background(), nil, bulk); !errors.Is(err, errTransportDetached) {
t.Fatalf("nil dial result error = %v, want transport detached", err)
}
}
func TestDedicatedSidecarCloseWaitsForAttachment(t *testing.T) {
left, right := netpkg.Pipe()
defer left.Close()
defer right.Close()
sidecar := newBulkDedicatedSidecar(left, 1)
entered := make(chan struct{})
release := make(chan struct{})
attachDone := make(chan error, 1)
go func() {
attachDone <- sidecar.withConn(func(conn netpkg.Conn) error {
close(entered)
<-release
return nil
})
}()
select {
case <-entered:
case <-time.After(time.Second):
t.Fatal("sidecar attachment did not start")
}
closeDone := make(chan struct{})
go func() {
sidecar.close()
close(closeDone)
}()
select {
case <-closeDone:
t.Fatal("sidecar closed while attachment still held")
case <-time.After(25 * time.Millisecond):
}
close(release)
if err := <-attachDone; err != nil {
t.Fatalf("sidecar attachment error = %v", err)
}
select {
case <-closeDone:
case <-time.After(time.Second):
t.Fatal("sidecar close did not finish after attachment release")
}
}
func TestBulkAcceptDispatchRejectsResetBeforeHandler(t *testing.T) {
bulk := newBulkHandle(context.Background(), newBulkRuntime("sblk"), serverFileDomain+":test", BulkOpenRequest{
BulkID: "stale-dispatch",
}, 0, nil, nil, 0, nil, nil, nil, nil, nil)
bulk.markReset(errTransportDetached)
var calls atomic.Int32
err := dispatchBulkAccept(func(BulkAcceptInfo) error {
calls.Add(1)
return nil
}, bulk, BulkAcceptInfo{Bulk: bulk})
if !errors.Is(err, errTransportDetached) {
t.Fatalf("stale dispatch error = %v, want transport detached", err)
}
if got := calls.Load(); got != 0 {
t.Fatalf("stale dispatch handler calls = %d, want 0", got)
}
}
func TestClientDedicatedSidecarDropsFrameAfterTransportReattach(t *testing.T) {
client := NewClient().(*ClientCommon)
UseLegacySecurityClient(client)
stopCtx, stopFn := context.WithCancel(context.Background())
defer stopFn()
queue := stario.NewQueueCtx(stopCtx, 4, ^uint32(0))
oldLeft, oldRight := netpkg.Pipe()
defer oldRight.Close()
epoch := client.beginClientSessionEpoch()
client.setClientSessionRuntime(newClientSessionRuntime(oldLeft, stopCtx, stopFn, queue, epoch))
client.markSessionStarted()
defer client.markSessionStopped("test done", nil)
oldRoute := client.clientSessionRouteSnapshot()
sidecarLeft, sidecarRight := netpkg.Pipe()
defer sidecarRight.Close()
payload, err := client.encodeDedicatedBulkBatchPayload(101, []bulkDedicatedSendRequest{{
Type: bulkFastPayloadTypeData,
Seq: 1,
Payload: []byte("stale"),
}})
if err != nil {
t.Fatalf("encode stale sidecar payload: %v", err)
}
frameRead := make(chan struct{})
releaseFrame := make(chan struct{})
readConn := &gatedDedicatedReadConn{
Conn: sidecarLeft,
total: bulkDedicatedRecordHeaderLen + len(payload),
reached: frameRead,
release: releaseFrame,
}
sidecar := newBulkDedicatedSidecar(readConn, 1)
loopDone := make(chan struct{})
go func() {
client.readDedicatedSidecarLoopAtRoute(sidecar, oldRoute)
close(loopDone)
}()
writeDone := make(chan error, 1)
go func() { writeDone <- writeBulkDedicatedRecord(sidecarRight, payload) }()
select {
case <-frameRead:
case <-time.After(time.Second):
t.Fatal("sidecar loop did not read the complete frame")
}
newLeft, newRight := netpkg.Pipe()
defer newRight.Close()
if err := client.attachClientSessionTransport(newLeft); err != nil {
t.Fatalf("attach replacement client transport: %v", err)
}
runtime := client.getBulkRuntime()
bulk := newBulkHandle(stopCtx, runtime, clientFileScope(), BulkOpenRequest{
BulkID: "stale-sidecar-frame",
DataID: 101,
Range: BulkRange{Length: 32},
}, oldRoute.epoch, nil, nil, 0, nil, nil, nil, nil, nil)
bulk.setClientSnapshotOwner(client)
bulk.setClientSessionRoute(oldRoute)
if err := runtime.registerInbound(clientFileScope(), bulk); err != nil {
t.Fatalf("register stale-route bulk: %v", err)
}
defer bulk.markReset(errors.New("test cleanup"))
close(releaseFrame)
if err := <-writeDone; err != nil {
t.Fatalf("write stale sidecar payload: %v", err)
}
select {
case <-loopDone:
case <-time.After(time.Second):
t.Fatal("stale-route sidecar loop did not stop after reattach")
}
bulk.mu.Lock()
defer bulk.mu.Unlock()
if len(bulk.readQueue) != 0 || len(bulk.readBuf.data) != 0 || bulk.resetErr != nil {
t.Fatalf("stale sidecar frame mutated bulk: queued=%d buffered=%d reset=%v", len(bulk.readQueue), len(bulk.readBuf.data), bulk.resetErr)
}
}