package grpc import ( "context" "errors" "net" "testing" hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/status" "google.golang.org/grpc/test/bufconn" ) type mockRemoteService struct { sendCommandFunc func(ctx context.Context, deviceID, command, commandType string) error } func (m *mockRemoteService) SendCommand(ctx context.Context, deviceID, command, commandType string) error { if m.sendCommandFunc == nil { return nil } return m.sendCommandFunc(ctx, deviceID, command, commandType) } func TestRemoteGRPCSendCommand(t *testing.T) { tests := []struct { name string err error wantCode codes.Code }{ {name: "happy path", wantCode: codes.OK}, {name: "generic error maps to codes.Internal", err: errors.New("boom"), wantCode: codes.Internal}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var gotDeviceID, gotCommand, gotCommandType string conn := newRemoteTestClientConn(t, &mockRemoteService{ sendCommandFunc: func(ctx context.Context, deviceID, command, commandType string) error { gotDeviceID = deviceID gotCommand = command gotCommandType = commandType return tt.err }, }) client := hav1.NewRemoteServiceClient(conn) _, err := client.SendCommand(context.Background(), &hav1.SendCommandRequest{ DeviceId: "dyson-id", Command: "turnOn", CommandType: "command", }) if status.Code(err) != tt.wantCode { t.Fatalf("status code = %v, want %v", status.Code(err), tt.wantCode) } if tt.wantCode != codes.OK { return } if gotDeviceID != "dyson-id" || gotCommand != "turnOn" || gotCommandType != "command" { t.Fatalf("SendCommand args = (%q, %q, %q), want (%q, %q, %q)", gotDeviceID, gotCommand, gotCommandType, "dyson-id", "turnOn", "command") } }) } } func newRemoteTestClientConn(t *testing.T, svc *mockRemoteService) *grpc.ClientConn { t.Helper() lis := bufconn.Listen(testBufSize) server := grpc.NewServer() hav1.RegisterRemoteServiceServer(server, NewRemoteGRPC(svc)) go func() { _ = server.Serve(lis) }() t.Cleanup(func() { server.Stop() _ = lis.Close() }) conn, err := grpc.DialContext( context.Background(), "bufnet", grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) { return lis.Dial() }), grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { t.Fatalf("grpc.DialContext() error = %v", err) } t.Cleanup(func() { _ = conn.Close() }) return conn }