LocalSend-compatible file-transfer TUI for headless Arch/Omarchy servers. - Pure-stdlib implementation of the LocalSend v2 protocol (discovery, HTTPS with matching cert fingerprint, send/receive, PIN). - Bubble Tea TUI: Devices, Transfers, Manage (received-file housekeeping) and Settings, theme-aware on Omarchy. - Dual-mode install.sh: curl-pipe download or build-from-clone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
1.9 KiB
Go
73 lines
1.9 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"omarchy-send/internal/discovery"
|
|
"omarchy-send/internal/protocol"
|
|
"omarchy-send/internal/server"
|
|
"omarchy-send/internal/transfer"
|
|
)
|
|
|
|
// TestSendToReceiver drives our sender against our receiver over loopback HTTP
|
|
// and asserts the file arrives intact — the M3 end-to-end guarantee.
|
|
func TestSendToReceiver(t *testing.T) {
|
|
recvDir := t.TempDir()
|
|
recvInfo := protocol.DeviceInfo{
|
|
Alias: "recv", Version: protocol.ProtocolVersion, Port: 53995, Protocol: "http",
|
|
}
|
|
srv := server.New(server.Options{Info: recvInfo, ReceiveDir: recvDir, AutoAccept: true})
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
if err := srv.Start(ctx); err != nil {
|
|
t.Fatalf("server start: %v", err)
|
|
}
|
|
go func() {
|
|
for range srv.Transfers() {
|
|
}
|
|
}()
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
// Source file to send.
|
|
srcDir := t.TempDir()
|
|
srcPath := filepath.Join(srcDir, "photo.jpg")
|
|
content := bytes.Repeat([]byte("localsend-payload-"), 5000) // ~90KB
|
|
if err := os.WriteFile(srcPath, content, 0o644); err != nil {
|
|
t.Fatalf("write src: %v", err)
|
|
}
|
|
|
|
sender := New(protocol.DeviceInfo{Alias: "sender", Fingerprint: "snd1", Version: "2.1", Protocol: "http"})
|
|
peer := discovery.Peer{Info: recvInfo, IP: "127.0.0.1"}
|
|
sender.Send(peer, []string{srcPath}, "")
|
|
|
|
// Wait for the outgoing FileDone (or fail on error/timeout).
|
|
deadline := time.After(5 * time.Second)
|
|
for {
|
|
select {
|
|
case ev := <-sender.Events():
|
|
if ev.Kind == transfer.Error {
|
|
t.Fatalf("send error: %v", ev.Err)
|
|
}
|
|
if ev.Kind == transfer.FileDone {
|
|
goto verify
|
|
}
|
|
case <-deadline:
|
|
t.Fatal("timed out waiting for send to complete")
|
|
}
|
|
}
|
|
|
|
verify:
|
|
got, err := os.ReadFile(filepath.Join(recvDir, "photo.jpg"))
|
|
if err != nil {
|
|
t.Fatalf("read received: %v", err)
|
|
}
|
|
if !bytes.Equal(got, content) {
|
|
t.Fatalf("content mismatch: %d vs %d bytes", len(got), len(content))
|
|
}
|
|
}
|