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  }