Testing
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
package ca_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/uopi/goca/proto"
|
||||
"github.com/uopi/goca/testca"
|
||||
)
|
||||
|
||||
// TestGetDisconnectMidRequest covers the "circuit disconnected during GET" path
|
||||
// in conn.go: the server drops the connection while a READ_NOTIFY is in flight,
|
||||
// so the in-flight reply channel is closed by the reconnect loop and the waiter
|
||||
// unblocks with ok=false.
|
||||
func TestGetDisconnectMidRequest(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
cli := newTestClient(t, srv)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// First GET establishes the channel/circuit successfully.
|
||||
if _, err := cli.Get(ctx, "TEST:DOUBLE"); err != nil {
|
||||
t.Fatalf("initial Get: %v", err)
|
||||
}
|
||||
|
||||
// Arm a mid-request disconnect for the next READ_NOTIFY.
|
||||
srv.SetGetFault(testca.GetFaultDisconnect)
|
||||
|
||||
if _, err := cli.Get(ctx, "TEST:DOUBLE"); err == nil {
|
||||
t.Fatal("Get with mid-request disconnect: want error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetCorruptReply covers the "failed to decode GET reply" path: the server
|
||||
// answers READ_NOTIFY with a payload too short for DecodeTimeValue.
|
||||
func TestGetCorruptReply(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
cli := newTestClient(t, srv)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Establish the channel first so resolution succeeds.
|
||||
if _, err := cli.Get(ctx, "TEST:DOUBLE"); err != nil {
|
||||
t.Fatalf("initial Get: %v", err)
|
||||
}
|
||||
|
||||
srv.SetGetFault(testca.GetFaultCorrupt)
|
||||
|
||||
if _, err := cli.Get(ctx, "TEST:DOUBLE"); err == nil {
|
||||
t.Fatal("Get with corrupt reply: want error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestServerDisconnectReconnects covers the CmdServerDisc dispatch branch and the
|
||||
// reconnect loop: after an orderly server disconnect the client transparently
|
||||
// reconnects, re-creates its channel, re-subscribes the monitor, and continues to
|
||||
// receive updates.
|
||||
func TestServerDisconnectReconnects(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
cli := newTestClient(t, srv)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
ch := make(chan proto.TimeValue, 16)
|
||||
unsub, err := cli.Subscribe(ctx, "TEST:DOUBLE", ch)
|
||||
if err != nil {
|
||||
t.Fatalf("Subscribe: %v", err)
|
||||
}
|
||||
defer unsub()
|
||||
|
||||
// Drain the initial value.
|
||||
select {
|
||||
case <-ch:
|
||||
case <-ctx.Done():
|
||||
t.Fatal("timeout waiting for initial value")
|
||||
}
|
||||
|
||||
// Force an orderly disconnect; the client must reconnect on its own.
|
||||
srv.Disconnect()
|
||||
|
||||
// After the reconnect settles, push a fresh value and expect to see it.
|
||||
// The reconnect back-off is ~1s, so poll generously and re-arm the value
|
||||
// until it is observed (the re-subscribe also delivers an initial value).
|
||||
deadline := time.After(8 * time.Second)
|
||||
tick := time.NewTicker(300 * time.Millisecond)
|
||||
defer tick.Stop()
|
||||
for {
|
||||
select {
|
||||
case tv := <-ch:
|
||||
if math.Abs(tv.Double-88.8) < 1e-6 {
|
||||
return // reconnect succeeded and the update flowed through
|
||||
}
|
||||
case <-tick.C:
|
||||
_ = srv.SetValue("TEST:DOUBLE", 88.8)
|
||||
case <-deadline:
|
||||
t.Fatal("timeout waiting for post-reconnect update")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user