]> git.giacomolli.uk - onehouse/blobdiff - driver/m31/fake_helper_test.go
Keep interrupted Modbus work a context stop
[onehouse] / driver / m31 / fake_helper_test.go
index c53dbac7b0a624b3c1017a7eac3a9a0b72fec95b..6b5a1d4d0129cf988d08d1b31730fd2645c1f97f 100644 (file)
@@ -18,27 +18,30 @@ import (
 const testDeviceID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"
 
 type fakeClient struct {
 const testDeviceID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"
 
 type fakeClient struct {
-       mu             sync.Mutex
-       inputs         []bool
-       coils          []bool
-       analog         []uint16
-       registers      map[uint16]uint16
-       writes         []coilWrite
-       registerWrites []uint16
-       attempts       []time.Time
-       closes         int
-       coilReads      int
-       connectErrors  []error
-       inputErr       error
-       readErr        error
-       writeErr       error
-       registerErr    error
-       closeErr       error
-       wrongConfig    bool
-       shortRead      bool
-       mismatch       int
-       writeDelay     time.Duration
-       inputDelay     time.Duration
+       registerReadErr error
+       wrongRegister   uint16
+       failAfterWrites int
+       mu              sync.Mutex
+       inputs          []bool
+       coils           []bool
+       analog          []uint16
+       registers       map[uint16]uint16
+       writes          []coilWrite
+       registerWrites  []uint16
+       attempts        []time.Time
+       closes          int
+       coilReads       int
+       connectErrors   []error
+       inputErr        error
+       readErr         error
+       writeErr        error
+       registerErr     error
+       closeErr        error
+       wrongConfig     bool
+       shortRead       bool
+       mismatch        int
+       writeDelay      time.Duration
+       inputDelay      time.Duration
 }
 
 func (c *fakeClient) Connect(ctx context.Context, _ string, _ byte) error {
 }
 
 func (c *fakeClient) Connect(ctx context.Context, _ string, _ byte) error {
@@ -98,13 +101,13 @@ func (c *fakeClient) ReadRegisters(ctx context.Context, address uint16, count ui
        c.mu.Lock()
        defer c.mu.Unlock()
 
        c.mu.Lock()
        defer c.mu.Unlock()
 
-       if c.registerErr != nil || ctx.Err() != nil {
-               return nil, errors.Join(c.registerErr, ctx.Err())
+       if c.registerErr != nil || c.registerReadErr != nil || ctx.Err() != nil {
+               return nil, errors.Join(c.registerErr, c.registerReadErr, ctx.Err())
        }
 
        if address >= pulseWidthBase {
                value := c.registers[address]
        }
 
        if address >= pulseWidthBase {
                value := c.registers[address]
-               if c.wrongConfig {
+               if c.wrongConfig || address == c.wrongRegister {
                        value++
                }
 
                        value++
                }
 
@@ -128,7 +131,7 @@ func (c *fakeClient) write(ctx context.Context, address uint16, values []bool, m
 
        time.Sleep(c.writeDelay)
 
 
        time.Sleep(c.writeDelay)
 
-       if c.writeErr != nil || ctx.Err() != nil {
+       if c.writeErr != nil && len(c.writes) >= c.failAfterWrites || ctx.Err() != nil {
                return errors.Join(c.writeErr, ctx.Err())
        }
 
                return errors.Join(c.writeErr, ctx.Err())
        }
 
@@ -182,6 +185,7 @@ type fakeTransport struct {
        mu       sync.Mutex
        messages []protocol.Message[any]
        failKind protocol.Kind
        mu       sync.Mutex
        messages []protocol.Message[any]
        failKind protocol.Kind
+       failOnce bool
        err      error
 }
 
        err      error
 }
 
@@ -195,7 +199,12 @@ func (f *fakeTransport) Publish(ctx context.Context, publication protocol.Public
        }
 
        if message.Address.Kind == f.failKind && f.err != nil {
        }
 
        if message.Address.Kind == f.failKind && f.err != nil {
-               return f.err
+               err = f.err
+               if f.failOnce {
+                       f.err = nil
+               }
+
+               return err
        }
 
        if ctx.Err() != nil {
        }
 
        if ctx.Err() != nil {
@@ -229,11 +238,14 @@ func (f *fakeTransport) all() []protocol.Message[any] {
        return slices.Clone(f.messages)
 }
 
        return slices.Clone(f.messages)
 }
 
-func testAssembly(t *testing.T) (*Driver, *fakeClient, *fakeTransport) {
+func testAssembly(t *testing.T) (*Driver, *fakeClient, *fakeTransport, *driver.Router) {
        t.Helper()
 
        transport := &fakeTransport{}
        publisher := driver.NewPublisher(transport)
        t.Helper()
 
        transport := &fakeTransport{}
        publisher := driver.NewPublisher(transport)
+       router, err := driver.NewRouter([]string{testDeviceID})
+       require.NoError(t, err)
+
        client := &fakeClient{
                inputs:    make([]bool, 16),
                coils:     make([]bool, 32),
        client := &fakeClient{
                inputs:    make([]bool, 16),
                coils:     make([]bool, 32),
@@ -253,16 +265,35 @@ func testAssembly(t *testing.T) (*Driver, *fakeClient, *fakeTransport) {
                        {Model: MixedModel},
                },
        }
                        {Model: MixedModel},
                },
        }
-       d, err := New(settings, publisher, client)
+       d, err := New(settings, publisher, router, client)
+       require.NoError(t, err)
+
+       return d, client, transport, router
+}
+
+func testCommand(t *testing.T, capability string, value string) driver.Command {
+       t.Helper()
+
+       id, err := protocol.NewID(time.Now())
        require.NoError(t, err)
 
        require.NoError(t, err)
 
-       return d, client, transport
+       expires := time.Now().UTC().Add(time.Minute)
+
+       return driver.Command{
+               DeviceID:   testDeviceID,
+               Capability: capability,
+               Metadata: protocol.Metadata{
+                       MessageID: id,
+                       ExpiresAt: &expires,
+               },
+               Payload: protocol.ValuePayload{Value: []byte(value)},
+       }
 }
 
 func testConnection(t *testing.T) (*connection, *fakeClient, *fakeTransport) {
        t.Helper()
 
 }
 
 func testConnection(t *testing.T) (*connection, *fakeClient, *fakeTransport) {
        t.Helper()
 
-       d, client, transport := testAssembly(t)
+       d, client, transport, _ := testAssembly(t)
        connection := &connection{owner: d}
        require.NoError(t, connection.start(t.Context()))
 
        connection := &connection{owner: d}
        require.NoError(t, connection.start(t.Context()))
 
@@ -272,3 +303,32 @@ func testConnection(t *testing.T) (*connection, *fakeClient, *fakeTransport) {
 
        return connection, client, transport
 }
 
        return connection, client, transport
 }
+
+func queueCommands(t *testing.T, router *driver.Router, commands []driver.Command) {
+       t.Helper()
+
+       var (
+               publication protocol.Publication
+               err         error
+       )
+
+       if len(commands) == 1 {
+               command := commands[0]
+               publication, err = protocol.BuildCommand(command.DeviceID, command.Capability, command.Metadata, command.Payload)
+       } else {
+               members := make([]protocol.BatchCommand, len(commands))
+
+               for index, command := range commands {
+                       members[index] = protocol.BatchCommand{
+                               Capability: command.Capability,
+                               Metadata:   command.Metadata,
+                               Payload:    command.Payload,
+                       }
+               }
+
+               publication, err = protocol.BuildCommandBatch(testDeviceID, commands[0].Metadata, protocol.BatchPayload{Commands: members})
+       }
+
+       require.NoError(t, err)
+       require.NoError(t, router.Handle(t.Context(), publication.Topic, publication.Data, false))
+}