package device import ( "fmt" "testing" "github.com/stretchr/testify/require" "src.dualinventive.com/go/cp3000-interface/internal/storage" "src.dualinventive.com/go/cp3000-interface/internal/testenv" "src.dualinventive.com/go/dinet" "src.dualinventive.com/go/dinet/rpc" "src.dualinventive.com/go/lib/cp3000" ) func registerCP3000Device(t *testing.T, td *testenv.TestData, newFunc createDeviceFunc) { base, err := newBaseDevice(td.Cp3000Router, td.Repo, "v2.0", 2) require.Nil(t, err) dev := newFunc(base) require.NotNil(t, dev) base.dev = dinet.NewChildDevice(dev, td.ServiceDev) dev.construct() err = dev.RegisterCallbacks() require.Nil(t, err) } func TestRegister(t *testing.T) { testData := testenv.CreateTestData(t) testCases := []struct { LegacyDevType int DeviceType rpc.DeviceType ExpectedType Device ExpectRetrSwitch bool }{ {0, rpc.DeviceTypeZKL3000, &Zkl3000{}, false}, {19, rpc.DeviceTypeZKL3000, &Zkl3000{}, false}, {47, rpc.DeviceTypeZKL3000, &Zkl3000{}, false}, {66, rpc.DeviceTypeZKL3000, &Zkl3000{}, false}, {2, rpc.DeviceTypeZKL3000RC, &Zkl3000Rc{}, true}, {27, rpc.DeviceTypeZKL3000RC, &Zkl3000Rc{}, true}, {33, rpc.DeviceTypeZKL3000RC, &Zkl3000Rc{}, true}, {35, rpc.DeviceTypeZKL3000RC, &Zkl3000Rc{}, true}, {49, rpc.DeviceTypeZKL3000RC, &Zkl3000Rc{}, true}, {61, rpc.DeviceTypeZKL3000RC, &Zkl3000Rc{}, true}, {71, rpc.DeviceTypeZKL3000RC, &Zkl3000Rc{}, true}, {25, rpc.DeviceTypeZKL3000RCC, &Zkl3000Rc{}, true}, {26, rpc.DeviceTypeZKL3000RCC, &Zkl3000Rc{}, true}, {64, rpc.DeviceTypeZKL3000RCC, &Zkl3000Rc{}, true}, {36, rpc.DeviceTypeCRTMGateway, &CrtmGateway{}, false}, {59, rpc.DeviceTypeGRB3000, &CrtmGateway{}, false}, {31, rpc.DeviceTypeCRTMSensor, &CrtmSensor{}, false}, {55, rpc.DeviceTypeCRTMSensor, &CrtmSensor{}, false}, {60, rpc.DeviceTypeCRTMSensor, &CrtmSensor{}, false}, {63, rpc.DeviceTypeCRTMSensor, &CrtmSensor{}, false}, } for id, tc := range testCases { imei := fmt.Sprintf("imei_%d", tc.LegacyDevType) testData.Repo.Devices[imei] = &storage.Device{ ID: id, Type: tc.LegacyDevType, IMEI: imei, HWVersion: fmt.Sprintf("hw_%d", tc.LegacyDevType), } } for _, tc := range testCases { // Transfer tc to local variable for fixing schope tc := tc t.Run(fmt.Sprintf("Dev: %d", tc.LegacyDevType), func(t *testing.T) { if tc.ExpectRetrSwitch { testZkl3000RequestSwitchInfoAssertReqRep(testData) testData.Cp3000Connection.AssertReqRep(&cp3000.Msg{ Type: cp3000.TypeCommand, Command: cp3000.CommandRetrieve, Params: []string{"SWITCH"}, }, &cp3000.Msg{Type: cp3000.TypeReply, Command: cp3000.Command("00"), Params: []string{"01008F14"}}) } d, err := Register(testData.ServiceDev, testData.Repo, testData.Cp3000Router, fmt.Sprintf("imei_%d", tc.LegacyDevType)) require.Nil(t, err) require.IsType(t, tc.ExpectedType, d) testData.Cp3000Connection.WaitForEmptyQueue() }) } testData.Finish() } func TestDeviceInfo(t *testing.T) { testCases := []struct { newFunc createDeviceFunc devType rpc.DeviceType hwKey rpc.VersionKey versions []string keys []rpc.VersionKey }{ { newFunc: newZkl3000, devType: rpc.DeviceTypeZKL3000, hwKey: rpc.VersionKeyHwZKLMain, versions: []string{"FW-MCU", "FW-WCPU"}, keys: []rpc.VersionKey{rpc.VersionKeyFwZKLMain, rpc.VersionKeyFwZKLWcpu}, }, { newFunc: newZkl3000Rc, devType: rpc.DeviceTypeZKL3000RC, hwKey: rpc.VersionKeyHwZKLRCMain, versions: []string{"FW-MCU", "FW-WCPU", "FW-SW3000-M", "FW-SW3000-D"}, keys: []rpc.VersionKey{ rpc.VersionKeyFwZKLRCMain, rpc.VersionKeyFwZKLRCWcpu, rpc.VersionKeyFwZKLRCSwitchMeas, rpc.VersionKeyFwZKLRCSwitchDrive, }, }, { newFunc: newZkl3000Rcc, devType: rpc.DeviceTypeZKL3000RCC, hwKey: rpc.VersionKeyHwZKLRCMain, versions: []string{"FW-MCU", "FW-WCPU", "FW-SW3000-M", "FW-SW3000-D"}, keys: []rpc.VersionKey{ rpc.VersionKeyFwZKLRCMain, rpc.VersionKeyFwZKLRCWcpu, rpc.VersionKeyFwZKLRCSwitchMeas, rpc.VersionKeyFwZKLRCSwitchDrive, }, }, { newFunc: newCrtmGateway, devType: rpc.DeviceTypeCRTMGateway, hwKey: rpc.VersionKeyHwGateway, versions: []string{"FW-WCPU"}, keys: []rpc.VersionKey{rpc.VersionKeyFwGateway}, }, { newFunc: newGreenHub, devType: rpc.DeviceTypeGRB3000, hwKey: rpc.VersionKeyHwGRB, versions: []string{"FW-WCPU"}, keys: []rpc.VersionKey{rpc.VersionKeyFwGRB}, }, { newFunc: newCrtmSensor, devType: rpc.DeviceTypeCRTMSensor, hwKey: rpc.VersionKeyHwCRTM, versions: []string{"FW-MCU"}, keys: []rpc.VersionKey{rpc.VersionKeyFwCRTM}, }, } for _, tc := range testCases { // Transfer tc to local variable for fixing schope tc := tc t.Run(string(tc.devType), func(t *testing.T) { testData := testenv.CreateTestData(t) for i, cmd := range tc.versions { testData.Cp3000Connection.AssertReqRep(&cp3000.Msg{ Type: cp3000.TypeCommand, Command: cp3000.CommandRetrieve, Params: []string{fmt.Sprintf("VERSION[%s]", cmd)}, }, &cp3000.Msg{Type: cp3000.TypeReply, Command: cp3000.Command("00"), Params: []string{fmt.Sprintf("v1.%d", i)}}) } registerCP3000Device(t, testData, tc.newFunc) response := testData.ReqRep(rpc.ClassMethodDeviceInfo) require.Nil(t, response.Error) var information []rpc.DeviceInfo require.Nil(t, response.Result.Unmarshal(&information)) require.Len(t, information, 1) require.Equal(t, tc.devType, information[0].Type) vMap := rpc.VersionMap{tc.hwKey: "v2.0"} for i, key := range tc.keys { vMap[key] = fmt.Sprintf("v1.%d", i) } require.Equal(t, vMap, information[0].Version) testData.Finish() }) } } func TestConnectionInfo(t *testing.T) { testCases := []struct { newFunc createDeviceFunc devType rpc.DeviceType timeout uint32 transport rpc.ConnectionTransport }{ { newFunc: newZkl3000, devType: rpc.DeviceTypeZKL3000, timeout: 60, transport: rpc.ConnectionTransportCP3000, }, { newFunc: newZkl3000Rc, devType: rpc.DeviceTypeZKL3000RC, timeout: 60, transport: rpc.ConnectionTransportCP3000, }, { newFunc: newZkl3000Rcc, devType: rpc.DeviceTypeZKL3000RCC, timeout: 60, transport: rpc.ConnectionTransportCP3000, }, { newFunc: newCrtmGateway, devType: rpc.DeviceTypeCRTMGateway, timeout: 60, transport: rpc.ConnectionTransportCP3000, }, { newFunc: newGreenHub, devType: rpc.DeviceTypeGRB3000, timeout: 60, transport: rpc.ConnectionTransportCP3000, }, { newFunc: newCrtmSensor, devType: rpc.DeviceTypeCRTMSensor, timeout: 60, transport: rpc.ConnectionTransportXBee, }, } for _, tc := range testCases { // Transfer tc to local variable for fixing schope tc := tc t.Run(string(tc.devType), func(t *testing.T) { testData := testenv.CreateTestData(t) if tc.transport == rpc.ConnectionTransportCP3000 { testData.Cp3000Connection.AssertReqRep(&cp3000.Msg{ Type: cp3000.TypeCommand, Command: cp3000.CommandRetrieve, Params: []string{"VERSION[IMSI]"}, }, &cp3000.Msg{Type: cp3000.TypeReply, Command: cp3000.Command("00"), Params: []string{"imsi"}}) testData.Cp3000Connection.AssertReqRep(&cp3000.Msg{ Type: cp3000.TypeCommand, Command: cp3000.CommandRetrieve, Params: []string{"VERSION[SIM]"}, }, &cp3000.Msg{Type: cp3000.TypeReply, Command: cp3000.Command("00"), Params: []string{"sim"}}) testData.Cp3000Connection.AssertReqRep(&cp3000.Msg{ Type: cp3000.TypeCommand, Command: cp3000.CommandRetrieve, Params: []string{"VERSION[IMEI]"}, }, &cp3000.Msg{Type: cp3000.TypeReply, Command: cp3000.Command("00"), Params: []string{"imei"}}) testData.Cp3000Connection.AssertReqRep(&cp3000.Msg{ Type: cp3000.TypeCommand, Command: cp3000.CommandRetrieve, Params: []string{"DBSERVER"}, }, &cp3000.Msg{Type: cp3000.TypeReply, Command: cp3000.Command("00"), Params: []string{"dbserver"}}) testData.Cp3000Connection.AssertReqRep(&cp3000.Msg{ Type: cp3000.TypeCommand, Command: cp3000.CommandRetrieve, Params: []string{"GPRS"}, }, &cp3000.Msg{Type: cp3000.TypeReply, Command: cp3000.Command("00"), Params: []string{"gprs"}}) } registerCP3000Device(t, testData, tc.newFunc) response := testData.ReqRep(rpc.ClassMethodConnectionInfo) require.Nil(t, response.Error) var information []rpc.ConnectionInfo require.Nil(t, response.Result.Unmarshal(&information)) require.Len(t, information, 1) require.Equal(t, tc.timeout, information[0].Timeout) require.Equal(t, tc.transport, information[0].Transport) require.Nil(t, information[0].Service) require.Nil(t, information[0].Can) require.Nil(t, information[0].Cellular) require.Nil(t, information[0].LoRa) if tc.transport == rpc.ConnectionTransportCP3000 { require.Nil(t, information[0].XBee) require.NotNil(t, information[0].CP3000) require.Equal(t, rpc.ConnectionInfoCP3000{ DatabaseID: 2, Imsi: "imsi", Iccid: "sim", Imei: "imei", GPRSApn: "gprs", Endpoint: "dbserver", }, *information[0].CP3000) } else if tc.transport == rpc.ConnectionTransportXBee { require.Nil(t, information[0].CP3000) require.NotNil(t, information[0].XBee) require.Equal(t, rpc.ConnectionInfoXBee{}, *information[0].XBee) } testData.Finish() }) } } func TestConfigInfoSensorInfo(t *testing.T) { // nolint: dupl testCases := []struct { newFunc createDeviceFunc devType rpc.DeviceType configs []rpc.ResultConfigInfoItem sensors []rpc.ResultInfoItem }{ { newFunc: newZkl3000, devType: rpc.DeviceTypeZKL3000, configs: []rpc.ResultConfigInfoItem{rpc.ConfigEndpoint.Info()}, sensors: []rpc.ResultInfoItem{ rpc.SensorBattery1Voltage.Info(), rpc.SensorBattery1State.Info(), rpc.SensorBattery2Voltage.Info(), rpc.SensorBattery2State.Info(), rpc.SensorGPS.Info(), rpc.SensorRSSI.Info(), rpc.SensorBER.Info(), rpc.ZKLSensorDetectionQuality.Info(), rpc.ZKLSensorDetection.Info(), rpc.ZKLSensorMeasurement.Info(), rpc.ZKLSensorBa.Info(), rpc.ZKLSensorFrequency.Info(), rpc.ZKLSensorRMS.Info(), rpc.ZKLSensorBAAutocal.Info(), rpc.ZKLSensorTempOnboard.Info(), rpc.ZKLSensorTempNTC.Info(), }, }, { newFunc: newZkl3000Rc, devType: rpc.DeviceTypeZKL3000RC, configs: []rpc.ResultConfigInfoItem{rpc.ConfigEndpoint.Info(), rpc.ConfigToken.Info(), rpc.ConfigActive.Info()}, sensors: []rpc.ResultInfoItem{ rpc.SensorBattery1Voltage.Info(), rpc.SensorBattery1State.Info(), rpc.SensorBattery2Voltage.Info(), rpc.SensorBattery2State.Info(), rpc.SensorCharger1State.Info(), rpc.SensorGPS.Info(), rpc.SensorRSSI.Info(), rpc.SensorBER.Info(), rpc.ZKLSensorDetectionQuality.Info(), rpc.ZKLSensorDetection.Info(), rpc.ZKLSensorMeasurement.Info(), rpc.ZKLSensorBa.Info(), rpc.ZKLSensorFrequency.Info(), rpc.ZKLSensorRMS.Info(), rpc.ZKLSensorBAAutocal.Info(), rpc.ZKLSensorTempOnboard.Info(), rpc.ZKLSensorTempNTC.Info(), rpc.ZKLRCSensorSectionsShort.Info(), rpc.ZKLRCSensorSectionsBattery.Info(), rpc.ZKLRCSensorSwitchShort.Info(), rpc.ZKLRCSensorKeySwitch.Info(), }, }, { newFunc: newZkl3000Rcc, devType: rpc.DeviceTypeZKL3000RCC, configs: []rpc.ResultConfigInfoItem{rpc.ConfigEndpoint.Info(), rpc.ConfigToken.Info(), rpc.ConfigActive.Info()}, sensors: []rpc.ResultInfoItem{ rpc.SensorBattery1Voltage.Info(), rpc.SensorBattery1State.Info(), rpc.SensorBattery2Voltage.Info(), rpc.SensorBattery2State.Info(), rpc.SensorCharger1State.Info(), rpc.SensorGPS.Info(), rpc.SensorRSSI.Info(), rpc.SensorBER.Info(), rpc.ZKLSensorDetectionQuality.Info(), rpc.ZKLSensorDetection.Info(), rpc.ZKLSensorMeasurement.Info(), rpc.ZKLSensorBa.Info(), rpc.ZKLSensorFrequency.Info(), rpc.ZKLSensorRMS.Info(), rpc.ZKLSensorBAAutocal.Info(), rpc.ZKLSensorTempOnboard.Info(), rpc.ZKLSensorTempNTC.Info(), rpc.ZKLRCSensorSectionsShort.Info(), rpc.ZKLRCSensorSectionsBattery.Info(), rpc.ZKLRCSensorSwitchShort.Info(), rpc.ZKLRCSensorKeySwitch.Info(), }, }, { newFunc: newCrtmGateway, devType: rpc.DeviceTypeCRTMGateway, configs: []rpc.ResultConfigInfoItem{rpc.ConfigEndpoint.Info()}, sensors: []rpc.ResultInfoItem{ rpc.SensorBattery1Voltage.Info(), rpc.SensorBattery1State.Info(), rpc.SensorRSSI.Info(), rpc.SensorBER.Info(), rpc.GatewaySensorTemperature1.Info(), }, }, { newFunc: newGreenHub, devType: rpc.DeviceTypeGRB3000, configs: []rpc.ResultConfigInfoItem{rpc.ConfigEndpoint.Info()}, sensors: []rpc.ResultInfoItem{ rpc.SensorBattery1Voltage.Info(), rpc.SensorBattery1State.Info(), rpc.SensorRSSI.Info(), rpc.SensorBER.Info(), rpc.GatewaySensorTemperature1.Info(), }, }, { newFunc: newCrtmSensor, devType: rpc.DeviceTypeCRTMSensor, configs: []rpc.ResultConfigInfoItem{rpc.ConfigEndpoint.Info()}, sensors: []rpc.ResultInfoItem{ rpc.SensorBattery1Voltage.Info(), rpc.SensorBattery1State.Info(), rpc.SensorRSSI.Info(), rpc.SensorBER.Info(), rpc.CRTMSensorTemperature1.Info(), rpc.CRTMSensorTemperature2.Info(), rpc.CRTMSensorRailContact.Info(), rpc.CRTMSensorRailContactSleep.Info(), rpc.CRTMSensorAcceleration.Info(), }, }, } for _, tc := range testCases { // Transfer tc to local variable for fixing schope tc := tc classmethodTestCase := []struct { classMethod rpc.ClassMethod expect interface{} unmarshal interface{} }{ {rpc.ClassMethodConfigInfo, tc.configs, &[]rpc.ResultConfigInfoItem{}}, {rpc.ClassMethodSensorInfo, tc.sensors, &[]rpc.ResultInfoItem{}}, } for _, classmethod := range classmethodTestCase { // Transfer tc to local variable for fixing schope classmethod := classmethod t.Run(fmt.Sprintf("%s - %s", tc.devType, classmethod.classMethod), func(t *testing.T) { testData := testenv.CreateTestData(t) registerCP3000Device(t, testData, tc.newFunc) response := testData.ReqRep(classmethod.classMethod) require.Nil(t, response.Error) require.Nil(t, response.Result.Unmarshal(classmethod.unmarshal)) switch data := classmethod.unmarshal.(type) { case *[]rpc.ResultConfigInfoItem: require.ElementsMatch(t, classmethod.expect, *data) case *[]rpc.ResultInfoItem: require.ElementsMatch(t, classmethod.expect, *data) } testData.Finish() }) } } } func TestNotSupportedOperations(t *testing.T) { testCases := []struct { newFunc createDeviceFunc devType rpc.DeviceType }{ { newFunc: newZkl3000, devType: rpc.DeviceTypeZKL3000, }, { newFunc: newZkl3000Rc, devType: rpc.DeviceTypeZKL3000RC, }, { newFunc: newZkl3000Rcc, devType: rpc.DeviceTypeZKL3000RCC, }, { newFunc: newCrtmGateway, devType: rpc.DeviceTypeCRTMGateway, }, { newFunc: newGreenHub, devType: rpc.DeviceTypeGRB3000, }, { newFunc: newCrtmSensor, devType: rpc.DeviceTypeCRTMSensor, }, } for _, tc := range testCases { // Transfer tc to local variable for fixing schope tc := tc t.Run(string(tc.devType), func(t *testing.T) { testData := testenv.CreateTestData(t) registerCP3000Device(t, testData, tc.newFunc) response := testData.ReqRep(rpc.ClassMethodActionInfo) require.NotNil(t, response.Error) testData = testenv.CreateTestData(t) registerCP3000Device(t, testData, tc.newFunc) response = testData.ReqRep(rpc.ClassMethodNotifyInfo) require.NotNil(t, response.Error) }) } }