Skip to content

Commit a6bcf2e

Browse files
committed
Test coverage push #3: 78% → 79%
- Add nameserver SetTTL/Reap + reapExpired test - Add standalone tasksubmit.Server tests (accept + reject) - Add CalculateTimeStaged/CalculateTimeCpu edge case tests - Remove dead code: sendKeyExchangeAuto, sendAuthKeyExchange, sendKeyExchange, SetPeerPubKey, closeFull, unregisterAcceptCh
1 parent b24b3ff commit a6bcf2e

6 files changed

Lines changed: 180 additions & 58 deletions

File tree

pkg/daemon/tunnel.go

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,6 @@ func (tm *TunnelManager) SetPeerVerifyFunc(fn func(uint32) (ed25519.PublicKey, e
199199
tm.mu.Unlock()
200200
}
201201

202-
// SetPeerPubKey caches a peer's Ed25519 public key for authentication.
203-
func (tm *TunnelManager) SetPeerPubKey(nodeID uint32, pubKey ed25519.PublicKey) {
204-
tm.mu.Lock()
205-
tm.peerPubKeys[nodeID] = pubKey
206-
tm.mu.Unlock()
207-
}
208-
209202
// SetBeaconAddr configures the beacon address for NAT hole-punching and relay.
210203
func (tm *TunnelManager) SetBeaconAddr(addr string) error {
211204
a, err := net.ResolveUDPAddr("udp", addr)
@@ -769,42 +762,6 @@ func (tm *TunnelManager) buildKeyExchangeFrame() []byte {
769762
return frame
770763
}
771764

772-
// sendKeyExchangeAuto sends an authenticated key exchange if identity is available,
773-
// otherwise falls back to unauthenticated. Uses addr-based direct send (for backward compat).
774-
func (tm *TunnelManager) sendKeyExchangeAuto(addr *net.UDPAddr) {
775-
tm.mu.RLock()
776-
hasIdentity := tm.identity != nil
777-
tm.mu.RUnlock()
778-
if hasIdentity {
779-
tm.sendAuthKeyExchange(addr)
780-
} else {
781-
tm.sendKeyExchange(addr)
782-
}
783-
}
784-
785-
// sendAuthKeyExchange sends our X25519 public key + Ed25519 signature to a peer (direct).
786-
func (tm *TunnelManager) sendAuthKeyExchange(addr *net.UDPAddr) {
787-
frame := tm.buildAuthKeyExchangeFrame()
788-
if frame == nil {
789-
tm.sendKeyExchange(addr)
790-
return
791-
}
792-
if _, err := tm.conn.WriteToUDP(frame, addr); err != nil {
793-
slog.Error("send auth key exchange failed", "addr", addr, "error", err)
794-
}
795-
}
796-
797-
// sendKeyExchange sends our public key to a peer (unauthenticated, direct).
798-
func (tm *TunnelManager) sendKeyExchange(addr *net.UDPAddr) {
799-
frame := tm.buildKeyExchangeFrame()
800-
if frame == nil {
801-
return
802-
}
803-
if _, err := tm.conn.WriteToUDP(frame, addr); err != nil {
804-
slog.Error("send key exchange failed", "addr", addr, "error", err)
805-
}
806-
}
807-
808765
// flushPending sends any queued packets for a peer now that encryption is ready.
809766
func (tm *TunnelManager) flushPending(nodeID uint32) {
810767
tm.pendMu.Lock()

pkg/driver/ipc.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -275,15 +275,6 @@ func (c *ipcClient) registerAcceptCh(port uint16) chan []byte {
275275
return ch
276276
}
277277

278-
func (c *ipcClient) unregisterAcceptCh(port uint16) {
279-
c.acceptMu.Lock()
280-
defer c.acceptMu.Unlock()
281-
if ch, ok := c.acceptChs[port]; ok {
282-
close(ch)
283-
delete(c.acceptChs, port)
284-
}
285-
}
286-
287278
func (c *ipcClient) registerRecvCh(connID uint32) chan []byte {
288279
ch := make(chan []byte, 256)
289280
c.recvMu.Lock()

pkg/driver/listener.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,6 @@ func (l *Listener) Close() error {
7272
return nil
7373
}
7474

75-
// closeFull closes the listener and unregisters the accept channel.
76-
func (l *Listener) closeFull() {
77-
l.Close()
78-
l.ipc.unregisterAcceptCh(l.port)
79-
}
80-
8175
func (l *Listener) Addr() net.Addr {
8276
return pilotAddr(protocol.SocketAddr{Port: l.port})
8377
}

pkg/nameserver/records.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,18 @@ func (rs *RecordStore) reapExpired() {
143143
}
144144
}
145145

146+
// SetTTL overrides the default record TTL.
147+
func (rs *RecordStore) SetTTL(d time.Duration) {
148+
rs.mu.Lock()
149+
rs.ttl = d
150+
rs.mu.Unlock()
151+
}
152+
153+
// Reap forces an immediate removal of expired records.
154+
func (rs *RecordStore) Reap() {
155+
rs.reapExpired()
156+
}
157+
146158
// SetStorePath enables persistence to the given file path and loads existing data.
147159
func (rs *RecordStore) SetStorePath(path string) {
148160
rs.mu.Lock()

tests/nameserver_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,52 @@ func TestNameserverMultipleClients(t *testing.T) {
309309
t.Logf("both clients resolved correctly: B=%s, C=%s", addrB, addrC)
310310
}
311311

312+
// TestNameserverReapExpired verifies that expired records are reaped.
313+
func TestNameserverReapExpired(t *testing.T) {
314+
t.Parallel()
315+
316+
store := nameserver.NewRecordStore()
317+
defer store.Close()
318+
319+
// Set TTL to zero so all records are immediately expired
320+
store.SetTTL(0)
321+
322+
// Register A, N, and S records
323+
store.RegisterA("reap-a", protocol.AddrZero)
324+
store.RegisterN("reap-n", 42)
325+
store.RegisterS("reap-s", protocol.AddrZero, 1, 7)
326+
327+
// Verify records exist before reap
328+
_, err := store.LookupA("reap-a")
329+
if err != nil {
330+
t.Fatalf("LookupA before reap: %v", err)
331+
}
332+
333+
// Force reap — all records should be removed (TTL=0)
334+
time.Sleep(time.Millisecond) // ensure time.Now() > CreatedAt
335+
store.Reap()
336+
337+
// Verify A record is gone
338+
_, err = store.LookupA("reap-a")
339+
if err == nil {
340+
t.Error("expected A record to be reaped")
341+
}
342+
343+
// Verify N record is gone
344+
_, err = store.LookupN("reap-n")
345+
if err == nil {
346+
t.Error("expected N record to be reaped")
347+
}
348+
349+
// Verify S record is gone
350+
entries := store.LookupS(1, 7)
351+
if len(entries) > 0 {
352+
t.Error("expected S record to be reaped")
353+
}
354+
355+
t.Log("all expired records reaped successfully")
356+
}
357+
312358
var _ = protocol.AddrZero // keep protocol import
313359
var _ = os.Remove // keep os import
314360
var _ = filepath.Join // keep filepath import

tests/tasksubmit_test.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tests
33
import (
44
"encoding/json"
55
"fmt"
6+
"net"
67
"os"
78
"testing"
89
"time"
@@ -1533,3 +1534,124 @@ func TestTaskResultsEndToEnd(t *testing.T) {
15331534
t.Errorf("expected result text, got %q", string(data))
15341535
}
15351536
}
1537+
1538+
// TestTaskSubmitServerStandalone tests the standalone tasksubmit.Server.
1539+
func TestTaskSubmitServerStandalone(t *testing.T) {
1540+
t.Parallel()
1541+
env := NewTestEnv(t)
1542+
1543+
// Daemon A with built-in task submit DISABLED — we'll use the standalone server
1544+
a := env.AddDaemon(func(c *daemon.Config) { c.DisableTaskSubmit = true })
1545+
b := env.AddDaemon()
1546+
1547+
// Start standalone tasksubmit.Server on daemon A
1548+
accepted := make(chan *tasksubmit.SubmitRequest, 1)
1549+
srv := tasksubmit.NewServer(a.Driver, func(conn net.Conn, req *tasksubmit.SubmitRequest) bool {
1550+
accepted <- req
1551+
return true
1552+
})
1553+
go srv.ListenAndServe()
1554+
time.Sleep(100 * time.Millisecond) // wait for listen
1555+
1556+
// B submits a task to A via the tasksubmit client
1557+
client, err := tasksubmit.Dial(b.Driver, a.Daemon.Addr())
1558+
if err != nil {
1559+
t.Fatalf("dial: %v", err)
1560+
}
1561+
defer client.Close()
1562+
1563+
resp, err := client.SubmitTask("standalone test task", a.Daemon.Addr().String())
1564+
if err != nil {
1565+
t.Fatalf("submit: %v", err)
1566+
}
1567+
if resp.Status != tasksubmit.StatusAccepted {
1568+
t.Errorf("expected accepted, got status %d", resp.Status)
1569+
}
1570+
t.Logf("response: status=%d message=%q", resp.Status, resp.Message)
1571+
1572+
// Verify handler received the request
1573+
select {
1574+
case req := <-accepted:
1575+
if req.TaskDescription != "standalone test task" {
1576+
t.Errorf("expected description, got %q", req.TaskDescription)
1577+
}
1578+
t.Logf("handler received: %q", req.TaskDescription)
1579+
case <-time.After(3 * time.Second):
1580+
t.Fatal("handler did not receive request")
1581+
}
1582+
}
1583+
1584+
// TestCalculateTimeStagedEdgeCases tests empty/invalid inputs for CalculateTimeStaged.
1585+
func TestCalculateTimeStagedEdgeCases(t *testing.T) {
1586+
t.Parallel()
1587+
1588+
// Empty StagedAt — should be a no-op
1589+
tf := &tasksubmit.TaskFile{TaskID: "calc-staged-empty"}
1590+
tf.CalculateTimeStaged()
1591+
if tf.ExecuteStartedAt != "" {
1592+
t.Errorf("expected empty ExecuteStartedAt for empty StagedAt, got %q", tf.ExecuteStartedAt)
1593+
}
1594+
if tf.TimeStagedMs != 0 {
1595+
t.Errorf("expected zero TimeStagedMs for empty StagedAt, got %d", tf.TimeStagedMs)
1596+
}
1597+
1598+
// Invalid StagedAt — should be a no-op
1599+
tf2 := &tasksubmit.TaskFile{TaskID: "calc-staged-invalid", StagedAt: "not-a-date"}
1600+
tf2.CalculateTimeStaged()
1601+
if tf2.ExecuteStartedAt != "" {
1602+
t.Error("expected empty ExecuteStartedAt for invalid StagedAt")
1603+
}
1604+
}
1605+
1606+
// TestCalculateTimeCpuEdgeCases tests empty/invalid inputs for CalculateTimeCpu.
1607+
func TestCalculateTimeCpuEdgeCases(t *testing.T) {
1608+
t.Parallel()
1609+
1610+
// Empty ExecuteStartedAt — should be a no-op
1611+
tf := &tasksubmit.TaskFile{TaskID: "calc-cpu-empty"}
1612+
tf.CalculateTimeCpu()
1613+
if tf.CompletedAt != "" {
1614+
t.Errorf("expected empty CompletedAt for empty ExecuteStartedAt, got %q", tf.CompletedAt)
1615+
}
1616+
if tf.TimeCpuMs != 0 {
1617+
t.Errorf("expected zero TimeCpuMs for empty ExecuteStartedAt, got %d", tf.TimeCpuMs)
1618+
}
1619+
1620+
// Invalid ExecuteStartedAt — should be a no-op
1621+
tf2 := &tasksubmit.TaskFile{TaskID: "calc-cpu-invalid", ExecuteStartedAt: "garbage"}
1622+
tf2.CalculateTimeCpu()
1623+
if tf2.CompletedAt != "" {
1624+
t.Error("expected empty CompletedAt for invalid ExecuteStartedAt")
1625+
}
1626+
}
1627+
1628+
// TestTaskSubmitServerReject tests the standalone server rejection path.
1629+
func TestTaskSubmitServerReject(t *testing.T) {
1630+
t.Parallel()
1631+
env := NewTestEnv(t)
1632+
1633+
a := env.AddDaemon(func(c *daemon.Config) { c.DisableTaskSubmit = true })
1634+
b := env.AddDaemon()
1635+
1636+
// Server that always rejects
1637+
srv := tasksubmit.NewServer(a.Driver, func(conn net.Conn, req *tasksubmit.SubmitRequest) bool {
1638+
return false
1639+
})
1640+
go srv.ListenAndServe()
1641+
time.Sleep(100 * time.Millisecond)
1642+
1643+
client, err := tasksubmit.Dial(b.Driver, a.Daemon.Addr())
1644+
if err != nil {
1645+
t.Fatalf("dial: %v", err)
1646+
}
1647+
defer client.Close()
1648+
1649+
resp, err := client.SubmitTask("reject me", a.Daemon.Addr().String())
1650+
if err != nil {
1651+
t.Fatalf("submit: %v", err)
1652+
}
1653+
if resp.Status != tasksubmit.StatusRejected {
1654+
t.Errorf("expected rejected (status %d), got %d", tasksubmit.StatusRejected, resp.Status)
1655+
}
1656+
t.Logf("rejection response: status=%d message=%q", resp.Status, resp.Message)
1657+
}

0 commit comments

Comments
 (0)