package m31 import ( "encoding/json" "errors" "testing" "testing/synctest" "time" "github.com/stretchr/testify/require" "onehouse/protocol" ) func TestConnectionBaselineAndInputEvents(t *testing.T) { synctest.Test(t, func(t *testing.T) { c, client, transport := testConnection(t) require.Empty(t, transport.ofKind(protocol.Event)) client.inputs[0], client.inputs[15] = true, true client.inputDelay = time.Millisecond observedAt := time.Now().Add(time.Millisecond).UTC() require.NoError(t, c.poll(t.Context())) events := transport.ofKind(protocol.Event) require.Len(t, events, 2) for _, event := range events { require.Equal(t, &observedAt, event.Metadata.SentAt) require.JSONEq(t, `{"action":"pressed","value":true}`, eventJSON(t, event.Payload)) } require.NoError(t, c.poll(t.Context())) require.Len(t, transport.ofKind(protocol.Event), 2) client.inputs[0] = false require.NoError(t, c.poll(t.Context())) events = transport.ofKind(protocol.Event) require.Len(t, events, 3) require.JSONEq(t, `{"action":"released","value":false}`, eventJSON(t, events[2].Payload)) client.coils[2] = true transport.messages = nil require.NoError(t, c.poll(t.Context())) require.Empty(t, transport.messages) }) } func TestConnectionRejectsConfigurationAndReadFailuresBeforeOnline(t *testing.T) { testCases := []struct { name string change func(*fakeClient) }{ {name: "rejects pulse register write failure", change: func(c *fakeClient) { c.registerErr = errors.New("configuration failed") }}, {name: "rejects configuration mismatch", change: func(c *fakeClient) { c.wrongConfig = true }}, {name: "rejects input read failure", change: func(c *fakeClient) { c.inputErr = errors.New("input read failed") }}, {name: "rejects coil read failure", change: func(c *fakeClient) { c.readErr = errors.New("coil read failed") }}, {name: "rejects incomplete read", change: func(c *fakeClient) { c.shortRead = true }}, {name: "rejects unsafe voltage", change: func(c *fakeClient) { c.analog[0] = 10001 }}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { d, client, transport := testAssembly(t) tc.change(client) c := &connection{owner: d} err := c.start(t.Context()) var failure *deviceError require.ErrorAs(t, err, &failure) require.Empty(t, transport.messages) }) } } func TestConnectionReturnsPublicationFailures(t *testing.T) { testCases := []struct { name string kind protocol.Kind change func(*fakeClient) }{ {name: "returns input report failure", kind: protocol.Reported, change: func(c *fakeClient) { c.inputs[0] = true }}, {name: "returns transition failure", kind: protocol.Event, change: func(c *fakeClient) { c.inputs[0] = true }}, {name: "returns output report failure", kind: protocol.Reported, change: func(c *fakeClient) { c.coils[0] = true }}, {name: "returns voltage report failure", kind: protocol.Reported, change: func(c *fakeClient) { c.analog[0] = 5000 }}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { c, client, transport := testConnection(t) failure := errors.New("publish failed") transport.failKind, transport.err = tc.kind, failure tc.change(client) require.ErrorIs(t, c.poll(t.Context()), failure) }) } } func eventJSON(t *testing.T, value any) string { t.Helper() data, err := json.Marshal(value) require.NoError(t, err) return string(data) }