rs2322tcp/internal/config/config_test.go

705 lines
14 KiB
Go

/*
Package config_test contains tests for the rs2322tcp configuration package.
Project: rs2322tcp
Module: git.lang-dieter.de/rs2322tcp
*/
package config_test
import (
"encoding/json"
"git.lang-dieter.de/rs2322tcp/internal/config"
"os"
"path/filepath"
"strings"
"testing"
)
///////////////////////////////////////////////////////////////////////////////
// Server configuration
///////////////////////////////////////////////////////////////////////////////
func TestLoadServer(t *testing.T) {
dir := t.TempDir()
filename := filepath.Join(dir, "server.json")
data := `{
"listen": {
"address": "0.0.0.0",
"port": 5000
},
"devices": [
{
"id": "radio",
"name": "Funkgerät",
"serial_port": "/dev/ttyUSB0",
"baud_rate": 9600,
"data_bits": 8,
"parity": "none",
"stop_bits": 1
}
]
}`
if err := os.WriteFile(filename, []byte(data), 0644); err != nil {
t.Fatalf("write test configuration: %v", err)
}
cfg, err := config.LoadServer(filename)
if err != nil {
t.Fatalf("LoadServer() failed: %v", err)
}
if cfg.HardwareErrorResponse != config.DefaultHardwareErrorResponse {
t.Errorf(
"HardwareErrorResponse = %q, want %q",
cfg.HardwareErrorResponse,
config.DefaultHardwareErrorResponse,
)
}
if cfg.Listen.Address != "0.0.0.0" {
t.Errorf(
"Listen.Address = %q, want %q",
cfg.Listen.Address,
"0.0.0.0",
)
}
if cfg.Listen.Port != 5000 {
t.Errorf("Listen.Port = %d, want %d", cfg.Listen.Port, 5000)
}
if len(cfg.Devices) != 1 {
t.Fatalf("len(Devices) = %d, want 1", len(cfg.Devices))
}
device := cfg.Devices[0]
if device.ID != "radio" {
t.Errorf("Device.ID = %q, want %q", device.ID, "radio")
}
if device.SerialPort != "/dev/ttyUSB0" {
t.Errorf(
"Device.SerialPort = %q, want %q",
device.SerialPort,
"/dev/ttyUSB0",
)
}
if device.BaudRate != 9600 {
t.Errorf("Device.BaudRate = %d, want %d", device.BaudRate, 9600)
}
}
///////////////////////////////////////////////////////////////////////////////
// Client configuration
///////////////////////////////////////////////////////////////////////////////
func TestLoadClient(t *testing.T) {
dir := t.TempDir()
filename := filepath.Join(dir, "client.json")
data := `{
"server": {
"address": "100.64.0.10",
"port": 5000
},
"virtual_ports": [
{
"port": "COM7",
"remote_device": "radio"
}
]
}`
if err := os.WriteFile(filename, []byte(data), 0644); err != nil {
t.Fatalf("write test configuration: %v", err)
}
cfg, err := config.LoadClient(filename)
if err != nil {
t.Fatalf("LoadClient() failed: %v", err)
}
if cfg.Server.Address != "100.64.0.10" {
t.Errorf(
"Server.Address = %q, want %q",
cfg.Server.Address,
"100.64.0.10",
)
}
if cfg.Server.Port != 5000 {
t.Errorf("Server.Port = %d, want %d", cfg.Server.Port, 5000)
}
if cfg.VirtualPortRange.First != config.DefaultVirtualPortFirst {
t.Errorf(
"VirtualPortRange.First = %d, want %d",
cfg.VirtualPortRange.First,
config.DefaultVirtualPortFirst,
)
}
if cfg.VirtualPortRange.Last != config.DefaultVirtualPortLast {
t.Errorf(
"VirtualPortRange.Last = %d, want %d",
cfg.VirtualPortRange.Last,
config.DefaultVirtualPortLast,
)
}
if len(cfg.VirtualPorts) != 1 {
t.Fatalf(
"len(VirtualPorts) = %d, want 1",
len(cfg.VirtualPorts),
)
}
virtualPort := cfg.VirtualPorts[0]
if virtualPort.Port != "COM7" {
t.Errorf(
"VirtualPort.Port = %q, want %q",
virtualPort.Port,
"COM7",
)
}
if virtualPort.RemoteDevice != "radio" {
t.Errorf(
"VirtualPort.RemoteDevice = %q, want %q",
virtualPort.RemoteDevice,
"radio",
)
}
if virtualPort.StartupBytes != "" {
t.Errorf(
"VirtualPort.StartupBytes = %q, want empty",
virtualPort.StartupBytes,
)
}
}
func TestLoadClientWithStartupBytes(t *testing.T) {
dir := t.TempDir()
filename := filepath.Join(dir, "client.json")
data := `{
"server": {
"address": "100.64.0.10",
"port": 5000
},
"virtual_ports": [
{
"port": "COM7",
"remote_device": "radio",
"startup_bytes": "57 00 00 1F 20"
}
]
}`
if err := os.WriteFile(filename, []byte(data), 0644); err != nil {
t.Fatalf("write test configuration: %v", err)
}
cfg, err := config.LoadClient(filename)
if err != nil {
t.Fatalf("LoadClient() failed: %v", err)
}
if len(cfg.VirtualPorts) != 1 {
t.Fatalf(
"len(VirtualPorts) = %d, want 1",
len(cfg.VirtualPorts),
)
}
if cfg.VirtualPorts[0].StartupBytes != "57 00 00 1F 20" {
t.Errorf(
"VirtualPort.StartupBytes = %q, want %q",
cfg.VirtualPorts[0].StartupBytes,
"57 00 00 1F 20",
)
}
}
func TestLoadClientCustomVirtualPortRange(t *testing.T) {
dir := t.TempDir()
filename := filepath.Join(dir, "client.json")
data := `{
"server": {
"address": "100.64.0.10",
"port": 5000
},
"virtual_port_range": {
"first": 200,
"last": 299
},
"virtual_ports": []
}`
if err := os.WriteFile(filename, []byte(data), 0644); err != nil {
t.Fatalf("write test configuration: %v", err)
}
cfg, err := config.LoadClient(filename)
if err != nil {
t.Fatalf("LoadClient() failed: %v", err)
}
if cfg.VirtualPortRange.First != 200 {
t.Errorf(
"VirtualPortRange.First = %d, want 200",
cfg.VirtualPortRange.First,
)
}
if cfg.VirtualPortRange.Last != 299 {
t.Errorf(
"VirtualPortRange.Last = %d, want 299",
cfg.VirtualPortRange.Last,
)
}
}
///////////////////////////////////////////////////////////////////////////////
// Save and reload
///////////////////////////////////////////////////////////////////////////////
func TestSaveAndLoadClient(t *testing.T) {
dir := t.TempDir()
filename := filepath.Join(dir, "client.json")
original := &config.ClientConfig{
Server: config.ServerConnectionConfig{
Address: "100.64.0.10",
Port: 5000,
},
VirtualPortRange: config.VirtualPortRangeConfig{
First: 200,
Last: 299,
},
VirtualPorts: []config.VirtualPortConfig{
{
Port: "COM7",
RemoteDevice: "radio",
StartupBytes: "57 00 00 1F 20",
},
{
Port: "COM8",
RemoteDevice: "rotor",
},
},
}
if err := config.SaveClient(filename, original); err != nil {
t.Fatalf("SaveClient() failed: %v", err)
}
loaded, err := config.LoadClient(filename)
if err != nil {
t.Fatalf("LoadClient() failed: %v", err)
}
if loaded.Server != original.Server {
t.Errorf("loaded Server differs from original")
}
if loaded.VirtualPortRange != original.VirtualPortRange {
t.Errorf("loaded VirtualPortRange differs from original")
}
if len(loaded.VirtualPorts) != len(original.VirtualPorts) {
t.Fatalf(
"len(VirtualPorts) = %d, want %d",
len(loaded.VirtualPorts),
len(original.VirtualPorts),
)
}
for i := range original.VirtualPorts {
if loaded.VirtualPorts[i] != original.VirtualPorts[i] {
t.Errorf("VirtualPorts[%d] differs from original", i)
}
}
}
///////////////////////////////////////////////////////////////////////////////
// Validation
///////////////////////////////////////////////////////////////////////////////
func TestServerValidationDuplicateDeviceID(t *testing.T) {
cfg := &config.ServerConfig{
Listen: config.ListenConfig{
Port: 5000,
},
Devices: []config.DeviceConfig{
{
ID: "radio",
SerialPort: "/dev/ttyUSB0",
BaudRate: 9600,
DataBits: 8,
StopBits: 1,
},
{
ID: "radio",
SerialPort: "/dev/ttyUSB1",
BaudRate: 4800,
DataBits: 8,
StopBits: 1,
},
},
}
if err := cfg.Validate(); err == nil {
t.Fatal("Validate() succeeded, want duplicate ID error")
}
}
func TestClientValidationDuplicateVirtualPort(t *testing.T) {
cfg := &config.ClientConfig{
Server: config.ServerConnectionConfig{
Port: 5000,
},
VirtualPortRange: config.DefaultVirtualPortRange(),
VirtualPorts: []config.VirtualPortConfig{
{
Port: "COM7",
RemoteDevice: "radio",
},
{
Port: "COM7",
RemoteDevice: "rotor",
},
},
}
if err := cfg.Validate(); err == nil {
t.Fatal("Validate() succeeded, want duplicate virtual port error")
}
}
func TestServerValidationInvalidPort(t *testing.T) {
cfg := &config.ServerConfig{
Listen: config.ListenConfig{
Port: 70000,
},
}
if err := cfg.Validate(); err == nil {
t.Fatal("Validate() succeeded, want invalid port error")
}
}
func TestClientValidationInvalidPort(t *testing.T) {
cfg := &config.ClientConfig{
Server: config.ServerConnectionConfig{
Port: 0,
},
VirtualPortRange: config.DefaultVirtualPortRange(),
}
if err := cfg.Validate(); err == nil {
t.Fatal("Validate() succeeded, want invalid port error")
}
}
func TestClientValidationVirtualPortRangeBelowReservedArea(t *testing.T) {
cfg := &config.ClientConfig{
Server: config.ServerConnectionConfig{
Port: 5000,
},
VirtualPortRange: config.VirtualPortRangeConfig{
First: 99,
Last: 199,
},
}
if err := cfg.Validate(); err == nil {
t.Fatal("Validate() succeeded, want range-below-reserved-area error")
}
}
func TestClientValidationVirtualPortRangeReversed(t *testing.T) {
cfg := &config.ClientConfig{
Server: config.ServerConnectionConfig{
Port: 5000,
},
VirtualPortRange: config.VirtualPortRangeConfig{
First: 200,
Last: 100,
},
}
if err := cfg.Validate(); err == nil {
t.Fatal("Validate() succeeded, want reversed-range error")
}
}
func TestClientValidationVirtualPortRangeValid(t *testing.T) {
cfg := &config.ClientConfig{
Server: config.ServerConnectionConfig{
Port: 5000,
},
VirtualPortRange: config.VirtualPortRangeConfig{
First: 200,
Last: 299,
},
}
if err := cfg.Validate(); err != nil {
t.Fatalf("Validate() failed: %v", err)
}
}
///////////////////////////////////////////////////////////////////////////////
// Example configurations
///////////////////////////////////////////////////////////////////////////////
func TestExampleServerConfig(t *testing.T) {
cfg, err := config.LoadServer("../../configs/server.json")
if err != nil {
t.Fatalf("LoadServer() failed: %v", err)
}
if len(cfg.Devices) != 3 {
t.Fatalf("len(Devices) = %d, want 3", len(cfg.Devices))
}
}
func TestExampleClientConfig(t *testing.T) {
cfg, err := config.LoadClient("../../configs/client.json")
if err != nil {
t.Fatalf("LoadClient() failed: %v", err)
}
if len(cfg.VirtualPorts) != 1 {
t.Fatalf(
"len(VirtualPorts) = %d, want 1",
len(cfg.VirtualPorts),
)
}
port := cfg.VirtualPorts[0]
if port.Port != "COM100" {
t.Errorf(
"VirtualPorts[0].Port = %q, want %q",
port.Port,
"COM100",
)
}
if port.RemoteDevice != "rotor" {
t.Errorf(
"VirtualPorts[0].RemoteDevice = %q, want %q",
port.RemoteDevice,
"rotor",
)
}
}
///////////////////////////////////////////////////////////////////////////////
// Remote device
///////////////////////////////////////////////////////////////////////////////
func TestDeviceRemoteDevice(t *testing.T) {
device := config.DeviceConfig{
ID: "radio",
Name: "Funkgerät",
SerialPort: "/dev/ttyUSB0",
BaudRate: 9600,
DataBits: 8,
Parity: "none",
StopBits: 1,
}
remote := device.RemoteDevice()
if remote.ID != "radio" {
t.Errorf(
"RemoteDevice.ID = %q, want %q",
remote.ID,
"radio",
)
}
if remote.Name != "Funkgerät" {
t.Errorf(
"RemoteDevice.Name = %q, want %q",
remote.Name,
"Funkgerät",
)
}
if remote.BaudRate != 9600 {
t.Errorf(
"RemoteDevice.BaudRate = %d, want %d",
remote.BaudRate,
9600,
)
}
if remote.DataBits != 8 {
t.Errorf(
"RemoteDevice.DataBits = %d, want %d",
remote.DataBits,
8,
)
}
if remote.Parity != "none" {
t.Errorf(
"RemoteDevice.Parity = %q, want %q",
remote.Parity,
"none",
)
}
if remote.StopBits != 1 {
t.Errorf(
"RemoteDevice.StopBits = %d, want %d",
remote.StopBits,
1,
)
}
}
///////////////////////////////////////////////////////////////////////////////
// Remote device list
///////////////////////////////////////////////////////////////////////////////
func TestServerRemoteDevices(t *testing.T) {
cfg := &config.ServerConfig{
Devices: []config.DeviceConfig{
{
ID: "radio",
Name: "Funkgerät",
SerialPort: "/dev/ttyUSB0",
BaudRate: 9600,
DataBits: 8,
Parity: "none",
StopBits: 1,
},
{
ID: "rotor",
Name: "Antennenrotor",
SerialPort: "/dev/ttyUSB1",
BaudRate: 4800,
DataBits: 8,
Parity: "none",
StopBits: 1,
},
},
}
list := cfg.RemoteDevices()
if len(list.Devices) != 2 {
t.Fatalf("len(Devices) = %d, want 2", len(list.Devices))
}
if list.Devices[0].ID != "radio" {
t.Errorf(
"Devices[0].ID = %q, want %q",
list.Devices[0].ID,
"radio",
)
}
if list.Devices[1].ID != "rotor" {
t.Errorf(
"Devices[1].ID = %q, want %q",
list.Devices[1].ID,
"rotor",
)
}
if list.Devices[0].Name != "Funkgerät" {
t.Errorf(
"Devices[0].Name = %q, want %q",
list.Devices[0].Name,
"Funkgerät",
)
}
if list.Devices[0].BaudRate != 9600 {
t.Errorf(
"Devices[0].BaudRate = %d, want 9600",
list.Devices[0].BaudRate,
)
}
if list.Devices[1].BaudRate != 4800 {
t.Errorf(
"Devices[1].BaudRate = %d, want 4800",
list.Devices[1].BaudRate,
)
}
}
func TestServerRemoteDevicesNil(t *testing.T) {
var cfg *config.ServerConfig
list := cfg.RemoteDevices()
if len(list.Devices) != 0 {
t.Fatalf("len(Devices) = %d, want 0", len(list.Devices))
}
}
///////////////////////////////////////////////////////////////////////////////
// Remote device JSON
///////////////////////////////////////////////////////////////////////////////
func TestRemoteDeviceJSON(t *testing.T) {
list := config.DeviceList{
Devices: []config.RemoteDevice{
{
ID: "radio",
Name: "Funkgerät",
BaudRate: 9600,
DataBits: 8,
Parity: "none",
StopBits: 1,
},
},
}
data, err := json.Marshal(list)
if err != nil {
t.Fatalf("json.Marshal() failed: %v", err)
}
jsonText := string(data)
if strings.Contains(jsonText, "serial_port") {
t.Fatalf("JSON contains server-internal serial_port")
}
var decoded config.DeviceList
if err := json.Unmarshal(data, &decoded); err != nil {
t.Fatalf("json.Unmarshal() failed: %v", err)
}
if len(decoded.Devices) != 1 {
t.Fatalf(
"len(decoded.Devices) = %d, want 1",
len(decoded.Devices),
)
}
if decoded.Devices[0].ID != "radio" {
t.Errorf(
"Devices[0].ID = %q, want %q",
decoded.Devices[0].ID,
"radio",
)
}
}