From 6d7f08beddafb17e9c181890babda7bd3c7d0b16 Mon Sep 17 00:00:00 2001 From: m1saka Date: Wed, 22 Jul 2026 11:45:57 +0800 Subject: [PATCH] fix: preserve batch result compatibility --- device.go | 10 ++-------- device_test.go | 48 +++++++++++++++++++++++++++--------------------- types.go | 9 ++++++++- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/device.go b/device.go index fb576f0..e09ad2c 100644 --- a/device.go +++ b/device.go @@ -767,18 +767,12 @@ func (device *Device) GetMany(ctx context.Context, names []string) ([]DeviceProp identity := propertyIdentity{did: request.DID, siid: request.SIID, piid: request.PIID} name := names[start+index] if _, duplicate := duplicateIdentities[identity]; duplicate { - results[start+index] = DevicePropertyResult{ - Name: name, - Err: fmt.Errorf("get properties protocol error: duplicate identity (%s,%d,%d)", request.DID, request.SIID, request.PIID), - } + results[start+index] = DevicePropertyResult{Name: name, Code: PropertyResultCodeDuplicate} continue } result, ok := byIdentity[identity] if !ok { - results[start+index] = DevicePropertyResult{ - Name: name, - Err: fmt.Errorf("get properties protocol error: missing identity (%s,%d,%d)", request.DID, request.SIID, request.PIID), - } + results[start+index] = DevicePropertyResult{Name: name, Code: PropertyResultCodeMissing} continue } results[start+index] = DevicePropertyResult{Name: name, Value: result.Value, Code: result.Code} diff --git a/device_test.go b/device_test.go index 3c30df5..74a72fb 100644 --- a/device_test.go +++ b/device_test.go @@ -18,6 +18,8 @@ import ( "time" ) +var _ = DevicePropertyResult{"x", nil, 0} + type deviceTestServer struct { t *testing.T fixture []byte @@ -625,51 +627,55 @@ func TestDeviceGetManyMatchesIdentityAndPreservesBusinessErrors(t *testing.T) { } } -func TestDeviceGetManyPreservesPartialResultsForInvalidResponseIdentities(t *testing.T) { +func TestDeviceGetManyClassifiesMissingAndDuplicateResults(t *testing.T) { tests := []struct { name string response string - wantErrs []string + want []DevicePropertyResult }{ - {name: "missing", response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0}]`, wantErrs: []string{"", "missing identity"}}, - {name: "duplicate and missing", response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0},{"did":"a","siid":2,"piid":1,"value":false,"code":0}]`, wantErrs: []string{"duplicate identity", "missing identity"}}, + { + name: "missing", + response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0}]`, + want: []DevicePropertyResult{{"power", true, 0}, {"brightness", nil, PropertyResultCodeMissing}}, + }, + { + name: "duplicate", + response: `[{"did":"a","siid":2,"piid":1,"value":true,"code":0},{"did":"a","siid":2,"piid":1,"value":false,"code":0},{"did":"a","siid":2,"piid":2,"value":5,"code":0}]`, + want: []DevicePropertyResult{{"power", nil, PropertyResultCodeDuplicate}, {"brightness", json.Number("5"), 0}}, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { device := fixtureDeviceWithResults(t, []string{test.response}, 0) results, err := device.GetMany(context.Background(), []string{"power", "brightness"}) - if err != nil || len(results) != 2 { - t.Fatalf("GetMany() = %#v, %v", results, err) - } - for index, wantErr := range test.wantErrs { - if results[index].Name != []string{"power", "brightness"}[index] { - t.Fatalf("result %d name = %q", index, results[index].Name) - } - if wantErr == "" { - if results[index].Err != nil || results[index].Value != true { - t.Fatalf("result %d = %#v, want successful power result", index, results[index]) - } - } else if results[index].Err == nil || !strings.Contains(results[index].Err.Error(), wantErr) { - t.Fatalf("result %d error = %v, want %q", index, results[index].Err, wantErr) - } + if err != nil || !reflect.DeepEqual(results, test.want) { + t.Fatalf("GetMany() = %#v, %v, want %#v, nil", results, err, test.want) } }) } } -func TestDeviceGetManyRejectsExtraResponseIdentity(t *testing.T) { +func TestDeviceGetManyRejectsExtraResult(t *testing.T) { device := fixtureDeviceWithResults(t, []string{`[ {"did":"a","siid":2,"piid":1,"value":true,"code":0}, {"did":"a","siid":2,"piid":2,"value":5,"code":0}, {"did":"other","siid":9,"piid":9,"value":1,"code":0} ]`}, 0) - results, err := device.GetMany(context.Background(), []string{"power", "brightness"}) if err == nil || results != nil || !strings.Contains(err.Error(), "protocol") { t.Fatalf("GetMany() = %#v, %v, want nil protocol error", results, err) } } +func TestDeviceGetManyReturnsTransportError(t *testing.T) { + device, testServer := fixtureDeviceWithServer(t, nil) + testServer.server.Close() + + results, err := device.GetMany(context.Background(), []string{"power", "brightness"}) + if err == nil || results != nil { + t.Fatalf("GetMany() = %#v, %v, want nil transport error", results, err) + } +} func TestDeviceGetManyValidatesBeforeNetwork(t *testing.T) { device, testServer := fixtureDeviceWithServer(t, nil) tests := [][]string{{"power", "power"}, {"power", "missing"}, {"power", "write-only"}} @@ -723,7 +729,7 @@ func TestDeviceGetManyWaitsOnceWithPartialProtocolErrors(t *testing.T) { started := time.Now() results, err := device.GetMany(context.Background(), []string{"power", "brightness"}) elapsed := time.Since(started) - if err != nil || len(results) != 2 || results[1].Err == nil { + if err != nil || len(results) != 2 || results[1].Code != PropertyResultCodeMissing { t.Fatalf("GetMany() = %#v, %v", results, err) } if elapsed < 30*time.Millisecond || elapsed >= 75*time.Millisecond { diff --git a/types.go b/types.go index 0c0f8ee..32d276a 100644 --- a/types.go +++ b/types.go @@ -5,9 +5,17 @@ import ( "encoding/json" "fmt" "io" + "math" "time" ) +const ( + // PropertyResultCodeMissing classifies a missing GetMany result locally and is never returned by upstream. + PropertyResultCodeMissing int = math.MinInt32 + // PropertyResultCodeDuplicate classifies duplicate GetMany results locally and is never returned by upstream. + PropertyResultCodeDuplicate int = math.MinInt32 + 1 +) + type Home struct { ID string `json:"id"` Name string `json:"name"` @@ -163,7 +171,6 @@ type DevicePropertyResult struct { Name string Value any Code int - Err error `json:"-"` } type ActionRequest struct {