From 3c6c50c914f00753f64db7569765bc8216c03c5d Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Mon, 2 Mar 2026 14:01:47 -0600 Subject: [PATCH] add tests for midi and mqtt message create processors --- .../test/midi-message-create_test.go | 259 ++++++++++++++++++ .../test/mqtt-message-create_test.go | 208 ++++++++++++++ 2 files changed, 467 insertions(+) diff --git a/internal/processor/test/midi-message-create_test.go b/internal/processor/test/midi-message-create_test.go index 5c036b1..33e855d 100644 --- a/internal/processor/test/midi-message-create_test.go +++ b/internal/processor/test/midi-message-create_test.go @@ -1,10 +1,12 @@ package processor_test import ( + "reflect" "testing" "github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/processor" + "gitlab.com/gomidi/midi/v2" ) func TestMIDIMessageCreateFromRegistry(t *testing.T) { @@ -31,3 +33,260 @@ func TestMIDIMessageCreateFromRegistry(t *testing.T) { t.Fatalf("midi.message.create processor has wrong type: %s", processorInstance.Type()) } } + +func TestGoodMIDIMessageCreate(t *testing.T) { + + tests := []struct { + name string + params map[string]any + payload any + expected any + }{ + { + name: "note_on message", + params: map[string]any{ + "type": "note_on", + "channel": "1", + "note": "60", + "velocity": "100", + }, + payload: "test", + expected: midi.NoteOn(1, 60, 100), + }, + { + name: "note_off message", + params: map[string]any{ + "type": "note_off", + "channel": "1", + "note": "60", + "velocity": "100", + }, + payload: "test", + expected: midi.NoteOffVelocity(1, 60, 100), + }, + { + name: "control_change message", + params: map[string]any{ + "type": "control_change", + "channel": "1", + "control": "64", + "value": "127", + }, + payload: "test", + expected: midi.ControlChange(1, 64, 127), + }, + { + name: "program_change message", + params: map[string]any{ + "type": "program_change", + "channel": "1", + "program": "10", + }, + payload: "test", + expected: midi.ProgramChange(1, 10), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + registration, ok := processor.ProcessorRegistry["midi.message.create"] + if !ok { + t.Fatalf("midi.message.create processor not registered") + } + + processorInstance, err := registration.New(config.ProcessorConfig{ + Type: "midi.message.create", + Params: test.params, + }) + + if err != nil { + t.Fatalf("midi.message.create failed to create processor: %s", err) + } + + got, err := processorInstance.Process(t.Context(), test.payload) + + gotMessage, ok := got.(midi.Message) + if !ok { + t.Fatalf("midi.message.create returned a %T payload: %s", got, got) + } + if err != nil { + t.Fatalf("midi.message.create failed: %s", err) + } + if !reflect.DeepEqual(gotMessage, test.expected) { + t.Fatalf("midi.message.create got %v, expected %v", gotMessage, test.expected) + } + }) + } +} + +func TestBadMIDIMessageCreate(t *testing.T) { + + tests := []struct { + name string + params map[string]any + payload any + errorString string + }{ + { + name: "no type parameter", + params: map[string]any{}, + payload: "test", + errorString: "midi.message.create type error: not found", + }, + { + name: "non-string type parameter", + params: map[string]any{ + "type": 1, + }, + payload: "test", + errorString: "midi.message.create type error: not a string", + }, + { + name: "unknown type parameter", + params: map[string]any{ + "type": "asdf", + }, + payload: "test", + errorString: "midi.message.create does not support type asdf", + }, + { + name: "note_on message no channel", + params: map[string]any{ + "type": "note_on", + "note": "60", + "velocity": "100", + }, + payload: "test", + errorString: "midi.message.create channel error: not found", + }, + { + name: "note_on message no note", + params: map[string]any{ + "type": "note_on", + "channel": "1", + "velocity": "100", + }, + payload: "test", + errorString: "midi.message.create note error: not found", + }, + { + name: "note_on message no velocity", + params: map[string]any{ + "type": "note_on", + "channel": "1", + "note": "60", + }, + payload: "test", + errorString: "midi.message.create velocity error: not found", + }, + { + name: "note_off message no channel", + params: map[string]any{ + "type": "note_off", + "note": "60", + "velocity": "100", + }, + payload: "test", + errorString: "midi.message.create channel error: not found", + }, + { + name: "note_off message no note", + params: map[string]any{ + "type": "note_off", + "channel": "1", + "velocity": "100", + }, + payload: "test", + errorString: "midi.message.create note error: not found", + }, + { + name: "note_off message no velocity", + params: map[string]any{ + "type": "note_off", + "channel": "1", + "note": "60", + }, + payload: "test", + errorString: "midi.message.create velocity error: not found", + }, + { + name: "control_change no channel", + params: map[string]any{ + "type": "control_change", + "control": "64", + "value": "127", + }, + payload: "test", + errorString: "midi.message.create channel error: not found", + }, + { + name: "control_change no control", + params: map[string]any{ + "type": "control_change", + "channel": "1", + "value": "127", + }, + payload: "test", + errorString: "midi.message.create control error: not found", + }, + { + name: "control_change no value", + params: map[string]any{ + "type": "control_change", + "channel": "1", + "control": "64", + }, + payload: "test", + errorString: "midi.message.create value error: not found", + }, + { + name: "program_change no channel", + params: map[string]any{ + "type": "program_change", + "program": "64", + }, + payload: "test", + errorString: "midi.message.create channel error: not found", + }, + { + name: "program_change no program", + params: map[string]any{ + "type": "program_change", + "channel": "1", + }, + payload: "test", + errorString: "midi.message.create program error: not found", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + registration, ok := processor.ProcessorRegistry["midi.message.create"] + if !ok { + t.Fatalf("midi.message.create processor not registered") + } + + processorInstance, err := registration.New(config.ProcessorConfig{ + Type: "midi.message.create", + Params: test.params, + }) + + if err != nil { + if test.errorString != err.Error() { + t.Fatalf("string.create got error '%s', expected '%s'", err.Error(), test.errorString) + } + return + } + + got, err := processorInstance.Process(t.Context(), test.payload) + + if err == nil { + t.Fatalf("midi.message.create expected to fail but succeeded, got: %v", got) + } + + if err.Error() != test.errorString { + t.Fatalf("midi.message.create got error '%s', expected '%s'", err.Error(), test.errorString) + } + }) + } +} diff --git a/internal/processor/test/mqtt-message-create_test.go b/internal/processor/test/mqtt-message-create_test.go index 30c1b8d..cf57bed 100644 --- a/internal/processor/test/mqtt-message-create_test.go +++ b/internal/processor/test/mqtt-message-create_test.go @@ -1,8 +1,10 @@ package processor_test import ( + "reflect" "testing" + mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/processor" ) @@ -31,3 +33,209 @@ func TestMQTTMessageCreateFromRegistry(t *testing.T) { t.Fatalf("mqtt.message.create processor has wrong type: %s", processorInstance.Type()) } } + +func TestGoodMQTTMessageCreate(t *testing.T) { + tests := []struct { + name string + payload any + params map[string]any + expected any + }{ + { + name: "basic topic and string payload", + params: map[string]any{ + "topic": "test/topic", + "payload": "Hello, World!", + "qos": 1, + "retained": true, + }, + payload: "test", + expected: processor.NewMQTTMessage("test/topic", 1, true, []byte("Hello, World!")), + }, + { + name: "basic topic and []byte payload", + params: map[string]any{ + "topic": "test/topic", + "payload": []byte{72, 101, 108, 108, 111}, + "qos": 1, + "retained": true, + }, + payload: "test", + expected: processor.NewMQTTMessage("test/topic", 1, true, []byte("Hello")), + }, + { + name: "basic topic and []int payload", + params: map[string]any{ + "topic": "test/topic", + "payload": []int{72, 101, 108, 108, 111}, + "qos": 1, + "retained": true, + }, + payload: "test", + expected: processor.NewMQTTMessage("test/topic", 1, true, []byte("Hello")), + }, + { + name: "basic topic and []uint payload", + params: map[string]any{ + "topic": "test/topic", + "payload": []uint{72, 101, 108, 108, 111}, + "qos": 1, + "retained": true, + }, + payload: "test", + expected: processor.NewMQTTMessage("test/topic", 1, true, []byte("Hello")), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + registration, ok := processor.ProcessorRegistry["mqtt.message.create"] + if !ok { + t.Fatalf("mqtt.message.create processor not registered") + } + + processorInstance, err := registration.New(config.ProcessorConfig{ + Type: "mqtt.message.create", + Params: test.params, + }) + + if err != nil { + t.Fatalf("mqtt.message.create failed to create processor: %s", err) + } + + got, err := processorInstance.Process(t.Context(), test.payload) + + if err != nil { + t.Fatalf("mqtt.message.create process failed: %s", err) + } + + if test.expected == nil { + if got != nil { + t.Fatalf("mqtt.message.create got %+v, expected nil", got) + } + return + } + + gotMessage, ok := got.(mqtt.Message) + if !ok { + t.Fatalf("mqtt.message.create returned a %T payload: %s", got, got) + } + + if !reflect.DeepEqual(gotMessage, test.expected) { + t.Fatalf("mqtt.message.create got %+v, expected %+v", gotMessage, test.expected) + } + }) + } +} + +func TestBadMQTTMessageCreate(t *testing.T) { + tests := []struct { + name string + params map[string]any + payload any + errorString string + }{ + { + name: "no topic parameter", + params: map[string]any{}, + payload: "test", + errorString: "mqtt.message.create topic error: not found", + }, + { + name: "non-string topic parameter", + params: map[string]any{ + "topic": 1, + }, + payload: "test", + errorString: "mqtt.message.create topic error: not a string", + }, + { + name: "no qos parameter", + params: map[string]any{ + "topic": "test/topic", + }, + payload: "test", + errorString: "mqtt.message.create qos error: not found", + }, + { + name: "non-number qos parameter", + params: map[string]any{ + "topic": "test/topic", + "qos": "1", + }, + payload: "test", + errorString: "mqtt.message.create qos error: not a number", + }, + { + name: "no retained parameter", + params: map[string]any{ + "topic": "test/topic", + "qos": 1, + }, + payload: "test", + errorString: "mqtt.message.create retained error: not found", + }, + { + name: "non-bool retained parameter", + params: map[string]any{ + "topic": "test/topic", + "qos": 1, + "retained": "1", + }, + payload: "test", + errorString: "mqtt.message.create retained error: not a boolean", + }, + { + name: "no payload parameter", + params: map[string]any{ + "topic": "test/topic", + "qos": 1, + "retained": true, + }, + payload: "test", + errorString: "mqtt.message.create payload error: not found", + }, + { + name: "non-string payload parameter", + params: map[string]any{ + "topic": "test/topic", + "qos": 1, + "retained": true, + "payload": 123, + }, + payload: 1, + errorString: "mqtt.message.create payload error: not a slice", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + registration, ok := processor.ProcessorRegistry["mqtt.message.create"] + if !ok { + t.Fatalf("mqtt.message.create processor not registered") + } + + processorInstance, err := registration.New(config.ProcessorConfig{ + Type: "mqtt.message.create", + Params: test.params, + }) + + if err != nil { + if test.errorString != err.Error() { + t.Fatalf("mqtt.message.create got error '%s', expected '%s'", err.Error(), test.errorString) + } + return + } + + got, err := processorInstance.Process(t.Context(), test.payload) + + if err == nil { + t.Fatalf("mqtt.message.create expected to fail but succeeded, got: %v", got) + } + + if err.Error() != test.errorString { + t.Fatalf("mqtt.message.create got error '%s', expected '%s'", err.Error(), test.errorString) + } + }) + } +}