Skip to content

Commit cb29ca7

Browse files
(powersensor/sensor) Guard against nil responses in rdk client/servers (viamrobotics#4621)
1 parent 139248a commit cb29ca7

File tree

7 files changed

+46
-0
lines changed

7 files changed

+46
-0
lines changed

components/powersensor/client_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"go.viam.com/rdk/components/motor"
1313
"go.viam.com/rdk/components/powersensor"
14+
"go.viam.com/rdk/components/sensor"
1415
viamgrpc "go.viam.com/rdk/grpc"
1516
"go.viam.com/rdk/logging"
1617
"go.viam.com/rdk/resource"
@@ -57,6 +58,10 @@ func TestClient(t *testing.T) {
5758
return 0, errPowerFailed
5859
}
5960

61+
failingPowerSensor.ReadingsFunc = func(ctx context.Context, extra map[string]interface{}) (map[string]interface{}, error) {
62+
return nil, nil
63+
}
64+
6065
resourceMap := map[resource.Name]powersensor.PowerSensor{
6166
motor.Named(workingPowerSensorName): workingPowerSensor,
6267
motor.Named(failingPowerSensorName): failingPowerSensor,
@@ -134,6 +139,9 @@ func TestClient(t *testing.T) {
134139
test.That(t, err.Error(), test.ShouldContainSubstring, errPowerFailed.Error())
135140
test.That(t, watts, test.ShouldEqual, 0)
136141

142+
_, err = client.Readings(context.Background(), make(map[string]interface{}))
143+
test.That(t, err.Error(), test.ShouldContainSubstring, sensor.ErrReadingsNil("power-sensor", failingPowerSensorName).Error())
144+
137145
test.That(t, client.Close(context.Background()), test.ShouldBeNil)
138146
test.That(t, conn.Close(), test.ShouldBeNil)
139147
})

components/powersensor/server.go

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
commonpb "go.viam.com/api/common/v1"
77
pb "go.viam.com/api/component/powersensor/v1"
88

9+
"go.viam.com/rdk/components/sensor"
910
"go.viam.com/rdk/protoutils"
1011
"go.viam.com/rdk/resource"
1112
)
@@ -33,6 +34,9 @@ func (s *serviceServer) GetReadings(
3334
if err != nil {
3435
return nil, err
3536
}
37+
if readings == nil {
38+
return nil, sensor.ErrReadingsNil("power-sensor", req.Name)
39+
}
3640
m, err := protoutils.ReadingGoToProto(readings)
3741
if err != nil {
3842
return nil, err

components/powersensor/server_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@ func TestServerGetReadings(t *testing.T) {
179179
test.That(t, err, test.ShouldNotBeNil)
180180
test.That(t, err.Error(), test.ShouldContainSubstring, errReadingsFailed.Error())
181181

182+
failingPowerSensor.ReadingsFunc = func(ctx context.Context, extra map[string]interface{}) (map[string]interface{}, error) {
183+
return nil, nil
184+
}
185+
186+
_, err = powerSensorServer.GetReadings(context.Background(), &commonpb.GetReadingsRequest{Name: failingPowerSensorName})
187+
test.That(t, err, test.ShouldNotBeNil)
188+
test.That(t, err.Error(), test.ShouldContainSubstring, sensor.ErrReadingsNil("power-sensor", failingPowerSensorName).Error())
189+
182190
_, err = powerSensorServer.GetReadings(context.Background(), &commonpb.GetReadingsRequest{Name: missingPowerSensorName})
183191
test.That(t, err, test.ShouldNotBeNil)
184192
test.That(t, err.Error(), test.ShouldContainSubstring, "not found")

components/sensor/client_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ func TestClient(t *testing.T) {
106106
test.That(t, err, test.ShouldNotBeNil)
107107
test.That(t, err.Error(), test.ShouldContainSubstring, errReadingsFailed.Error())
108108

109+
injectSensor2.ReadingsFunc = func(ctx context.Context, extra map[string]interface{}) (map[string]interface{}, error) {
110+
return nil, nil
111+
}
112+
_, err = client2.Readings(context.Background(), make(map[string]interface{}))
113+
test.That(t, err, test.ShouldNotBeNil)
114+
test.That(t, err.Error(), test.ShouldContainSubstring, sensor.ErrReadingsNil("sensor", failSensorName).Error())
115+
109116
test.That(t, client2.Close(context.Background()), test.ShouldBeNil)
110117
test.That(t, conn.Close(), test.ShouldBeNil)
111118
})

components/sensor/server.go

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sensor
33

44
import (
55
"context"
6+
"fmt"
67

78
commonpb "go.viam.com/api/common/v1"
89
pb "go.viam.com/api/component/sensor/v1"
@@ -11,6 +12,11 @@ import (
1112
"go.viam.com/rdk/resource"
1213
)
1314

15+
// ErrReadingsNil is the returned error if sensor readings are nil.
16+
var ErrReadingsNil = func(sensorType, sensorName string) error {
17+
return fmt.Errorf("%v component %v Readings should not return nil readings", sensorType, sensorName)
18+
}
19+
1420
// serviceServer implements the SensorService from sensor.proto.
1521
type serviceServer struct {
1622
pb.UnimplementedSensorServiceServer
@@ -35,6 +41,9 @@ func (s *serviceServer) GetReadings(
3541
if err != nil {
3642
return nil, err
3743
}
44+
if readings == nil {
45+
return nil, ErrReadingsNil("sensor", req.Name)
46+
}
3847
m, err := protoutils.ReadingGoToProto(readings)
3948
if err != nil {
4049
return nil, err

components/sensor/server_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ func TestServer(t *testing.T) {
6767
test.That(t, err, test.ShouldNotBeNil)
6868
test.That(t, err.Error(), test.ShouldContainSubstring, errReadingsFailed.Error())
6969

70+
injectSensor2.ReadingsFunc = func(ctx context.Context, extra map[string]interface{}) (map[string]interface{}, error) {
71+
return nil, nil
72+
}
73+
_, err = sensorServer.GetReadings(context.Background(), &commonpb.GetReadingsRequest{Name: failSensorName})
74+
test.That(t, err, test.ShouldNotBeNil)
75+
test.That(t, err.Error(), test.ShouldContainSubstring, sensor.ErrReadingsNil("sensor", failSensorName).Error())
76+
7077
_, err = sensorServer.GetReadings(context.Background(), &commonpb.GetReadingsRequest{Name: missingSensorName})
7178
test.That(t, err, test.ShouldNotBeNil)
7279
test.That(t, err.Error(), test.ShouldContainSubstring, "not found")

protoutils/sensor.go

+3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ func ReadingGoToProto(readings map[string]interface{}) (map[string]*structpb.Val
125125

126126
// ReadingProtoToGo converts proto readings to go readings.
127127
func ReadingProtoToGo(readings map[string]*structpb.Value) (map[string]interface{}, error) {
128+
if readings == nil {
129+
return nil, nil
130+
}
128131
m := map[string]interface{}{}
129132
for k, v := range readings {
130133
m[k] = cleanSensorType(v.AsInterface())

0 commit comments

Comments
 (0)