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>
57 lines
1.4 KiB
Go
57 lines
1.4 KiB
Go
package server
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"omarchy-send/internal/protocol"
|
|
)
|
|
|
|
func TestMessageOf(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
files map[string]protocol.FileMetadata
|
|
wantOK bool
|
|
wantTxt string
|
|
}{
|
|
{
|
|
name: "text mime with preview is a message",
|
|
files: map[string]protocol.FileMetadata{"a": {FileType: "text/plain", Preview: "hi"}},
|
|
wantOK: true,
|
|
wantTxt: "hi",
|
|
},
|
|
{
|
|
name: "bare text enum with preview is a message",
|
|
files: map[string]protocol.FileMetadata{"a": {FileType: "text", Preview: "yo"}},
|
|
wantOK: true, wantTxt: "yo",
|
|
},
|
|
{
|
|
name: "text file without preview is a real file, not a message",
|
|
files: map[string]protocol.FileMetadata{"a": {FileType: "text/plain", Preview: ""}},
|
|
wantOK: false,
|
|
},
|
|
{
|
|
name: "non-text with preview is not a message",
|
|
files: map[string]protocol.FileMetadata{"a": {FileType: "image/jpeg", Preview: "data"}},
|
|
wantOK: false,
|
|
},
|
|
{
|
|
name: "two files is never a message",
|
|
files: map[string]protocol.FileMetadata{
|
|
"a": {FileType: "text/plain", Preview: "hi"},
|
|
"b": {FileType: "text/plain", Preview: "bye"},
|
|
},
|
|
wantOK: false,
|
|
},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
txt, ok := messageOf(c.files)
|
|
if ok != c.wantOK {
|
|
t.Fatalf("ok = %v, want %v", ok, c.wantOK)
|
|
}
|
|
if ok && txt != c.wantTxt {
|
|
t.Fatalf("text = %q, want %q", txt, c.wantTxt)
|
|
}
|
|
})
|
|
}
|
|
}
|