/* 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.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 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") } } /////////////////////////////////////////////////////////////////////////////// // 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, }, VirtualPorts: []config.VirtualPortConfig{ { Port: "COM7", RemoteDevice: "radio", }, { 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 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, }, 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, }, } if err := cfg.Validate(); err == nil { t.Fatal("Validate() succeeded, want invalid port error") } } /////////////////////////////////////////////////////////////////////////////// // 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) != 2 { t.Fatalf("len(Devices) = %d, want 2", 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) != 2 { t.Fatalf("len(VirtualPorts) = %d, want 2", len(cfg.VirtualPorts)) } } /////////////////////////////////////////////////////////////////////////////// // 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 %d", list.Devices[0].BaudRate, 9600) } if list.Devices[1].BaudRate != 4800 { t.Errorf("Devices[1].BaudRate = %d, want %d", list.Devices[1].BaudRate, 4800) } } 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(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") } }