Two related additions for using omarchy-send beyond the focused TUI: Headless send (no TUI / no TTY) — for scripts, cron, SSH sessions: omarchy-send --to "<alias>" --message "<text>" [--send-pin N] [--wait 15s] Resolves the peer by alias over multicast (discovery only, not the HTTP receiver, so it co-exists with a running instance), sends, prints a one-line result, and exits non-zero on not-found / failure. New discovery.FindPeer/Snapshot and client.SendMessageSync (returns the error directly, incl. ErrPinRequired). Bugfix surfaced by the above: the official LocalSend client answers a message prepare-upload with HTTP 204 No Content (the text rides in the preview field, nothing to upload). prepareUpload only accepted 200, so message sends to official peers failed with "prepare-upload status 204" in BOTH the new headless path and the existing TUI. Now treats 204 as success (empty response). Desktop notifications: a running receiver raises a notify-send notification on an incoming message or file offer, so mako shows it on Omarchy/Hyprland even when the TUI isn't focused. New internal/notify (best-effort; self-disabling on headless boxes with no notify-send / session bus). Off-switch: --no-notify flag, cfg.NoNotify, and a Settings 'n' toggle wired to a live atomic gate via Controller.SetNotify so it takes effect without a restart. Tests: discovery/find_test, client/message_sync_test, client/prepare_204_test, app/events_test. Race-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
74 lines
2.4 KiB
Go
74 lines
2.4 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"omarchy-send/internal/discovery"
|
|
"omarchy-send/internal/protocol"
|
|
"omarchy-send/internal/server"
|
|
"omarchy-send/internal/transfer"
|
|
)
|
|
|
|
// SendMessageSync delivers the message and returns nil, with the text arriving
|
|
// on the receiver's Messages channel — the synchronous path used by headless
|
|
// `-to/-message` sends.
|
|
func TestSendMessageSyncSuccess(t *testing.T) {
|
|
recvInfo := protocol.DeviceInfo{
|
|
Alias: "recv", Version: protocol.ProtocolVersion, Port: 53991, Protocol: "http",
|
|
}
|
|
srv := server.New(server.Options{Info: recvInfo, ReceiveDir: t.TempDir(), 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: "cli", Fingerprint: "cli1", Version: "2.1", Protocol: "http"})
|
|
peer := discovery.Peer{Info: recvInfo, IP: "127.0.0.1"}
|
|
if err := sender.SendMessageSync(peer, "headless hello", ""); err != nil {
|
|
t.Fatalf("SendMessageSync: %v", err)
|
|
}
|
|
|
|
select {
|
|
case m := <-srv.Messages():
|
|
if m.Text != "headless hello" {
|
|
t.Fatalf("message text = %q", m.Text)
|
|
}
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("timed out waiting for the message")
|
|
}
|
|
}
|
|
|
|
// A peer that requires a PIN rejects a PIN-less send with ErrPinRequired, so the
|
|
// CLI can tell the user to pass -send-pin.
|
|
func TestSendMessageSyncPinRequired(t *testing.T) {
|
|
recvInfo := protocol.DeviceInfo{
|
|
Alias: "recv", Version: protocol.ProtocolVersion, Port: 53992, Protocol: "http",
|
|
}
|
|
srv := server.New(server.Options{Info: recvInfo, ReceiveDir: t.TempDir(), PIN: "2468"})
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
if err := srv.Start(ctx); err != nil {
|
|
t.Fatalf("server start: %v", err)
|
|
}
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
sender := New(protocol.DeviceInfo{Alias: "cli", Fingerprint: "cli1", Version: "2.1", Protocol: "http"})
|
|
peer := discovery.Peer{Info: recvInfo, IP: "127.0.0.1"}
|
|
err := sender.SendMessageSync(peer, "no pin", "")
|
|
if !errors.Is(err, transfer.ErrPinRequired) {
|
|
t.Fatalf("err = %v, want ErrPinRequired", err)
|
|
}
|
|
// With the correct PIN it goes through.
|
|
if err := sender.SendMessageSync(peer, "with pin", "2468"); err != nil {
|
|
t.Fatalf("SendMessageSync with PIN: %v", err)
|
|
}
|
|
}
|