github.com/enbility/spine-go@v0.7.0/spine/helper_test.go (about) 1 package spine 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "sync" 8 "testing" 9 "time" 10 11 shipapi "github.com/enbility/ship-go/api" 12 "github.com/enbility/spine-go/api" 13 "github.com/enbility/spine-go/model" 14 "github.com/enbility/spine-go/util" 15 "github.com/google/go-cmp/cmp" 16 "github.com/google/go-cmp/cmp/cmpopts" 17 "github.com/stretchr/testify/assert" 18 ) 19 20 const ( 21 wallbox_detaileddiscoverydata_recv_reply_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_reply.json" 22 wallbox_detaileddiscoverydata_recv_notify_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_notify.json" 23 wallbox_detaileddiscoverydata_recv_notify_remove_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_notify_remove.json" 24 wallbox_detaileddiscoverydata_recv_reply_full_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_reply_full.json" 25 wallbox_detaileddiscoverydata_recv_notify_full_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_notify_full.json" 26 wallbox_detaileddiscoverydata_recv_notify_remove_full_file_path = "./testdata/wallbox_detaileddiscoverydata_recv_notify_remove_full.json" 27 ) 28 29 type WriteMessageHandler struct { 30 sentMessages [][]byte 31 32 mux sync.Mutex 33 } 34 35 var _ shipapi.ShipConnectionDataWriterInterface = (*WriteMessageHandler)(nil) 36 37 func (t *WriteMessageHandler) WriteShipMessageWithPayload(message []byte) { 38 t.mux.Lock() 39 defer t.mux.Unlock() 40 41 t.sentMessages = append(t.sentMessages, message) 42 } 43 44 func (t *WriteMessageHandler) LastMessage() []byte { 45 t.mux.Lock() 46 defer t.mux.Unlock() 47 48 if len(t.sentMessages) == 0 { 49 return nil 50 } 51 52 return t.sentMessages[len(t.sentMessages)-1] 53 } 54 55 func (t *WriteMessageHandler) MessageWithReference(msgCounterReference *model.MsgCounterType) []byte { 56 t.mux.Lock() 57 defer t.mux.Unlock() 58 59 var datagram model.Datagram 60 61 for _, msg := range t.sentMessages { 62 if err := json.Unmarshal(msg, &datagram); err != nil { 63 return nil 64 } 65 if datagram.Datagram.Header.MsgCounterReference == nil { 66 continue 67 } 68 if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { 69 continue 70 } 71 if datagram.Datagram.Payload.Cmd[0].ResultData != nil { 72 continue 73 } 74 75 return msg 76 } 77 78 return nil 79 } 80 81 func (t *WriteMessageHandler) ResultWithReference(msgCounterReference *model.MsgCounterType) []byte { 82 t.mux.Lock() 83 defer t.mux.Unlock() 84 85 var datagram model.Datagram 86 87 for _, msg := range t.sentMessages { 88 if err := json.Unmarshal(msg, &datagram); err != nil { 89 return nil 90 } 91 if datagram.Datagram.Header.MsgCounterReference == nil { 92 continue 93 } 94 if uint(*datagram.Datagram.Header.MsgCounterReference) != uint(*msgCounterReference) { 95 continue 96 } 97 if datagram.Datagram.Payload.Cmd[0].ResultData == nil { 98 continue 99 } 100 101 return msg 102 } 103 104 return nil 105 } 106 107 func loadFileData(t *testing.T, fileName string) []byte { 108 fileData, err := os.ReadFile(fileName) // #nosec G304 109 if err != nil { 110 t.Fatal(err) 111 } 112 113 return fileData 114 } 115 116 func checkSentData(t *testing.T, sendBytes []byte, msgSendFilePrefix string) { 117 msgSendExpectedBytes, err := os.ReadFile(msgSendFilePrefix + "_expected.json") // #nosec G304 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 msgSendActualFileName := msgSendFilePrefix + "_actual.json" 123 equal := jsonDatagramEqual(t, msgSendExpectedBytes, sendBytes) 124 if !equal { 125 saveJsonToFile(t, sendBytes, msgSendActualFileName) 126 } 127 assert.Truef(t, equal, "Assert equal failed! Check '%s' ", msgSendActualFileName) 128 } 129 130 func jsonDatagramEqual(t *testing.T, expectedJson, actualJson []byte) bool { 131 var actualDatagram model.Datagram 132 if err := json.Unmarshal(actualJson, &actualDatagram); err != nil { 133 t.Fatal(err) 134 } 135 var expectedDatagram model.Datagram 136 if err := json.Unmarshal(expectedJson, &expectedDatagram); err != nil { 137 t.Fatal(err) 138 } 139 140 less := func(a, b model.FunctionPropertyType) bool { return string(*a.Function) < string(*b.Function) } 141 return cmp.Equal(expectedDatagram, actualDatagram, cmpopts.SortSlices(less)) 142 } 143 144 func saveJsonToFile(t *testing.T, data json.RawMessage, fileName string) { 145 jsonIndent, err := json.MarshalIndent(data, "", " ") 146 if err != nil { 147 t.Fatal(err) 148 } 149 err = os.WriteFile(fileName, jsonIndent, os.ModePerm) 150 if err != nil { 151 t.Fatal(err) 152 } 153 } 154 155 func waitForAck(t *testing.T, msgCounterReference *model.MsgCounterType, writeHandler *WriteMessageHandler) { 156 var datagram model.Datagram 157 158 msg := writeHandler.ResultWithReference(msgCounterReference) 159 if msg == nil { 160 t.Fatal("acknowledge message was not sent!!") 161 } 162 163 if err := json.Unmarshal(msg, &datagram); err != nil { 164 t.Fatal(err) 165 } 166 167 cmd := datagram.Datagram.Payload.Cmd[0] 168 if cmd.ResultData != nil { 169 if cmd.ResultData.ErrorNumber != nil && uint(*cmd.ResultData.ErrorNumber) != uint(model.ErrorNumberTypeNoError) { 170 t.Fatal(fmt.Errorf("error '%d' result data received", uint(*cmd.ResultData.ErrorNumber))) 171 } 172 } 173 } 174 175 func createLocalDeviceAndEntity(entityId uint) (*DeviceLocal, *EntityLocal) { 176 localDevice := NewDeviceLocal("Vendor", "DeviceName", "SerialNumber", "DeviceCode", "Address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) 177 localDevice.address = util.Ptr(model.AddressDeviceType("Address")) 178 179 localEntity := NewEntityLocal(localDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}, time.Second*4) 180 localDevice.AddEntity(localEntity) 181 182 return localDevice, localEntity 183 } 184 185 func createLocalFeatures(localEntity *EntityLocal, featureType model.FeatureTypeType, writeFunction model.FunctionType) (api.FeatureLocalInterface, api.FeatureLocalInterface) { 186 localFeature := NewFeatureLocal(localEntity.NextFeatureId(), localEntity, featureType, model.RoleTypeClient) 187 localEntity.AddFeature(localFeature) 188 localServerFeature := NewFeatureLocal(localEntity.NextFeatureId(), localEntity, featureType, model.RoleTypeServer) 189 if len(string(writeFunction)) > 0 { 190 localServerFeature.AddFunctionType(writeFunction, true, true) 191 } 192 localEntity.AddFeature(localServerFeature) 193 194 return localFeature, localServerFeature 195 } 196 197 func createRemoteDevice(localDevice *DeviceLocal, ski string, sender api.SenderInterface) *DeviceRemote { 198 remoteDevice := NewDeviceRemote(localDevice, ski, sender) 199 remoteDevice.address = util.Ptr(model.AddressDeviceType(fmt.Sprintf("Address%s", ski))) 200 201 return remoteDevice 202 } 203 204 func createRemoteEntityAndFeature(remoteDevice *DeviceRemote, entityId uint, featureType model.FeatureTypeType, functionType model.FunctionType) (api.FeatureRemoteInterface, api.FeatureRemoteInterface) { 205 remoteEntity := NewEntityRemote(remoteDevice, model.EntityTypeTypeEVSE, []model.AddressEntityType{model.AddressEntityType(entityId)}) 206 remoteDevice.AddEntity(remoteEntity) 207 remoteFeature := NewFeatureRemote(remoteEntity.NextFeatureId(), remoteEntity, featureType, model.RoleTypeClient) 208 remoteEntity.AddFeature(remoteFeature) 209 remoteServerFeature := NewFeatureRemote(remoteEntity.NextFeatureId(), remoteEntity, featureType, model.RoleTypeServer) 210 remoteServerFeature.SetOperations([]model.FunctionPropertyType{ 211 { 212 Function: util.Ptr(functionType), 213 PossibleOperations: &model.PossibleOperationsType{ 214 Read: &model.PossibleOperationsReadType{ 215 Partial: &model.ElementTagType{}, 216 }, 217 }, 218 }, 219 }) 220 remoteEntity.AddFeature(remoteServerFeature) 221 222 return remoteFeature, remoteServerFeature 223 }