mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-15 08:42:47 -04:00
feat(tailscale): add Tailscale control center widget (#1875)
* feat(tailscale): add Tailscale control center widget Full-stack Tailscale integration for DMS control center: Backend (Go): - Event-driven manager via WatchIPNBus (no polling) - Reconnects with exponential backoff when tailscaled unavailable - Typed conversion from ipnstate.Status to QML-friendly IPC types - Testable via tailscaleClient interface with mock watcher - Manager cleanup in cleanupManagers() - 19 unit tests Frontend (QML): - TailscaleService with WebSocket subscription - TailscaleWidget with peer list, filter chips, search - Copy-to-clipboard for IPs and DNS names - Daemon lifecycle handling (offline/stopped states) Dependencies: - Add tailscale.com v1.96.1 (official local API client) - Bump Go to 1.26.1 (required by tailscale.com) * cleanups --------- Co-authored-by: bbedward <bbedward@gmail.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
package tailscale
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnstate"
|
||||
)
|
||||
|
||||
type mockConn struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
|
||||
func (m *mockConn) Close() error { return nil }
|
||||
func (m *mockConn) LocalAddr() net.Addr { return nil }
|
||||
func (m *mockConn) RemoteAddr() net.Addr { return nil }
|
||||
func (m *mockConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (m *mockConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
func handlerTestManager() *Manager {
|
||||
client := &mockClient{
|
||||
watchFn: func(ctx context.Context, mask ipn.NotifyWatchOpt) (ipnBusWatcher, error) {
|
||||
<-ctx.Done()
|
||||
return nil, ctx.Err()
|
||||
},
|
||||
statusFn: func(ctx context.Context) (*ipnstate.Status, error) {
|
||||
return runningStatus(), nil
|
||||
},
|
||||
}
|
||||
m := newManager(client)
|
||||
m.RefreshState()
|
||||
return m
|
||||
}
|
||||
|
||||
func TestHandleGetStatus(t *testing.T) {
|
||||
m := handlerTestManager()
|
||||
defer m.Close()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
conn := &mockConn{Buffer: buf}
|
||||
|
||||
req := models.Request{ID: 1, Method: "tailscale.getStatus"}
|
||||
handleGetStatus(conn, req, m)
|
||||
|
||||
var resp models.Response[TailscaleState]
|
||||
err := json.NewDecoder(buf).Decode(&resp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, resp.ID)
|
||||
assert.NotNil(t, resp.Result)
|
||||
assert.True(t, resp.Result.Connected)
|
||||
assert.Equal(t, "cachyos", resp.Result.Self.Hostname)
|
||||
}
|
||||
|
||||
func TestHandleRefresh(t *testing.T) {
|
||||
m := handlerTestManager()
|
||||
defer m.Close()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
conn := &mockConn{Buffer: buf}
|
||||
|
||||
req := models.Request{ID: 1, Method: "tailscale.refresh"}
|
||||
handleRefresh(conn, req, m)
|
||||
|
||||
var resp models.Response[models.SuccessResult]
|
||||
err := json.NewDecoder(buf).Decode(&resp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, resp.ID)
|
||||
assert.NotNil(t, resp.Result)
|
||||
assert.True(t, resp.Result.Success)
|
||||
}
|
||||
|
||||
func TestHandleRequest_UnknownMethod(t *testing.T) {
|
||||
m := handlerTestManager()
|
||||
defer m.Close()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
conn := &mockConn{Buffer: buf}
|
||||
|
||||
req := models.Request{ID: 1, Method: "tailscale.unknownMethod"}
|
||||
HandleRequest(conn, req, m)
|
||||
|
||||
var resp models.Response[any]
|
||||
err := json.NewDecoder(buf).Decode(&resp)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, resp.Result)
|
||||
assert.NotEmpty(t, resp.Error)
|
||||
assert.Contains(t, resp.Error, "unknown method")
|
||||
}
|
||||
Reference in New Issue
Block a user