Replace Python launcher with native Go UI
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseHostPort(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantAddress string
|
||||
wantPort int
|
||||
wantValid bool
|
||||
}{
|
||||
{name: "hostname", input: "example.com", wantAddress: "example.com", wantPort: 64738, wantValid: true},
|
||||
{name: "hostname and port", input: "example.com:64739", wantAddress: "example.com", wantPort: 64739, wantValid: true},
|
||||
{name: "bracketed IPv6", input: "[2001:db8::1]:64740", wantAddress: "2001:db8::1", wantPort: 64740, wantValid: true},
|
||||
{name: "bracketed IPv6 default port", input: "[2001:db8::1]", wantAddress: "2001:db8::1", wantPort: 64738, wantValid: true},
|
||||
{name: "unbracketed IPv6", input: "2001:db8::1", wantValid: false},
|
||||
{name: "invalid port", input: "example.com:70000", wantValid: false},
|
||||
{name: "non-numeric port", input: "example.com:abc", wantValid: false},
|
||||
{name: "empty", input: "", wantValid: false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
address, port, valid := parse_host_port(test.input)
|
||||
if valid != test.wantValid || address != test.wantAddress || port != test.wantPort {
|
||||
t.Fatalf("parse_host_port(%q) = %q, %d, %t; want %q, %d, %t",
|
||||
test.input, address, port, valid, test.wantAddress, test.wantPort, test.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServerInputPasswordShorthand(t *testing.T) {
|
||||
address, port, password, valid := parse_server_input("secret@example.com:64739")
|
||||
if !valid || address != "example.com" || port != 64739 || password != "secret" {
|
||||
t.Fatalf("unexpected parse result: %q, %d, %q, %t", address, port, password, valid)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveAndLoadServersRoundTrip(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "barnard", "servers.conf")
|
||||
want := map[string]Server{
|
||||
"Example": {
|
||||
Name: "Example",
|
||||
Address: "example.com",
|
||||
Port: 64739,
|
||||
Password: " secret value ",
|
||||
Insecure: true,
|
||||
},
|
||||
}
|
||||
if err := save_servers(path, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if info.Mode().Perm() != 0600 {
|
||||
t.Fatalf("server file mode = %o; want 600", info.Mode().Perm())
|
||||
}
|
||||
got, warnings, err := load_servers(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(warnings) != 0 {
|
||||
t.Fatalf("unexpected warnings: %v", warnings)
|
||||
}
|
||||
if got["Example"] != want["Example"] {
|
||||
t.Fatalf("loaded server = %#v; want %#v", got["Example"], want["Example"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadServersStartsEmptyWhenFileIsMissing(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "servers.conf")
|
||||
servers, warnings, err := load_servers(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(servers) != 0 {
|
||||
t.Fatalf("new configuration contains %d default servers; want none", len(servers))
|
||||
}
|
||||
if len(warnings) != 0 {
|
||||
t.Fatalf("unexpected warnings: %v", warnings)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveServersCreatesBackup(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "servers.conf")
|
||||
if err := os.WriteFile(path, []byte("old contents\n"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path+".bak", []byte("older contents\n"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := save_servers(path, map[string]Server{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backup, err := os.ReadFile(path + ".bak")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(backup) != "old contents\n" {
|
||||
t.Fatalf("backup = %q; want old contents", backup)
|
||||
}
|
||||
info, err := os.Stat(path + ".bak")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if info.Mode().Perm() != 0600 {
|
||||
t.Fatalf("backup mode = %o; want 600", info.Mode().Perm())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadServersRejectsUnknownKeys(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "servers.conf")
|
||||
contents := "[server]\nname = Example\naddress = example.com\nmystery = value\n"
|
||||
if err := os.WriteFile(path, []byte(contents), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, _, err := load_servers(path)
|
||||
if err == nil || !strings.Contains(err.Error(), "unknown key") {
|
||||
t.Fatalf("load_servers error = %v; want unknown key", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), "value") {
|
||||
t.Fatalf("load_servers exposed configuration value: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadServersRedactsMalformedLines(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "servers.conf")
|
||||
secret := "private-password-without-an-equals-sign"
|
||||
contents := "[server]\nname = Example\naddress = example.com\n" + secret + "\n"
|
||||
if err := os.WriteFile(path, []byte(contents), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, _, err := load_servers(path)
|
||||
if err == nil {
|
||||
t.Fatal("load_servers accepted a malformed line")
|
||||
}
|
||||
if strings.Contains(err.Error(), secret) {
|
||||
t.Fatalf("load_servers exposed malformed configuration content: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigHasValue(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), ".barnard.toml")
|
||||
contents := "# username = ignored\nUsername = \"Example User\"\nCertificate = \"\"\n"
|
||||
if err := os.WriteFile(path, []byte(contents), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !config_has_value(path, "username") {
|
||||
t.Fatal("expected username to be present")
|
||||
}
|
||||
if config_has_value(path, "certificate") {
|
||||
t.Fatal("empty certificate should not count as present")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatAddress(t *testing.T) {
|
||||
if got := format_address("example.com", 64738); got != "example.com:64738" {
|
||||
t.Fatalf("format_address hostname = %q", got)
|
||||
}
|
||||
if got := format_address("2001:db8::1", 64738); got != "[2001:db8::1]:64738" {
|
||||
t.Fatalf("format_address IPv6 = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultPathsAcceptsIsolatedConfigDirectory(t *testing.T) {
|
||||
configDir := filepath.Join(t.TempDir(), "isolated")
|
||||
paths, err := default_paths(configDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if paths.ConfigDir != configDir {
|
||||
t.Fatalf("config directory = %q; want %q", paths.ConfigDir, configDir)
|
||||
}
|
||||
if paths.ServerFile != filepath.Join(configDir, "servers.conf") {
|
||||
t.Fatalf("server file = %q", paths.ServerFile)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectionArgsNeverExposePassword(t *testing.T) {
|
||||
server := Server{Name: "Private", Address: "example.com", Port: 64738, Password: "do not expose"}
|
||||
args, err := connection_args(server, Paths{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(strings.Join(args, " "), server.Password) {
|
||||
t.Fatalf("connection arguments expose password: %v", args)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProgramIdentityAndAboutMenuUseBarnardUI(t *testing.T) {
|
||||
if programName != "barnard-ui" {
|
||||
t.Fatalf("programName = %q", programName)
|
||||
}
|
||||
options := main_menu_options()
|
||||
if !slices.Contains(options, "About barnard-ui") {
|
||||
t.Fatalf("main menu lacks About barnard-ui: %v", options)
|
||||
}
|
||||
for _, option := range options {
|
||||
if strings.Contains(option, "go-ui") {
|
||||
t.Fatalf("legacy go-ui name remains in main menu: %q", option)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user