Adds LocalSend-compatible text messaging alongside file transfer. Wire format (matches the LocalSend app): a message is a single "file" with fileType "text/plain" whose content rides in the prepare-upload `preview` field. The receiver returns an empty token set, so nothing is uploaded — the text is read straight from the preview. - client: SendMessage builds that single-file prepare-upload (no body upload). - server: detect a message (one text file with non-empty preview), surface it on a new Messages() channel instead of saving a file, and respond with an empty file set. Messages bypass the accept prompt (auto-received); the PIN gate still applies. - app: bridge the server's messages channel to the TUI as MessageMsg. - tui: a new Messages tab lists received messages (enter to read full, d to delete); press `m` on a device to compose and send one. Incoming messages show a footer notice. Tests: end-to-end send→receive (text intact, sender preserved, nothing written to disk) and unit coverage of the message-detection rule. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
1.6 KiB
Go
54 lines
1.6 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"omarchy-send/internal/discovery"
|
|
"omarchy-send/internal/protocol"
|
|
"omarchy-send/internal/server"
|
|
)
|
|
|
|
// A sent message must arrive on the receiver's Messages channel with its text
|
|
// and sender intact, and must NOT be written to disk as a file.
|
|
func TestSendMessageEndToEnd(t *testing.T) {
|
|
recvDir := t.TempDir()
|
|
recvInfo := protocol.DeviceInfo{
|
|
Alias: "recv", Version: protocol.ProtocolVersion, Port: 53990, Protocol: "http",
|
|
}
|
|
// AutoAccept is false on purpose: messages must bypass the accept prompt.
|
|
srv := server.New(server.Options{Info: recvInfo, ReceiveDir: recvDir, AutoAccept: false})
|
|
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)
|
|
|
|
sender := New(protocol.DeviceInfo{Alias: "sender", Fingerprint: "snd1", Version: "2.1", Protocol: "http"})
|
|
peer := discovery.Peer{Info: recvInfo, IP: "127.0.0.1"}
|
|
sender.SendMessage(peer, "hello over the wire\nsecond line")
|
|
|
|
select {
|
|
case m := <-srv.Messages():
|
|
if m.Text != "hello over the wire\nsecond line" {
|
|
t.Fatalf("message text = %q", m.Text)
|
|
}
|
|
if m.From != "sender" {
|
|
t.Errorf("message from = %q, want \"sender\"", m.From)
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("timed out waiting for the message")
|
|
}
|
|
|
|
// A message is not a file: nothing should land in the receive dir.
|
|
if entries, _ := os.ReadDir(recvDir); len(entries) != 0 {
|
|
t.Fatalf("message should not be saved; found %d entries in receive dir", len(entries))
|
|
}
|
|
}
|