package tui import ( "os" "path/filepath" "strings" "testing" "github.com/BurntSushi/toml" ) // fullConfigValues returns a ConfigValues with every field populated with valid data. func fullConfigValues() ConfigValues { return ConfigValues{ Login: map[string]string{"user": "u", "password": "p"}, Server: map[string]string{ "port": "9090", "timeout": "45", "environment": "production", "seccomp_unconfined": "Não", }, Database: map[string]string{ "database_type": "postgres", "database_url": "postgres://user:pass@db:5432/app_dono_db", "max_conns": "20", "min_conns": "5", }, Cert: map[string]string{"cert_dir_path": "/etc/app-dono/certs"}, Application: map[string]string{ "central_server_url": "https://central.example.com:8443", "enrollment_token": "tok-123", }, Wireguard: map[string]string{ "privkey": "PRIVKEY_VALUE", "vip": "127.0.0.1", "psk": "PSK_VALUE", "proxy_edps": "22:127.0.0.1:22", "mtu": "1380", "proto": "UDP", }, } } func TestGenerateConfigTOML_RoundTrip(t *testing.T) { cv := fullConfigValues() out, err := GenerateConfigTOML(cv) if err != nil { t.Fatalf("GenerateConfigTOML returned error: %v", err) } var cfg AppConfig if _, err := toml.Decode(out, &cfg); err != nil { t.Fatalf("generated output is not valid TOML / failed to decode: %v\n---\n%s", err, out) } if cfg.Server.Timeout != 45 { t.Errorf("timeout_seconds = %d, want 45", cfg.Server.Timeout) } if cfg.Server.Environment != "production" { t.Errorf("environment = %q, want %q", cfg.Server.Environment, "production") } if cfg.Server.HostPort != 9090 { t.Errorf("host_port = %d, want 9090", cfg.Server.HostPort) } if cfg.Server.SeccompUnconfined { t.Errorf("seccomp_unconfined = true, want false") } if cfg.Database.Type != "postgres" { t.Errorf("database type = %q, want %q", cfg.Database.Type, "postgres") } if cfg.Database.URL != "postgres://user:pass@db:5432/app_dono_db" { t.Errorf("database url = %q", cfg.Database.URL) } if cfg.Database.MaxConns != 20 { t.Errorf("max_conns = %d, want 20", cfg.Database.MaxConns) } if cfg.Database.MinConns != 5 { t.Errorf("min_conns = %d, want 5", cfg.Database.MinConns) } if cfg.Certificates.DirPath != "/etc/app-dono/certs" { t.Errorf("mapped_dir = %q", cfg.Certificates.DirPath) } if cfg.Application.CentralServerURL != "https://central.example.com:8443" { t.Errorf("central_server_url = %q", cfg.Application.CentralServerURL) } if cfg.Application.EnrollmentToken != "tok-123" { t.Errorf("enrollment_token = %q", cfg.Application.EnrollmentToken) } } // The container always listens on containerAppPort internally; the user-provided // "Porta (host)" value is only the host-side mapping and must never leak into the // generated config.toml. This guard fails loudly if that invariant is reverted. func TestGenerateConfigTOML_PortIsAlwaysContainerPort(t *testing.T) { cv := fullConfigValues() cv.Server["port"] = "9090" // host port, must NOT appear as the config port out, err := GenerateConfigTOML(cv) if err != nil { t.Fatalf("GenerateConfigTOML returned error: %v", err) } var cfg AppConfig if _, err := toml.Decode(out, &cfg); err != nil { t.Fatalf("failed to decode: %v", err) } if cfg.Server.Port != containerAppPort { t.Errorf("config port = %d, want fixed containerAppPort %d", cfg.Server.Port, containerAppPort) } if cfg.Server.HostPort != 9090 { t.Errorf("host_port = %d, want 9090", cfg.Server.HostPort) } } func TestGenerateWireguardConfig(t *testing.T) { tests := []struct { name string wireguard map[string]string wantContains []string wantNotContns []string }{ { name: "all fields set", wireguard: map[string]string{ "privkey": "PK", "vip": "10.0.0.1", "psk": "PSK", "proxy_edps": "22:127.0.0.1:22", "mtu": "1400", "proto": "TCP", }, wantContains: []string{ "PRIVKEY=PK", "VIP=10.0.0.1", "PSK=PSK", "PROXY_EDPS=22:127.0.0.1:22", "MTU=1400", "PROTO=TCP", }, }, { name: "empty mtu is commented out", wireguard: map[string]string{ "privkey": "PK", "vip": "10.0.0.1", "psk": "PSK", "proxy_edps": "22:127.0.0.1:22", "mtu": "", "proto": "UDP", }, wantContains: []string{"# MTU=1380", "PROTO=UDP"}, wantNotContns: []string{"\nMTU="}, }, { name: "empty proto defaults to UDP", wireguard: map[string]string{ "privkey": "PK", "vip": "10.0.0.1", "psk": "PSK", "proxy_edps": "22:127.0.0.1:22", "proto": "", }, wantContains: []string{"PROTO=UDP"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { out, err := GenerateWireguardConfig(ConfigValues{Wireguard: tt.wireguard}) if err != nil { t.Fatalf("GenerateWireguardConfig returned error: %v", err) } for _, want := range tt.wantContains { if !strings.Contains(out, want) { t.Errorf("output missing %q:\n%s", want, out) } } for _, notWant := range tt.wantNotContns { if strings.Contains(out, notWant) { t.Errorf("output should not contain %q:\n%s", notWant, out) } } }) } } func TestWriteConfigFile_NumericValidation(t *testing.T) { numeric := []string{"port", "timeout", "max_conns", "min_conns"} for _, field := range numeric { t.Run("invalid_"+field, func(t *testing.T) { cv := fullConfigValues() switch field { case "port", "timeout": cv.Server[field] = "abc" case "max_conns", "min_conns": cv.Database[field] = "abc" } path := filepath.Join(t.TempDir(), "config.toml") err := WriteConfigFile(cv, path) if err == nil { t.Fatalf("expected error for non-numeric %q, got nil", field) } if !strings.Contains(err.Error(), field) { t.Errorf("error %q does not mention field %q", err.Error(), field) } if _, statErr := os.Stat(path); statErr == nil { t.Errorf("file should not have been written on validation failure") } }) } } func TestWriteConfigFile_ValidWritesFile(t *testing.T) { cv := fullConfigValues() path := filepath.Join(t.TempDir(), "config.toml") if err := WriteConfigFile(cv, path); err != nil { t.Fatalf("WriteConfigFile returned error: %v", err) } data, err := os.ReadFile(path) if err != nil { t.Fatalf("config file was not written: %v", err) } var cfg AppConfig if _, err := toml.Decode(string(data), &cfg); err != nil { t.Fatalf("written file is not valid TOML: %v", err) } } func TestWriteWireguardConfigFile_MTUValidation(t *testing.T) { tests := []struct { name string mtu string wantErr bool }{ {name: "valid mtu", mtu: "1380", wantErr: false}, {name: "empty mtu allowed", mtu: "", wantErr: false}, {name: "non-numeric mtu rejected", mtu: "big", wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cv := fullConfigValues() cv.Wireguard["mtu"] = tt.mtu path := filepath.Join(t.TempDir(), "envs") err := WriteWireguardConfigFile(cv, path) if tt.wantErr && err == nil { t.Fatalf("expected error for mtu %q, got nil", tt.mtu) } if !tt.wantErr && err != nil { t.Fatalf("unexpected error for mtu %q: %v", tt.mtu, err) } if !tt.wantErr { if _, statErr := os.Stat(path); statErr != nil { t.Errorf("envs file should have been written: %v", statErr) } } }) } }