Run these tests manually using the built binary. Each test describes what to run, what to check, and when to ask the user for help.
Use nanomsg from ~/bin/nanomsg (the installed release build).
Before starting, verify the binary is installed and working:
nanomsg helpExpect: usage text listing all commands (chats, unread, history, search, contacts, send, help) and global options (--json, --no-contacts).
nanomsg chats --limit 3Check:
- Exactly 3 chats listed
- Each chat has a name (or phone/email) followed by
(#ID)or(#ID, group) - Each chat shows up to 3 recent messages with sender, date, and text preview
- Dates are human-readable (e.g., "Today at 10:30", "Yesterday at 18:00", "Feb 14, 2026 at 09:00"), not ISO 8601
- No raw JSON anywhere in the output
nanomsg chats --limit 2 --jsonCheck:
- Valid JSON array
- Each object has: chatId (number), guid (string), participants (array), recentMessages (array), unreadCount (number), isGroup (boolean)
- Dates are ISO 8601 strings with fractional seconds
nanomsg chats --limit 2
nanomsg chats --limit 2 --offset 2Check:
- The second command returns different chats than the first
- No overlap between the two result sets
nanomsg chats --limit 3
nanomsg chats --limit 3 --no-contactsCheck:
- First command shows resolved names (e.g., "Alice Smith")
- Second command shows raw handles (e.g., "+15550198765")
- Same chats, same order
nanomsg unreadIf output says "No unread messages." — stop and ask the user to open iMessage and mark at least two different conversations as unread (right-click a chat > Mark as Unread). Then re-run.
After the user has marked conversations as unread:
nanomsg unreadCheck:
- Shows at least 2 chat groups
- Each group has a header with chat name, chat ID, and unread count
- Messages are shown with sender and date
- The chats shown match what the user marked as unread
nanomsg unread --jsonCheck:
- Valid JSON array of objects
- Each object has: chatId, participants (array), messages (array)
- chatName is present when non-null (omitted when null — normal Swift JSON behavior)
- Messages have: rowid, text, sender, date, isFromMe (false for all)
Pick one chat ID from the unread output above.
nanomsg unread --chat-id <ID>Check:
- Only shows messages from that single chat
- Does not show other unread chats
nanomsg unread --json | python3 -c "import sys,json; msgs=[m for g in json.load(sys.stdin) for m in g['messages']]; assert all(m.get('text') for m in msgs), 'Found message with no text'; print(f'OK: {len(msgs)} unread messages, all have text')"Check:
- No assertion error — all unread messages have actual text content
- No system messages or empty messages leak through
Pick a chat ID from the chats output.
nanomsg history --chat-id <ID> --limit 5Check:
- Messages ordered oldest to newest
- Each message shows sender (or "You"), date, and text
- Reactions shown as
[like (Name)]etc. - Attachments shown as
📎 filename (size)
nanomsg history <ID> --limit 3Check:
- Same behavior as
--chat-id <ID>
nanomsg history --chat-id <ID> --since "2026-01-01" --limit 5Check:
- All messages are from 2026 or later
- No messages from before the specified date
nanomsg history --chat-id <ID> --limit 3 --jsonCheck:
- Valid JSON array
- Each message has: rowid, guid, text, sender, senderName, isFromMe, date, isRead
- Reactions (if present) have: type, sender, senderName, date
- Dates are ISO 8601
nanomsg history --chat-id <ID> --limit 3
nanomsg history --chat-id <ID> --limit 3 --offset 3Check:
- Second command shows older messages
- No overlap
nanomsg search "hello" --limit 5Check:
- All results contain "hello" (case-insensitive) in the message text
- Results shown with sender, date, and full text
- Ordered newest first
nanomsg search "the" --chat-id <ID> --limit 5Check:
- All results are from the specified chat only
Pick a contact name that appears in your messages.
nanomsg search "the" --from "<Name>" --limit 5Check:
- All results are from that sender (or show that sender's name)
- No messages from other senders
nanomsg search "xyzzy_nonexistent_string_12345"Check:
- Output: "No messages found."
- No errors
nanomsg search "the" --from "Nonexistent Person XYZZY"Check:
- Output: "No messages found."
- Must NOT return unfiltered results from all senders
nanomsg contacts | head -5Check:
- Columnar output: HANDLE, SERVICE, NAME
- Handles are phone numbers or emails
- Services are iMessage, SMS, or RCS
- Names are resolved where possible
nanomsg contacts "+1540"Check:
- Only handles containing "+1540" are shown
- May also show name matches
nanomsg contacts "smith"Check:
- Shows handles whose resolved name contains "smith"
- May also show handles containing "smith" in the ID
nanomsg contacts --limit 3
nanomsg contacts --limit 3 --offset 3Check:
- First returns exactly 3 results
- Second returns 3 different results
- No overlap
nanomsg contacts --limit 3 --jsonCheck:
- Valid JSON array
- Each object has: handleId, service, resolvedName (string or null)
Only run these if the user explicitly approves sending test messages.
nanomsg send --to "+1234567890" --chat-id 123 --text "test"Check:
- Error: "send requires --chat-id or --to, not both"
nanomsg send --to "+1234567890"Check:
- Error: "send requires --text "
Ask the user for a chat ID to send a test message to.
nanomsg send --chat-id <ID> --text "nanomsg test message, please ignore"Check:
- Output: "Sent: nanomsg test message, please ignore"
- Message appears in iMessage
nanomsg helpCheck:
- Lists all commands
- Shows --json and --no-contacts as global options
nanomsg help chats
nanomsg help history
nanomsg help send
nanomsg help contactsCheck:
- Each shows usage, description, and options
chatsmentions --messages, --unread, --offsethistorymentions positional chat ID alternativesendmentions --json in the Options block, mutual exclusivity notecontactsmentions --query, --limit, --offset
nanomsgCheck:
- Shows usage help (same as
nanomsg help)
nanomsg foobar 2>&1Check:
- Error on stderr: "nanomsg: unknown command: foobar"
- Exit code is non-zero
nanomsg history 2>&1
nanomsg search 2>&1Check:
historyerrors about missing --chat-idsearcherrors about missing query string