Keep a later config flag when extra words are present.

A non-flag word no longer stops parsing, and a missing home directory refuses to create or rewrite a config instead of writing an empty log path.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-07 12:42:01 +02:00
co-authored by Cursor
parent 465a26911e
commit 87e366e36c
6 changed files with 317 additions and 14 deletions
+215
View File
@@ -3,11 +3,13 @@
package main
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
"encoding/json/v2"
)
func TestResolveConfigPathDefaultIsHome(t *testing.T) {
@@ -122,3 +124,216 @@ func TestOpenStationUsesResolvedPath(t *testing.T) {
t.Fatalf("callsign = %q, want DL0FN-1", st.State().Callsign)
}
}
func TestConfigEqualsFlagSelectsTheFile(t *testing.T) {
abs := filepath.Join(t.TempDir(), "lab.json")
got, err := resolveConfigPath([]string{"--config=" + abs})
if err != nil {
t.Fatal(err)
}
if got != abs {
t.Fatalf("--config= = %q, want %q", got, abs)
}
}
func TestCreatedFileViaFlagMatchesHomeTemplate(t *testing.T) {
path := filepath.Join(t.TempDir(), "lab.json")
var stderr bytes.Buffer
st, err := openStationReported([]string{"-c", path}, &stderr)
if st != nil {
t.Fatal("station = non-nil, want nil before the window")
}
if err == nil {
t.Fatal("error = nil, want the file created")
}
if got, want := err.Error(), "created "+path; got != want {
t.Fatalf("error = %q, want %q", got, want)
}
if got, want := stderr.String(), err.Error()+"\n"; got != want {
t.Fatalf("stderr = %q, want %q", got, want)
}
info, err := os.Stat(path)
if err != nil {
t.Fatal(err)
}
if got, want := info.Mode().Perm(), os.FileMode(0o600); got != want {
t.Fatalf("mode = %o, want %o", got, want)
}
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var got map[string]any
if err := json.Unmarshal(body, &got); err != nil {
t.Fatalf("created file is not JSON: %v\n%s", err, body)
}
if got["host"] != "" || got["callsign"] != "" || got["authKey"] != "" {
t.Fatalf("string fields = %#v, want empty host, callsign, and authKey", got)
}
if got["port"] != float64(0) || got["talkGroup"] != float64(0) {
t.Fatalf("numeric fields = %#v, want port and talkGroup at zero", got)
}
if got["enableLogging"] != false {
t.Fatalf("enableLogging = %#v, want false", got["enableLogging"])
}
home, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
if got["logFile"] != filepath.Join(home, "funknetz.log") {
t.Fatalf("logFile = %#v, want funknetz.log in the home directory", got["logFile"])
}
}
func TestLaterFlagSelectsTheFile(t *testing.T) {
dir := t.TempDir()
first := filepath.Join(dir, "first.json")
second := filepath.Join(dir, "second.json")
got, err := resolveConfigPath([]string{"-c", first, "--config", second})
if err != nil {
t.Fatal(err)
}
if got != second {
t.Fatalf("later --config = %q, want %q", got, second)
}
got, err = resolveConfigPath([]string{"--config", first, "-c", second})
if err != nil {
t.Fatal(err)
}
if got != second {
t.Fatalf("later -c = %q, want %q", got, second)
}
}
func TestEmptyConfigFlagUsesHomeFile(t *testing.T) {
home, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
want := filepath.Join(home, ".funknetz.json")
for _, args := range [][]string{
{"-c", ""},
{"--config", ""},
{"--config="},
} {
got, err := resolveConfigPath(args)
if err != nil {
t.Fatal(err)
}
if got != want {
t.Fatalf("resolveConfigPath(%q) = %q, want %q", args, got, want)
}
}
}
func TestExtraWordsDoNotChangeTheSelectedFile(t *testing.T) {
abs := filepath.Join(t.TempDir(), "lab.json")
got, err := resolveConfigPath([]string{"-c", abs, "leftover"})
if err != nil {
t.Fatal(err)
}
if got != abs {
t.Fatalf("with leftover = %q, want %q", got, abs)
}
home, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
got, err = resolveConfigPath([]string{"note"})
if err != nil {
t.Fatal(err)
}
want := filepath.Join(home, ".funknetz.json")
if got != want {
t.Fatalf("bare word = %q, want %q", got, want)
}
got, err = resolveConfigPath([]string{"note", "-c", abs})
if err != nil {
t.Fatal(err)
}
if got != abs {
t.Fatalf("word before -c = %q, want %q", got, abs)
}
second := filepath.Join(filepath.Dir(abs), "second.json")
got, err = resolveConfigPath([]string{"-c", abs, "leftover", "--config", second})
if err != nil {
t.Fatal(err)
}
if got != second {
t.Fatalf("flag after a word = %q, want %q", got, second)
}
}
func TestSelectedFileIsLeftByteForByte(t *testing.T) {
path := filepath.Join(t.TempDir(), "lab.json")
original := []byte("{\"host\":\"127.0.0.1\",\"port\":5308,\"callsign\":\"DL1ABC\",\"authKey\":\"a b\",\"talkGroup\":20,\"enableLogging\":false,\"logFile\":\"/tmp/funknetz.log\"}\n")
if err := os.WriteFile(path, original, 0o640); err != nil {
t.Fatal(err)
}
st, err := openStation([]string{"--config", path})
if err != nil {
t.Fatal(err)
}
if st.State().Callsign != "DL1ABC" {
t.Fatalf("callsign = %q, want DL1ABC", st.State().Callsign)
}
got, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
if string(got) != string(original) {
t.Fatalf("file = %q, want %q", got, original)
}
}
func TestEmptyHostOnSelectedFileNamesHost(t *testing.T) {
path := filepath.Join(t.TempDir(), "lab.json")
body := []byte("{\"host\":\"\",\"port\":5308,\"callsign\":\"DL1ABC\",\"authKey\":\"key\",\"talkGroup\":20,\"enableLogging\":false,\"logFile\":\"/tmp/funknetz.log\"}\n")
if err := os.WriteFile(path, body, 0o600); err != nil {
t.Fatal(err)
}
var stderr bytes.Buffer
st, err := openStationReported([]string{"-c", path}, &stderr)
if st != nil {
t.Fatal("station = non-nil, want nil")
}
if err == nil || !strings.Contains(err.Error(), "host") {
t.Fatalf("error = %v, want it to name host", err)
}
if stderr.String() != err.Error()+"\n" {
t.Fatalf("stderr = %q, want the error line", stderr.String())
}
}
func TestBadFlagPrintsParserError(t *testing.T) {
cases := []struct {
args []string
want string
}{
{[]string{"-not-a-flag"}, "flag provided but not defined: -not-a-flag\n"},
{[]string{"-c"}, "flag needs an argument: -c\n"},
{[]string{"--config"}, "flag needs an argument: -config\n"},
}
for _, tc := range cases {
var stderr bytes.Buffer
st, err := openStationReported(tc.args, &stderr)
if st != nil {
t.Fatal("station = non-nil, want nil")
}
if err == nil {
t.Fatal("error = nil, want a flag error")
}
if got := stderr.String(); got != tc.want {
t.Fatalf("stderr = %q, want %q", got, tc.want)
}
if strings.Contains(strings.ToLower(stderr.String()), "usage") {
t.Fatalf("stderr = %q, want no usage text", stderr.String())
}
}
}