package handlers_test import ( "fmt" "strings" "testing" "time" "github.com/fiskerinc/cloud-services/services/attendant/handlers" "github.com/fiskerinc/cloud-services/services/attendant/services" "os" "github.com/fiskerinc/cloud-services/pkg/common" "github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel" "github.com/fiskerinc/cloud-services/pkg/common/manifestfingerprintparams" dbm "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" "github.com/fiskerinc/cloud-services/pkg/grpc/sms" "github.com/fiskerinc/cloud-services/pkg/kafka" kafkaMock "github.com/fiskerinc/cloud-services/pkg/kafka/mock" "github.com/fiskerinc/cloud-services/pkg/redis" rm "github.com/fiskerinc/cloud-services/pkg/redis/tester" th "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/fiskerinc/cloud-services/pkg/testrunner" "github.com/fiskerinc/cloud-services/pkg/utils/elptr" vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig" "github.com/jinzhu/copier" ) func TestSendManifest(t *testing.T) { os.Setenv("APP_SERVICE_NAME", "ATTENDANT") handlers.PrivateSeed = 123456789 ka := services.NewKeepAwakeService() ka.SetService(&services.MockKeepAwakeImplementation{}) now := time.Now() testVIN := "JH4KA7680RC01" tboxKey := common.TRex.Key(testVIN) hmiKey := common.HMI.Key(testVIN) smsKey := "manifest_tbox_send_cache:100" var bhex common.BinaryHex = []byte("test") redis.MockRedisConnection() falsePtrValue := elptr.ElPtr(false) truePtrValue := elptr.ElPtr(true) ecuaRollback := []*common.UpdateManifestECU{ { ID: 100, UpdateManifestID: 200, ECU: "ECUA", Version: "VERSIONOLD", Mode: "A", DBModelBase: th.Timestamp, Files: []*common.UpdateManifestFile{ { FileID: "FILEIDOLD", UpdateManifestECUID: 1001, Filename: "FILENAMEOLD", URL: "URLOLD", FileType: common.Software, WriteRegionID: 700, WriteRegion: common.MemoryRegion{ Offset: 701, Length: 702, }, FileSize: 240, DBModelBase: th.Timestamp, }, }, }, } ecus := []*common.UpdateManifestECU{ { ECU: "ICC", Version: "SWVERSION", HWVersions: []string{"HWVERSION"}, Mode: "D", SelfDownload: true, InstallPriority: 13, Files: []*common.UpdateManifestFile{ { FileID: "AAAAAAA", URL: "http://download.com/file1.bin", FileSize: 1000, Checksum: "AAAAAAA", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Calibration, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, DBModelBase: th.Timestamp, }, { FileID: "SHOULD_NOT_BE_IN_UPDATE", URL: "http://download.com/SHOULD_NOT_BE_IN_UPDATE.bin", FileSize: 1000, Checksum: "AAAAAAA", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Calibration, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: falsePtrValue, DBModelBase: th.Timestamp, }, { FileID: "MUST_BE_IN_UPDATE", URL: "http://download.com/MUST_BE_IN_UPDATE.bin", FileSize: 1000, Checksum: "AAAAAAA", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Calibration, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: truePtrValue, DBModelBase: th.Timestamp, }, }, }, { ECU: "ECUD", Version: "SWVERSION", HWVersions: []string{"HWVERSION"}, Mode: "D", InstallPriority: 4, Files: []*common.UpdateManifestFile{ { FileID: "SHOULD_NOT_BE_IN_UPDATE", URL: "http://download.com/SHOULD_NOT_BE_IN_UPDATE.bin", FileSize: 1000, Checksum: "AAAAAAA", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Calibration, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: falsePtrValue, DBModelBase: th.Timestamp, }, { FileID: "MUST_BE_IN_UPDATE", URL: "http://download.com/MUST_BE_IN_UPDATE.bin", FileSize: 1000, Checksum: "AAAAAAA", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Calibration, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: truePtrValue, DBModelBase: th.Timestamp, }, { FileID: "BBBBBBB", URL: "http://download.com/file2.bin", FileSize: 2000, Checksum: "BBBBBBB", FileType: common.Calibration, WriteRegionID: 300, WriteRegion: common.MemoryRegion{ Offset: 301, Length: 302, }, EraseRegionID: 400, EraseRegion: &common.MemoryRegion{ Offset: 401, Length: 402, }, DBModelBase: th.Timestamp, }, }, DBModelBase: th.Timestamp, }, { ID: 100, UpdateManifestID: 200, ECU: "PDU", Version: "PDU-VERS", HWVersions: []string{"PDU-VERS"}, Mode: "PDU", ConfigurationMask: "AAAAAAAA", InstallPriority: 7, Files: []*common.UpdateManifestFile{ { FileID: "NOT_BE_IN_UPDATE", URL: "http://download.com/NOT_BE_IN_UPDATE.bin", FileSize: 1000, Checksum: "AAAAAAA", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Calibration, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: falsePtrValue, DBModelBase: th.Timestamp, }, { FileID: "MUST_BE_IN_UPDATE", URL: "http://download.com/MUST_BE_IN_UPDATE.bin", FileSize: 1000, Checksum: "AAAAAAA", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Calibration, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: truePtrValue, DBModelBase: th.Timestamp, }, { FileID: "AAAAAAAA", URL: "http://download.com/filea.bin", FileSize: 2000, Checksum: "AAAAAAAA", WriteRegionID: 500, FileType: common.Calibration, WriteRegion: common.MemoryRegion{ Offset: 501, Length: 502, }, DBModelBase: th.Timestamp, }, }, DBModelBase: th.Timestamp, }, { ID: 100, UpdateManifestID: 200, ECU: "ECUA", Version: "A-VERS", HWVersions: []string{"A-VERS"}, Mode: "A", ConfigurationMask: "AAAAAAAA", InstallPriority: 1, Files: []*common.UpdateManifestFile{ { FileID: "FILEID", UpdateManifestECUID: 100, Filename: "FILENAME", URL: "URL", FileType: common.Calibration, WriteRegionID: 600, WriteRegion: common.MemoryRegion{ Offset: 601, Length: 602, }, FileSize: 240, DBModelBase: th.Timestamp, }, }, DBModelBase: th.Timestamp, }, } fingerprint := getTodaysFingerprint() standardManifest := common.UpdateManifest{ ID: 100, CarUpdateID: 297, Name: "TEST", Version: "MANIFEST_VERSION", SUMS: "2023.10.01.00", Description: "TESTDESC", ReleaseNotes: "http://fiskerinc.com/release_notes", Type: common.ManifestTypeForced, Active: truePtrValue, Country: "US", PowerTrain: "MD23", Restraint: "None", Model: "Ocean", Trim: "Sport", Year: 2022, BodyType: "truck", RollbackEnabled: true, ECUs: ecus, UpdateDuration: 30, } rollbackDisabledManifest := common.UpdateManifest{ ID: 100, CarUpdateID: 297, Name: "TEST", Version: "MANIFEST_VERSION", SUMS: "2023.10.01.00.E", Description: "TESTDESC", ReleaseNotes: "http://fiskerinc.com/release_notes", Type: "standard", Active: truePtrValue, Country: "US", PowerTrain: "MD23", Restraint: "None", Model: "Ocean", Trim: "Sport", Year: 2022, BodyType: "truck", ECUs: ecus, UpdateDuration: 30, } standardManifestNoICC := common.UpdateManifest{ ID: 100, CarUpdateID: 297, Name: "TEST", Version: "MANIFEST_VERSION", SUMS: "2023.10.01.00.E", Description: "TESTDESC", ReleaseNotes: "http://fiskerinc.com/release_notes", Type: "standard", Country: "US", PowerTrain: "MD23", Restraint: "None", Model: "Ocean", Trim: "Sport", Year: 2022, RollbackEnabled: true, BodyType: "truck", ECUs: []*common.UpdateManifestECU{ { ECU: "ECUD", HWVersions: []string{"HWVERSION"}, Version: "SWVERSION", Mode: "D", InstallPriority: 7, Files: []*common.UpdateManifestFile{ { FileID: "BBBBBBB", URL: "http://download.com/file2.bin", FileSize: 2000, Checksum: "BBBBBBB", FileType: common.Calibration, WriteRegionID: 700, WriteRegion: common.MemoryRegion{ Offset: 701, Length: 702, }, DBModelBase: th.Timestamp, }, }, DBModelBase: th.Timestamp, }, { ID: 100, UpdateManifestID: 200, ECU: "PDU", Version: "PDU-VERS", HWVersions: []string{"PDU-VERS"}, Mode: "PDU", ConfigurationMask: "AAAAAAAA", InstallPriority: 10, Files: []*common.UpdateManifestFile{ { FileID: "AAAAAAAA", URL: "http://download.com/filea.bin", FileSize: 2000, FileType: common.Calibration, Checksum: "AAAAAAAA", WriteRegionID: 800, WriteRegion: common.MemoryRegion{ Offset: 801, Length: 802, }, EraseRegionID: 900, EraseRegion: &common.MemoryRegion{ Offset: 901, Length: 902, }, DBModelBase: th.Timestamp, }, }, DBModelBase: th.Timestamp, }, { ID: 100, UpdateManifestID: 200, ECU: "ECUA", Version: "A-VERS", HWVersions: []string{"A-VERS"}, Mode: "A", ConfigurationMask: "AAAAAAAA", InstallPriority: 4, Files: []*common.UpdateManifestFile{ { FileID: "FILEID", UpdateManifestECUID: 100, Filename: "FILENAME", URL: "URL", FileType: common.Calibration, WriteRegionID: 600, WriteRegion: common.MemoryRegion{ Offset: 601, Length: 602, }, FileSize: 240, DBModelBase: th.Timestamp, }, }, DBModelBase: th.Timestamp, }, }, UpdateDuration: 30, } mockCarUpdate := &common.CarUpdate{ ID: 297, VIN: testVIN, UpdateManifestID: 100, } mockCarECUs := []common.CarECU{ {ECU: "ECUD", HWVersion: "HWVERSION"}, {ECU: "ECUA", HWVersion: "A-VERS", Version: "TEST122"}, {ECU: "OBC", HWVersion: "PDU-VERS", Version: "TEST121"}, {ECU: "BCM", HWVersion: ";#A;#"}, {ECU: "ICC", HWVersion: "HWVERSION", Version: "TEST123"}, } mockRedis := rm.MockRedis{} mockCUDB := dbm.MockCarUpdates{} mockManDB := dbm.MockUpdateManifests{} mockKeys := dbm.MockEccKeys{ MockListResponse: []common.ECCKeys{ { ECU: "PDU", PrivKey1: &bhex, PrivKey2: &bhex, PrivKey3: &bhex, PubKey1: &bhex, PubKey2: &bhex, PubKey3: &bhex, Env: "current", DBModelBase: dbbasemodel.DBModelBase{ CreatedAt: &now, UpdatedAt: &now, }, }, { ECU: "ECUA", PrivKey1: &bhex, PrivKey2: &bhex, PrivKey3: &bhex, PubKey1: &bhex, PubKey2: &bhex, PubKey3: &bhex, Env: "current", DBModelBase: dbbasemodel.DBModelBase{ CreatedAt: &now, UpdatedAt: &now, }, }, { ECU: "ECUD", PrivKey1: &bhex, PrivKey2: &bhex, PrivKey3: &bhex, PubKey1: &bhex, PubKey2: &bhex, PubKey3: &bhex, Env: "current", DBModelBase: dbbasemodel.DBModelBase{ CreatedAt: &now, UpdatedAt: &now, }, }, }, } services.GetDB().SetCarUpdates(&mockCUDB) services.GetDB().SetCars(&dbm.MockCars{SelectCarECUs: mockCarECUs, SelectResponse: &common.Car{ICCID: "8000000000000000000"}}) services.GetDB().SetManifests(&mockManDB) services.GetDB().SetECCKeys(&mockKeys) services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis)) mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) { return common.SAPResponse{ ModelYear: 2023, ModelType: "Ocean", VersionDuringModelYear: "1", Features: []common.SAPFeature{ { FamilyCode: "FamilyCode1", FeatureCode: "FeatureCode1", }, { FamilyCode: "FamilyCode2", FeatureCode: "FeatureCode2", }, }, }, nil }} mockConf := vconfig.ConfigMock{GetVODCDSCodingDataMock: func(request common.VODCDSRequest) (map[string]string, error) { return map[string]string{ "EPS": "000147303031313632011D", "ESP": "000F47303031313632000001012301027600000101000002B0", "ECUA": "config", "VOD": "", "ADAS": "000147303031313632011D", }, nil }} mockSMS := &sms.SMSMock{} mockSMS.SetHandleSMSQueueResponse(&sms.SMSQueueResponse{ SmsMsgID: "100", SentSuccessful: true, }, nil) services.SetSapService(mockSap) services.SetVehicleConfig(mockConf) services.SetSmsClient(mockSMS) mockKafkaProducer := kafkaMock.GetKafkaMock(nil) services.SetKafkaProducer(mockKafkaProducer) clonedStandardManifest := common.UpdateManifest{} clonedStandardManifestNoICC := common.UpdateManifest{} err := copier.CopyWithOption(&clonedStandardManifest, &standardManifest, copier.Option{DeepCopy: true}) if err != nil { t.Fatal(err) } err = copier.CopyWithOption(&clonedStandardManifestNoICC, &standardManifestNoICC, copier.Option{DeepCopy: true}) if err != nil { t.Fatal(err) } tests := []testrunner.TestCase{ { Name: "Send manifest w Self Download", DBTestCase: &dbm.DBTestCase{ MockLoadResponse: mockCarUpdate, SetupMockResponse: func() { mockCUDB.LoadManifest = &clonedStandardManifest mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) { if man.ECU == "ECUA" { return ecuaRollback, nil } return nil, nil } }, }, RedisTestCase: &rm.RedisTestCase{ Device: common.Service, DeviceKey: kafka.OTAUpdateService, PayloadData: `{"car_update_id":297}`, ExpectedMessages: map[string]string{ hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":301,"length":302},"erase_region":{"offset":401,"length":402}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":501,"length":502}}]},{"name":"ICC","version":"SWVERSION","current_version":"TEST123","hw_version":"HWVERSION","self_download":true,"files":[{"file_id":"AAAAAAA","url":"http://download.com/file1.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"forced","vod":"01011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110100202310010011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100001e","update_duration":30}}`, }, ExpectedCaches: map[string]rm.ExpiringCacheResult{ // carupdateKey: { // Value: `{"current_size":0,"ecu":"","errorcode":0,"file_size":0,"file_total":0,"id":297,"info":"ICC","installed":0,"status":"sent","total_files":0,"total_size":0}`, // Expires: expectedExpire, // }, smsKey: { Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`, }, }, }, }, { Name: "Send manifest w Self Download rollback disabled", DBTestCase: &dbm.DBTestCase{ MockLoadResponse: mockCarUpdate, SetupMockResponse: func() { mockCUDB.LoadManifest = &rollbackDisabledManifest mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) { if man.ECU == "ECUA" { return ecuaRollback, nil } return nil, nil } }, }, RedisTestCase: &rm.RedisTestCase{ Device: common.Service, DeviceKey: kafka.OTAUpdateService, PayloadData: `{"car_update_id":297}`, ExpectedMessages: map[string]string{ hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":301,"length":302},"erase_region":{"offset":401,"length":402}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":501,"length":502}}]},{"name":"ICC","version":"SWVERSION","current_version":"TEST123","hw_version":"HWVERSION","self_download":true,"files":[{"file_id":"AAAAAAA","url":"http://download.com/file1.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":false,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`, }, ExpectedCaches: map[string]rm.ExpiringCacheResult{ smsKey: { Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`, }, }, }, }, { Name: "Bad message", RedisTestCase: &rm.RedisTestCase{ Device: common.HMI, DeviceKey: testVIN, PayloadData: `{"car_update_id":`, ExpectedError: "unexpected end of JSON input", }, }, { Name: "Send manifest w No Self Download", DBTestCase: &dbm.DBTestCase{ MockLoadResponse: mockCarUpdate, SetupMockResponse: func() { mockCUDB.LoadManifest = &clonedStandardManifestNoICC }, }, RedisTestCase: &rm.RedisTestCase{ Device: common.Service, DeviceKey: kafka.OTAUpdateService, PayloadData: `{"car_update_id":297}`, ExpectedMessages: map[string]string{ hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":701,"length":702}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":801,"length":802},"erase_region":{"offset":901,"length":902}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`, tboxKey: `{"handler":"update_manifest","data":{"ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","configuration":"config","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}],"rollback":[{"version":"VERSIONOLD","files":[{"file_id":"FILEIDOLD","url":"URLOLD","file_size":240,"type":"software","write_region":{"offset":701,"length":702}}]}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":701,"length":702}}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":801,"length":802},"erase_region":{"offset":901,"length":902}}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`, }, ExpectedCaches: map[string]rm.ExpiringCacheResult{ "manifest_tbox_send_cache:UQIDEPFQUH": { Value: `{"VIN":"JH4KA7680RC01","ManifestID":0,"Destination":"ICC/TBOX"}`, }, }, }, }, } schemaTesterHMI := th.NewSchemaTestHelper(t, schemaToHMI) schemaTesterTRex := th.NewSchemaTestHelper(t, schemaToTRex) for _, test := range tests { mockRedis.Reset() for _, ecu := range ecus { ecu.Rollback = nil } if test.DBTestCase != nil { test.DBTestCase.SetupDB(&mockCUDB) } if test.RedisTestCase != nil { test.RedisTestCase.SetupRedis(&mockRedis) err := handlers.SendManifest(services.GetDB(), ka, test.RedisTestCase.Device, test.RedisTestCase.DeviceKey, []byte(test.RedisTestCase.PayloadData)) test.RedisTestCase.CheckHandlerError(t, test.Name, err) test.RedisTestCase.Validate(t, test.Name, &mockRedis) } if test.DBTestCase != nil { test.DBTestCase.Validate(t, test.Name, &mockCUDB) } for key, m := range test.RedisTestCase.ExpectedMessages { name := fmt.Sprintf("%s %s", test.Name, key) if strings.Contains(key, "2:") { schemaTesterHMI.ValidateSchemaObject(name, []byte(m)) continue } schemaTesterTRex.ValidateSchemaObject(name, []byte(m)) } } } func TestSendManifestMultiFile(t *testing.T) { os.Setenv("APP_SERVICE_NAME", "ATTENDANT") testVIN := "JH4KA7680RC01" now := time.Now() hmiKey := common.HMI.Key(testVIN) falsePtrValue := elptr.ElPtr(false) truePtrValue := elptr.ElPtr(true) var bhex common.BinaryHex = []byte("test") redis.MockRedisConnection() ka := services.NewKeepAwakeService() ka.SetService(&services.MockKeepAwakeImplementation{}) ecus := []*common.UpdateManifestECU{ { ECU: "BCM", Version: "DB22121A", HWVersions: []string{";#A;#"}, Mode: "D", SelfDownload: true, Files: []*common.UpdateManifestFile{ { FileID: "ShouldNotSee", Filename: "notparsed.s19", URL: "fakeurl.com", Checksum: "shouldnotsee", Parsed: falsePtrValue, }, { FileID: "3ee5fd6bc9402d67", Filename: "MAGNA_BCM_FBL_driver.s19_0.bin", URL: "https://upload-dev.fiskerdps.com/4466d78a-50e1-4f1d-a4b8-5f78076758ed/MAGNA_BCM_FBL_driver.s19_0.bin", FileSize: 1000, Checksum: "shouldsee", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Bootloader, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, DBModelBase: th.Timestamp, Parsed: truePtrValue, }, { FileID: "7878ede9c9407779", Filename: "FM29_Application_FR40_220111.s19_0.bin", URL: "ttps://upload-dev.fiskerdps.com/b5c3c8c8-86cd-422e-aa11-a248697edfa3/FM29_Application_FR40_220111.s19_0.bin", FileSize: 1000, Checksum: "SHOULD NOT SEE", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Software, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: truePtrValue, Signature: "ShouldNOtSee", DBModelBase: th.Timestamp, }, { FileID: "7bb5083dc9407ae9", Filename: "FM29_Application_FR40_220111.s19_1.bin", URL: "https://upload-dev.fiskerdps.com/2cdeb566-cb00-42cd-93a8-69769b19269a/FM29_Application_FR40_220111.s19_1.bin", FileSize: 1000, Checksum: "shantsee", WriteRegionID: 100, WriteRegion: common.MemoryRegion{ Offset: 101, Length: 102, }, FileType: common.Software, EraseRegionID: 200, EraseRegion: &common.MemoryRegion{ Offset: 201, Length: 202, }, Parsed: truePtrValue, DBModelBase: th.Timestamp, Signature: "SHOULD NOT SEE", }, { FileID: "7ebb4fadc9409b91", Filename: "FM29_Application_FR40_220111.s19_2.bin", URL: "https://upload-dev.fiskerdps.com/ec9f8a84-61fb-4a9b-9735-ac6f91b07a83/FM29_Application_FR40_220111.s19_2.bin", FileType: common.Software, Parsed: truePtrValue, Signature: "SomeSignatureYouShouldSee", }, }, }, } fingerprint := getTodaysFingerprint() standardManifest := common.UpdateManifest{ ID: 100, CarUpdateID: 297, Name: "TEST", Version: "MANIFEST_VERSION", SUMS: "2023.10.01.00", Description: "TESTDESC", ReleaseNotes: "http://fiskerinc.com/release_notes", Type: "standard", Active: truePtrValue, Country: "US", PowerTrain: "MD23", Restraint: "None", Model: "Ocean", Trim: "Sport", Year: 2022, BodyType: "truck", RollbackEnabled: false, ECUs: ecus, UpdateDuration: 30, } mockCarUpdate := &common.CarUpdate{ ID: 297, VIN: testVIN, UpdateManifestID: 100, } mockCarECUs := []common.CarECU{ {ECU: "ECUD", HWVersion: "HWVERSION"}, {ECU: "ECUA", HWVersion: "A-VERS"}, {ECU: "OBC", HWVersion: "PDU-VERS"}, {ECU: "BCM", HWVersion: ";#A;#"}, {ECU: "ICC", HWVersion: "HWVERSION"}, } mockRedis := rm.MockRedis{} mockCUDB := dbm.MockCarUpdates{} mockManDB := dbm.MockUpdateManifests{} mockKeys := dbm.MockEccKeys{ MockListResponse: []common.ECCKeys{ { ECU: "BCM", PrivKey1: &bhex, PrivKey2: &bhex, PrivKey3: &bhex, PubKey1: &bhex, PubKey2: &bhex, PubKey3: &bhex, Env: "current", DBModelBase: dbbasemodel.DBModelBase{ CreatedAt: &now, UpdatedAt: &now, }, }, }, } services.GetDB().SetCarUpdates(&mockCUDB) services.GetDB().SetCars(&dbm.MockCars{SelectCarECUs: mockCarECUs, SelectResponse: &common.Car{ICCID: ""}}) services.GetDB().SetManifests(&mockManDB) services.GetDB().SetECCKeys(&mockKeys) services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis)) mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) { return common.SAPResponse{ ModelYear: 2023, ModelType: "Ocean", VersionDuringModelYear: "1", Features: []common.SAPFeature{ { FamilyCode: "FamilyCode1", FeatureCode: "FeatureCode1", }, { FamilyCode: "FamilyCode2", FeatureCode: "FeatureCode2", }, }, }, nil }} mockConf := vconfig.ConfigMock{GetVODCDSCodingDataMock: func(request common.VODCDSRequest) (map[string]string, error) { return map[string]string{ "ECUA": "config", "VOD": "", }, nil }} mockSMS := &sms.SMSMock{} mockSMS.SetHandleSMSQueueResponse(&sms.SMSQueueResponse{ SmsMsgID: "100", SentSuccessful: true, }, nil) services.SetSmsClient(mockSMS) services.SetSapService(mockSap) services.SetVehicleConfig(mockConf) smsKey := "manifest_tbox_send_cache:100" tests := []testrunner.TestCase{ { Name: "MultiFile right order", DBTestCase: &dbm.DBTestCase{ MockLoadResponse: mockCarUpdate, SetupMockResponse: func() { mockCUDB.LoadManifest = &standardManifest mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) { if man.ECU == "ECUA" { return nil, nil } return nil, nil } }, }, RedisTestCase: &rm.RedisTestCase{ Device: common.Service, DeviceKey: kafka.OTAUpdateService, PayloadData: `{"car_update_id":297}`, ExpectedMessages: map[string]string{ hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"BCM","version":"DB22121A","hw_version":";#A;#","self_download":true,"files":[{"file_id":"3ee5fd6bc9402d67","url":"https://upload-dev.fiskerdps.com/4466d78a-50e1-4f1d-a4b8-5f78076758ed/MAGNA_BCM_FBL_driver.s19_0.bin","file_size":1000,"checksum":"shouldsee","type":"bootloader","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7878ede9c9407779","url":"ttps://upload-dev.fiskerdps.com/b5c3c8c8-86cd-422e-aa11-a248697edfa3/FM29_Application_FR40_220111.s19_0.bin","file_size":1000,"type":"software","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7bb5083dc9407ae9","url":"https://upload-dev.fiskerdps.com/2cdeb566-cb00-42cd-93a8-69769b19269a/FM29_Application_FR40_220111.s19_1.bin","file_size":1000,"type":"software","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7ebb4fadc9409b91","url":"https://upload-dev.fiskerdps.com/ec9f8a84-61fb-4a9b-9735-ac6f91b07a83/FM29_Application_FR40_220111.s19_2.bin","type":"software","write_region":{"offset":0,"length":0},"signature":"SomeSignatureYouShouldSee"}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":false,"type":"standard","vod":"01011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110100202310010011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100001e","update_duration":30}}`, }, ExpectedCaches: map[string]rm.ExpiringCacheResult{smsKey: { Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`, }}, }, }, } for _, test := range tests { mockRedis.Reset() for _, ecu := range ecus { ecu.Rollback = nil } if test.DBTestCase != nil { test.DBTestCase.SetupDB(&mockCUDB) } if test.RedisTestCase != nil { test.RedisTestCase.SetupRedis(&mockRedis) err := handlers.SendManifest(services.GetDB(), ka, test.RedisTestCase.Device, test.RedisTestCase.DeviceKey, []byte(test.RedisTestCase.PayloadData)) test.RedisTestCase.CheckHandlerError(t, test.Name, err) test.RedisTestCase.Validate(t, test.Name, &mockRedis) } if test.DBTestCase != nil { test.DBTestCase.Validate(t, test.Name, &mockCUDB) } } } func TestSendManifestIntegration(t *testing.T) { os.Setenv("APP_SERVICE_NAME", "ATTENDANT") ka := services.NewKeepAwakeService() ka.SetService(&services.MockKeepAwakeImplementation{}) err := handlers.SendManifest(services.GetDB(), ka, common.Service, "", []byte(`{"car_update_id":297}`)) if err != nil { t.Error(err) } } func getTodaysFingerprint() string { // generate today's fingerprint fpparams := manifestfingerprintparams.GetFPParams() var fm = common.UpdateManifest{} fm.GenerateFingerprint(fpparams.CurTime(), fpparams.ManifestSerial()) return fm.Fingerprint }