Files

101 lines
3.4 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package speech
import (
"context"
"net"
"path/filepath"
"strconv"
"testing"
)
func TestResolveServerAddressPrecedence(t *testing.T) {
runtimeDirectory := t.TempDir()
cacheDirectory := t.TempDir()
t.Setenv("XDG_RUNTIME_DIR", runtimeDirectory)
t.Setenv("XDG_CACHE_HOME", cacheDirectory)
t.Setenv("SPEECHD_SOCKET", "/tmp/legacy-speech.sock")
t.Setenv("SPEECHD_ADDRESS", "unix_socket:/tmp/address-speech.sock")
for _, test := range []struct {
name, override, speechAddress, legacySocket, runtimeDir, want string
}{
{"explicit", "/tmp/explicit-speech.sock", "unix_socket:/tmp/address-speech.sock", "/tmp/legacy-speech.sock", runtimeDirectory, "/tmp/explicit-speech.sock"},
{"address", "", "unix_socket:/tmp/address-speech.sock", "/tmp/legacy-speech.sock", runtimeDirectory, "/tmp/address-speech.sock"},
{"legacy", "", "", "/tmp/legacy-speech.sock", runtimeDirectory, "/tmp/legacy-speech.sock"},
{"runtime", "", "", "", runtimeDirectory, filepath.Join(runtimeDirectory, "speech-dispatcher", "speechd.sock")},
{"cache", "", "", "", "", filepath.Join(cacheDirectory, "speech-dispatcher", "speechd.sock")},
} {
t.Run(test.name, func(t *testing.T) {
t.Setenv("SPEECHD_ADDRESS", test.speechAddress)
t.Setenv("SPEECHD_SOCKET", test.legacySocket)
t.Setenv("XDG_RUNTIME_DIR", test.runtimeDir)
got, err := resolveServerAddress(test.override)
if err != nil || got.network != "unix" || got.address != test.want {
t.Fatalf("address = %+v, error = %v; want %q", got, err, test.want)
}
})
}
}
func TestParseServerAddress(t *testing.T) {
for _, test := range []struct {
input, network, address string
}{
{"unix_socket:/tmp/speech.sock", "unix", "/tmp/speech.sock"},
{"inet_socket", "tcp4", "127.0.0.1:6560"},
{"inet_socket:localhost:6561", "tcp4", "localhost:6561"},
} {
got, err := parseServerAddress(test.input)
if err != nil || got.network != test.network || got.address != test.address {
t.Fatalf("parse %q = %+v, %v", test.input, got, err)
}
}
for _, address := range []string{"invalid:/tmp/speech.sock", "unix_socket:relative.sock", "inet_socket:localhost:0", "inet_socket:localhost:bad"} {
if _, err := parseServerAddress(address); err == nil {
t.Fatalf("accepted invalid address %q", address)
}
}
}
func TestClientAndDiscoveryHonorSpeechdAddress(t *testing.T) {
path, _ := newSettingsServer(t, func(command string) string {
if command == "LIST OUTPUT_MODULES" {
return "200-test\r\n200 OK\r\n"
}
return ""
})
t.Setenv("SPEECHD_ADDRESS", "unix_socket:"+path)
t.Setenv("SPEECHD_SOCKET", "/tmp/absent-speech.sock")
client := New("")
defer client.Close(context.Background())
if err := client.Connect(context.Background()); err != nil {
t.Fatal(err)
}
if _, err := Discover(context.Background(), ""); err != nil {
t.Fatal(err)
}
}
func TestTCPAddressConnects(t *testing.T) {
listener, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
server := newSettingsServerListener(t, listener, nil)
port := listener.Addr().(*net.TCPAddr).Port
t.Setenv("SPEECHD_ADDRESS", "inet_socket:127.0.0.1:"+strconv.Itoa(port))
client := New("")
defer client.Close(context.Background())
if err := client.Connect(context.Background()); err != nil {
t.Fatal(err)
}
if !server.saw(1, "HISTORY GET CLIENT_ID") {
t.Fatal("speech client did not use the TCP server")
}
}