Skip to content

Commit 4c0101f

Browse files
authored
Merge pull request #16 from Avicted/feature/voice-daemon
More tests
2 parents 8fc5d4d + 2da777c commit 4c0101f

36 files changed

Lines changed: 4786 additions & 27 deletions

.github/workflows/badge.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
fi
4848
color="red"
4949
if awk "BEGIN {exit !($total >= 80)}"; then
50-
color="brightgreen"
50+
color="green"
5151
elif awk "BEGIN {exit !($total >= 60)}"; then
5252
color="yellow"
5353
fi

cmd/client/chat_test.go

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,3 +1358,208 @@ func TestChatModelDispatchVoiceLeaveDoesNotClearRoomBeforeAck(t *testing.T) {
13581358
t.Fatalf("timed out waiting for leave command on ipc")
13591359
}
13601360
}
1361+
1362+
func TestWaitForVoiceMsg(t *testing.T) {
1363+
ch := make(chan ipc.Message, 1)
1364+
ch <- ipc.Message{Event: ipc.EventPong}
1365+
1366+
msg := waitForVoiceMsg(ch)()
1367+
voiceMsg, ok := msg.(voiceIPCMsg)
1368+
if !ok {
1369+
t.Fatalf("expected voiceIPCMsg, got %T", msg)
1370+
}
1371+
if ipc.Message(voiceMsg).Event != ipc.EventPong {
1372+
t.Fatalf("expected pong event, got %#v", ipc.Message(voiceMsg))
1373+
}
1374+
1375+
closed := make(chan ipc.Message)
1376+
close(closed)
1377+
msg = waitForVoiceMsg(closed)()
1378+
errMsg, ok := msg.(voiceIPCErrorMsg)
1379+
if !ok {
1380+
t.Fatalf("expected voiceIPCErrorMsg, got %T", msg)
1381+
}
1382+
if !strings.Contains(errMsg.err.Error(), "voice daemon disconnected") {
1383+
t.Fatalf("unexpected disconnect error: %v", errMsg.err)
1384+
}
1385+
}
1386+
1387+
func TestChatModelConnectVoiceIPC(t *testing.T) {
1388+
m := newChatForTest(t, &APIClient{serverURL: "http://server", httpClient: http.DefaultClient})
1389+
m.voiceIPC = nil
1390+
if cmd := m.connectVoiceIPC(); cmd != nil {
1391+
t.Fatalf("expected nil connect command when voice IPC is not configured")
1392+
}
1393+
1394+
m.voiceIPC = newVoiceIPC("")
1395+
cmd := m.connectVoiceIPC()
1396+
if cmd == nil {
1397+
t.Fatalf("expected connect command")
1398+
}
1399+
msg := cmd()
1400+
errMsg, ok := msg.(voiceIPCErrorMsg)
1401+
if !ok {
1402+
t.Fatalf("expected voiceIPCErrorMsg for invalid address, got %T", msg)
1403+
}
1404+
if !strings.Contains(errMsg.err.Error(), "voice ipc address is empty") {
1405+
t.Fatalf("unexpected connect error: %v", errMsg.err)
1406+
}
1407+
1408+
addr := filepath.Join(t.TempDir(), "voice.sock")
1409+
listener, err := ipc.Listen(addr)
1410+
if err != nil {
1411+
t.Fatalf("listen voice ipc: %v", err)
1412+
}
1413+
defer listener.Close()
1414+
go func() {
1415+
conn, err := listener.Accept()
1416+
if err != nil {
1417+
return
1418+
}
1419+
_ = conn.Close()
1420+
}()
1421+
1422+
m.voiceIPC = newVoiceIPC(addr)
1423+
cmd = m.connectVoiceIPC()
1424+
msg = cmd()
1425+
connected, ok := msg.(voiceIPCConnectedMsg)
1426+
if !ok {
1427+
t.Fatalf("expected voiceIPCConnectedMsg, got %T", msg)
1428+
}
1429+
if connected.ch == nil {
1430+
t.Fatalf("expected non-nil voice IPC channel")
1431+
}
1432+
}
1433+
1434+
func TestChatModelHandleVoiceCommandPaths(t *testing.T) {
1435+
m := newChatForTest(t, &APIClient{serverURL: "http://server", httpClient: http.DefaultClient})
1436+
if cmd := m.handleVoiceCommand("/voice", []string{"/voice"}); cmd != nil {
1437+
t.Fatalf("expected no command for usage help")
1438+
}
1439+
if !strings.Contains(lastSystemMessage(m), "voice commands") {
1440+
t.Fatalf("expected voice help message")
1441+
}
1442+
1443+
m.voiceIPC = nil
1444+
if cmd := m.handleVoiceCommand("/voice mute", []string{"/voice", "mute"}); cmd != nil {
1445+
t.Fatalf("expected no command when voice IPC is nil")
1446+
}
1447+
if !strings.Contains(lastSystemMessage(m), "voice daemon not configured") {
1448+
t.Fatalf("expected missing daemon message")
1449+
}
1450+
1451+
m.voiceIPC = &voiceIPC{addr: ""}
1452+
m.voiceRoom = "room-1"
1453+
_ = m.handleVoiceCommand("/voice leave", []string{"/voice", "leave"})
1454+
if !strings.Contains(lastSystemMessage(m), "voice leave failed") {
1455+
t.Fatalf("expected leave failure message")
1456+
}
1457+
_ = m.handleVoiceCommand("/voice mute", []string{"/voice", "mute"})
1458+
if !strings.Contains(lastSystemMessage(m), "voice mute failed") {
1459+
t.Fatalf("expected mute failure message")
1460+
}
1461+
_ = m.handleVoiceCommand("/voice unmute", []string{"/voice", "unmute"})
1462+
if !strings.Contains(lastSystemMessage(m), "voice unmute failed") {
1463+
t.Fatalf("expected unmute failure message")
1464+
}
1465+
1466+
addr := filepath.Join(t.TempDir(), "voice.sock")
1467+
listener, err := ipc.Listen(addr)
1468+
if err != nil {
1469+
t.Fatalf("listen voice ipc: %v", err)
1470+
}
1471+
defer listener.Close()
1472+
1473+
recv := make(chan ipc.Message, 1)
1474+
go func() {
1475+
conn, err := listener.Accept()
1476+
if err != nil {
1477+
return
1478+
}
1479+
defer conn.Close()
1480+
var msg ipc.Message
1481+
if err := ipc.NewDecoder(conn).Decode(&msg); err == nil {
1482+
recv <- msg
1483+
}
1484+
}()
1485+
1486+
m.voiceIPC = newVoiceIPC(addr)
1487+
m.voiceCh = make(chan ipc.Message, 1)
1488+
m.channels["ch-1"] = channelInfo{ID: "ch-1", Name: "general"}
1489+
m.activeChannel = "ch-1"
1490+
1491+
if cmd := m.handleVoiceCommand("/voice join", []string{"/voice", "join"}); cmd != nil {
1492+
t.Fatalf("expected no reconnect cmd when voice channel already connected")
1493+
}
1494+
if m.voiceRoom != "ch-1" {
1495+
t.Fatalf("expected voice room set to joined channel, got %q", m.voiceRoom)
1496+
}
1497+
if !strings.Contains(lastSystemMessage(m), "voice join requested") {
1498+
t.Fatalf("expected join notice")
1499+
}
1500+
1501+
select {
1502+
case sent := <-recv:
1503+
if sent.Cmd != ipc.CommandVoiceJoin || sent.Room != "ch-1" {
1504+
t.Fatalf("unexpected join IPC message: %#v", sent)
1505+
}
1506+
case <-time.After(2 * time.Second):
1507+
t.Fatalf("timeout waiting for join message")
1508+
}
1509+
}
1510+
1511+
func TestChatModelVoiceHelperMethods(t *testing.T) {
1512+
m := newChatForTest(t, &APIClient{serverURL: "http://server", httpClient: http.DefaultClient})
1513+
1514+
pending := ipc.Message{Cmd: ipc.CommandVoiceJoin, Room: "room-1"}
1515+
m.queueVoiceCommand(pending, "room-1", "queued")
1516+
m.clearPendingVoiceCommand()
1517+
if m.voicePendingCmd != nil || m.voicePendingRoom != "" || m.voicePendingNotice != "" {
1518+
t.Fatalf("expected pending voice command state to be cleared")
1519+
}
1520+
1521+
if cmd := m.scheduleVoicePing(); cmd == nil {
1522+
t.Fatalf("expected non-nil voice ping schedule command")
1523+
}
1524+
1525+
m.voiceMembers = nil
1526+
m.clearVoiceMembers()
1527+
if m.voiceMembers == nil {
1528+
t.Fatalf("expected voiceMembers map initialized")
1529+
}
1530+
m.voiceMembers["user-1"] = true
1531+
m.clearVoiceMembers()
1532+
if len(m.voiceMembers) != 0 {
1533+
t.Fatalf("expected voiceMembers map cleared")
1534+
}
1535+
}
1536+
1537+
func TestChatModelShareDirectoryKeyCmdGuards(t *testing.T) {
1538+
m := newChatForTest(t, &APIClient{serverURL: "http://server", httpClient: http.DefaultClient})
1539+
m.auth.IsTrusted = false
1540+
if cmd := m.shareDirectoryKeyCmd(); cmd != nil {
1541+
t.Fatalf("expected nil cmd when user is not trusted")
1542+
}
1543+
1544+
m.auth.IsTrusted = true
1545+
m.directoryKey = nil
1546+
if cmd := m.shareDirectoryKeyCmd(); cmd != nil {
1547+
t.Fatalf("expected nil cmd without directory key")
1548+
}
1549+
1550+
m.directoryKey = bytes.Repeat([]byte{7}, crypto.KeySize)
1551+
m.api = nil
1552+
m.kp = nil
1553+
cmd := m.shareDirectoryKeyCmd()
1554+
if cmd == nil {
1555+
t.Fatalf("expected cmd when trusted and directory key exists")
1556+
}
1557+
msg := cmd()
1558+
result, ok := msg.(shareDirectoryMsg)
1559+
if !ok {
1560+
t.Fatalf("expected shareDirectoryMsg, got %T", msg)
1561+
}
1562+
if result.err != nil {
1563+
t.Fatalf("expected nil error when shareDirectoryKey short-circuits missing deps, got %v", result.err)
1564+
}
1565+
}

cmd/client/login_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,11 @@ func TestLoginViewWithErrorsAndLoading(t *testing.T) {
146146
t.Fatalf("expected view")
147147
}
148148
}
149+
150+
func TestLoginConfirmPasswordGetter(t *testing.T) {
151+
m := newLoginModel("http://server")
152+
m.confirmInput.SetValue("secret-confirm")
153+
if got := m.confirmPassword(); got != "secret-confirm" {
154+
t.Fatalf("confirmPassword() = %q, want %q", got, "secret-confirm")
155+
}
156+
}

cmd/client/main_entry_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"os"
7+
"os/exec"
8+
"strings"
9+
"testing"
10+
)
11+
12+
func TestClientMainExitsOnRunError(t *testing.T) {
13+
if os.Getenv("DIALTONE_TEST_CLIENT_MAIN_HELPER") == "1" {
14+
os.Args = []string{"dialtone", "-voice-vad", "0"}
15+
main()
16+
os.Exit(0)
17+
}
18+
19+
cmd := exec.Command(os.Args[0], "-test.run=TestClientMainExitsOnRunError")
20+
cmd.Env = append(os.Environ(), "DIALTONE_TEST_CLIENT_MAIN_HELPER=1")
21+
var stderr bytes.Buffer
22+
cmd.Stderr = &stderr
23+
24+
err := cmd.Run()
25+
var exitErr *exec.ExitError
26+
if !errors.As(err, &exitErr) {
27+
t.Fatalf("expected subprocess exit error, got %v", err)
28+
}
29+
if exitErr.ExitCode() != 1 {
30+
t.Fatalf("expected exit code 1, got %d", exitErr.ExitCode())
31+
}
32+
if !strings.Contains(stderr.String(), "error: voice-vad must be > 0") {
33+
t.Fatalf("expected main stderr to include run error, got %q", stderr.String())
34+
}
35+
}

0 commit comments

Comments
 (0)