Resolve Client.Go-022..027: bulk flags, bench cancel, batch loop
Client.Go-022 Re-applied Client.Go-015 shape — runWriteBulkVariant drops
the unused secured param and gates -current-user-id /
-verifier-user-id / -user-id behind the secured-only
variants.
Client.Go-023 Re-applied Client.Go-018 shape — bench warm-up and steady-
state loops respect ctx.Err().
Client.Go-024 Added SDK-level tests for WriteBulk / Write2Bulk /
WriteSecuredBulk / WriteSecured2Bulk / ReadBulk and
StreamAlarms via the existing bufconn fake gateway pattern.
Client.Go-025 Five bulk SDK methods short-circuit on empty input without
an RPC round-trip and document the behavior.
Client.Go-026 runBatch widens scanner.Buffer to 16 MiB and emits an
error-with-sentinel if a longer line still arrives, rather
than aborting the session silently.
Client.Go-027 runBatch treats blank lines as skip-and-continue; only EOF
ends the session.
All resolved at 2026-05-24; gofmt + go vet + go build + go test ./... all
green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -230,6 +230,206 @@ func TestSubscribeBulkBuildsOneBulkCommandAndReturnsResults(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteBulkBuildsOneBulkCommandAndReturnsPerEntryResults(t *testing.T) {
|
||||
fake := &fakeGatewayServer{
|
||||
invokeReply: &pb.MxCommandReply{
|
||||
SessionId: "session-1",
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_WRITE_BULK,
|
||||
ProtocolStatus: &pb.ProtocolStatus{
|
||||
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
||||
},
|
||||
Payload: &pb.MxCommandReply_WriteBulk{
|
||||
WriteBulk: &pb.BulkWriteReply{
|
||||
Results: []*pb.BulkWriteResult{
|
||||
{ItemHandle: 10, WasSuccessful: true},
|
||||
{ItemHandle: 11, WasSuccessful: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
client, cleanup := newBufconnClient(t, fake)
|
||||
defer cleanup()
|
||||
session := NewSessionForID(client, "session-1")
|
||||
|
||||
entries := []*WriteBulkEntry{
|
||||
{ItemHandle: 10, Value: Int32Value(7), UserId: 100},
|
||||
{ItemHandle: 11, Value: Int32Value(8), UserId: 100},
|
||||
}
|
||||
results, err := session.WriteBulk(context.Background(), 12, entries)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBulk() error = %v", err)
|
||||
}
|
||||
if len(results) != 2 {
|
||||
t.Fatalf("results len = %d, want 2", len(results))
|
||||
}
|
||||
req := fake.invokeRequest
|
||||
if req.GetCommand().GetKind() != pb.MxCommandKind_MX_COMMAND_KIND_WRITE_BULK {
|
||||
t.Fatalf("command kind = %s", req.GetCommand().GetKind())
|
||||
}
|
||||
if got := req.GetCommand().GetWriteBulk().GetEntries(); len(got) != 2 {
|
||||
t.Fatalf("entry count = %d, want 2", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteBulkRejectsNilEntries(t *testing.T) {
|
||||
fake := &fakeGatewayServer{}
|
||||
client, cleanup := newBufconnClient(t, fake)
|
||||
defer cleanup()
|
||||
session := NewSessionForID(client, "session-1")
|
||||
|
||||
if _, err := session.WriteBulk(context.Background(), 12, nil); err == nil {
|
||||
t.Fatal("WriteBulk(nil) returned no error")
|
||||
}
|
||||
if _, err := session.Write2Bulk(context.Background(), 12, nil); err == nil {
|
||||
t.Fatal("Write2Bulk(nil) returned no error")
|
||||
}
|
||||
if _, err := session.WriteSecuredBulk(context.Background(), 12, nil); err == nil {
|
||||
t.Fatal("WriteSecuredBulk(nil) returned no error")
|
||||
}
|
||||
if _, err := session.WriteSecured2Bulk(context.Background(), 12, nil); err == nil {
|
||||
t.Fatal("WriteSecured2Bulk(nil) returned no error")
|
||||
}
|
||||
if _, err := session.ReadBulk(context.Background(), 12, nil, 0); err == nil {
|
||||
t.Fatal("ReadBulk(nil) returned no error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBulkMethodsShortCircuitOnEmptySliceWithoutRoundTrip(t *testing.T) {
|
||||
fake := &fakeGatewayServer{
|
||||
invokeReply: &pb.MxCommandReply{
|
||||
ProtocolStatus: &pb.ProtocolStatus{
|
||||
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
||||
},
|
||||
},
|
||||
}
|
||||
client, cleanup := newBufconnClient(t, fake)
|
||||
defer cleanup()
|
||||
session := NewSessionForID(client, "session-1")
|
||||
|
||||
results, err := session.WriteBulk(context.Background(), 12, []*WriteBulkEntry{})
|
||||
if err != nil {
|
||||
t.Fatalf("WriteBulk(empty) error = %v", err)
|
||||
}
|
||||
if len(results) != 0 {
|
||||
t.Fatalf("WriteBulk(empty) results len = %d, want 0", len(results))
|
||||
}
|
||||
if fake.invokeRequest != nil {
|
||||
t.Fatal("WriteBulk(empty) sent a round trip; expected short-circuit")
|
||||
}
|
||||
|
||||
results2, err := session.Write2Bulk(context.Background(), 12, []*Write2BulkEntry{})
|
||||
if err != nil {
|
||||
t.Fatalf("Write2Bulk(empty) error = %v", err)
|
||||
}
|
||||
if len(results2) != 0 {
|
||||
t.Fatalf("Write2Bulk(empty) results len = %d, want 0", len(results2))
|
||||
}
|
||||
if fake.invokeRequest != nil {
|
||||
t.Fatal("Write2Bulk(empty) sent a round trip; expected short-circuit")
|
||||
}
|
||||
|
||||
results3, err := session.WriteSecuredBulk(context.Background(), 12, []*WriteSecuredBulkEntry{})
|
||||
if err != nil {
|
||||
t.Fatalf("WriteSecuredBulk(empty) error = %v", err)
|
||||
}
|
||||
if len(results3) != 0 {
|
||||
t.Fatalf("WriteSecuredBulk(empty) results len = %d, want 0", len(results3))
|
||||
}
|
||||
if fake.invokeRequest != nil {
|
||||
t.Fatal("WriteSecuredBulk(empty) sent a round trip; expected short-circuit")
|
||||
}
|
||||
|
||||
results4, err := session.WriteSecured2Bulk(context.Background(), 12, []*WriteSecured2BulkEntry{})
|
||||
if err != nil {
|
||||
t.Fatalf("WriteSecured2Bulk(empty) error = %v", err)
|
||||
}
|
||||
if len(results4) != 0 {
|
||||
t.Fatalf("WriteSecured2Bulk(empty) results len = %d, want 0", len(results4))
|
||||
}
|
||||
if fake.invokeRequest != nil {
|
||||
t.Fatal("WriteSecured2Bulk(empty) sent a round trip; expected short-circuit")
|
||||
}
|
||||
|
||||
readResults, err := session.ReadBulk(context.Background(), 12, []string{}, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadBulk(empty) error = %v", err)
|
||||
}
|
||||
if len(readResults) != 0 {
|
||||
t.Fatalf("ReadBulk(empty) results len = %d, want 0", len(readResults))
|
||||
}
|
||||
if fake.invokeRequest != nil {
|
||||
t.Fatal("ReadBulk(empty) sent a round trip; expected short-circuit")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadBulkForwardsTimeoutAndUnpacksCachedFlag(t *testing.T) {
|
||||
fake := &fakeGatewayServer{
|
||||
invokeReply: &pb.MxCommandReply{
|
||||
SessionId: "session-1",
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_READ_BULK,
|
||||
ProtocolStatus: &pb.ProtocolStatus{
|
||||
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
||||
},
|
||||
Payload: &pb.MxCommandReply_ReadBulk{
|
||||
ReadBulk: &pb.BulkReadReply{
|
||||
Results: []*pb.BulkReadResult{
|
||||
{TagAddress: "Tank01.Level", WasSuccessful: true, WasCached: true},
|
||||
{TagAddress: "Tank02.Level", WasSuccessful: true, WasCached: false},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
client, cleanup := newBufconnClient(t, fake)
|
||||
defer cleanup()
|
||||
session := NewSessionForID(client, "session-1")
|
||||
|
||||
results, err := session.ReadBulk(context.Background(), 12, []string{"Tank01.Level", "Tank02.Level"}, 250*time.Millisecond)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadBulk() error = %v", err)
|
||||
}
|
||||
if len(results) != 2 {
|
||||
t.Fatalf("results len = %d, want 2", len(results))
|
||||
}
|
||||
if !results[0].GetWasCached() || results[1].GetWasCached() {
|
||||
t.Fatalf("WasCached flags = [%v %v], want [true false]", results[0].GetWasCached(), results[1].GetWasCached())
|
||||
}
|
||||
req := fake.invokeRequest
|
||||
if req.GetCommand().GetKind() != pb.MxCommandKind_MX_COMMAND_KIND_READ_BULK {
|
||||
t.Fatalf("command kind = %s", req.GetCommand().GetKind())
|
||||
}
|
||||
if got := req.GetCommand().GetReadBulk().GetTimeoutMs(); got != 250 {
|
||||
t.Fatalf("timeout ms = %d, want 250", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadBulkSaturatesTimeoutAboveMaxUint32(t *testing.T) {
|
||||
fake := &fakeGatewayServer{
|
||||
invokeReply: &pb.MxCommandReply{
|
||||
SessionId: "session-1",
|
||||
Kind: pb.MxCommandKind_MX_COMMAND_KIND_READ_BULK,
|
||||
ProtocolStatus: &pb.ProtocolStatus{
|
||||
Code: pb.ProtocolStatusCode_PROTOCOL_STATUS_CODE_OK,
|
||||
},
|
||||
},
|
||||
}
|
||||
client, cleanup := newBufconnClient(t, fake)
|
||||
defer cleanup()
|
||||
session := NewSessionForID(client, "session-1")
|
||||
|
||||
// 100 days in milliseconds exceeds MaxUint32 (~49.7 days).
|
||||
hugeTimeout := 100 * 24 * time.Hour
|
||||
_, err := session.ReadBulk(context.Background(), 12, []string{"Tank01.Level"}, hugeTimeout)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadBulk() error = %v", err)
|
||||
}
|
||||
got := fake.invokeRequest.GetCommand().GetReadBulk().GetTimeoutMs()
|
||||
if got != ^uint32(0) {
|
||||
t.Fatalf("timeout ms = %d, want %d (MaxUint32)", got, ^uint32(0))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvokeReturnsTypedMxAccessErrorWithRawReply(t *testing.T) {
|
||||
hresult := int32(-2147467259)
|
||||
fake := &fakeGatewayServer{
|
||||
|
||||
Reference in New Issue
Block a user