Skip to content

Add Inbound Email API commands - #10

Merged
mklocek merged 7 commits into
mainfrom
inbound-v2
Aug 5, 2026
Merged

Add Inbound Email API commands#10
mklocek merged 7 commits into
mainfrom
inbound-v2

Conversation

@mklocek

@mklocek mklocek commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Inbound Email API support to the CLI, matching the released Node, Ruby, MCP, PHP, Python, Java, and .NET SDKs. Exposed as mailtrap inbound … — the commands are not account-scoped (no --account-id); they go to https://mailtrap.io/api/inbound/....

mailtrap inbound folders   list | get | create | update | delete
mailtrap inbound inboxes   list | get | create | update | delete     (--folder-id)
mailtrap inbound messages  list | get | delete | reply | reply-all | forward   (--inbox-id)
mailtrap inbound threads   list | get | delete                        (--inbox-id)

Also surfaces the other fields that ship with the Inbound Email API:

  • webhooks create/update: new --inbound-inbox-id flag and the inbound_receiving webhook type
  • domains: inbound_enabled / inbound_verified
  • email-logs: inbound threading fields (rfc_message_id, in_reply_to, references, thread_id)

Notes

  • Paths are built directly under /api/inbound/... on BaseGeneral (no AccountPath, no RequireAccountID). Inbox management is folder-scoped; messages and threads are accessed via the top-level inbox route.
  • Request bodies are sent flat (no resource envelope). Folder/inbox lists are bare arrays; message/thread lists return {data, total_count, last_id} (paginated via --last-id); reply/forward return {message_ids}.
  • reply/reply-all/forward reuse the send command's address parsing (extracted into cmdutil), so recipients accept Name <email> or email; forward requires --to. There is no --subject (the API derives it).
  • Follows the CLI conventions in sdk-dev/references/cli.md: subcommand factory, RequireFlag, PATCH Changed()-gating, per-spec response wrappers, single column slice, help-text style.

Summary by CodeRabbit

  • New Features
    • Added inbound CLI support for managing folders, inboxes, messages, and threads.
    • Added message pagination, retrieval, deletion, replies, reply-all, and forwarding.
    • Added inbound-aware webhook creation, updates, and listing.
    • Added inbound status and verification details to domain and email log output.
  • Documentation
    • Added command references, usage examples, and integration test guidance.
  • Bug Fixes
    • Standardized email address parsing and validation across sending commands.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The CLI adds inbound folders, inboxes, messages, and threads commands. It adds shared email address parsing, inbound webhook routing, related domain and email-log fields, integration tests, and documentation.

Changes

Inbound CLI support

Layer / File(s) Summary
Shared address parsing
internal/cmdutil/address.go, internal/commands/send/*, internal/commands/inbound/messages/send.go
Shared address types and parsers support inbound message sending and transactional sends.
Folder and inbox management
cmd/root.go, internal/commands/inbound/folders/*, internal/commands/inbound/inboxes/*
The CLI registers inbound commands and supports folder and inbox CRUD operations with table and JSON output.
Message and thread operations
internal/commands/inbound/messages/*, internal/commands/inbound/threads/*
The CLI supports message listing, pagination, retrieval, deletion, replies, reply-all, forwarding, and thread management.
Inbound integrations
internal/commands/webhooks/*, internal/commands/domains/list.go, internal/commands/email_logs/*
Webhook commands support inbound inbox routing. Domain and email-log output includes inbound and thread metadata.
Documentation and validation
README.md, docs/TEST_PLAN.md, skills/mailtrap-cli/*
Documentation and test plans describe inbound routes, flags, pagination, output, and execution order.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: igordobryn, piobeny

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant InboundAPI
  participant Output
  CLI->>InboundAPI: Execute inbound command
  InboundAPI-->>CLI: Return resource or send result
  CLI->>Output: Render table or JSON
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Inbound Email API commands to the CLI.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
internal/commands/inbound/messages/send.go (1)

53-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider collapsing the repeated to/cc/bcc parsing blocks.

The to, cc, and bcc blocks repeat the same pattern: check length, call cmdutil.ParseEmailAddrs, wrap the error, and assign to body. A small loop over the three fields removes the repetition without changing behavior.

♻️ Proposed refactor
-	if len(s.to) > 0 {
-		addrs, err := cmdutil.ParseEmailAddrs(s.to)
-		if err != nil {
-			return nil, fmt.Errorf("invalid --to address: %w", err)
-		}
-		body["to"] = addrs
-	}
-	if len(s.cc) > 0 {
-		addrs, err := cmdutil.ParseEmailAddrs(s.cc)
-		if err != nil {
-			return nil, fmt.Errorf("invalid --cc address: %w", err)
-		}
-		body["cc"] = addrs
-	}
-	if len(s.bcc) > 0 {
-		addrs, err := cmdutil.ParseEmailAddrs(s.bcc)
-		if err != nil {
-			return nil, fmt.Errorf("invalid --bcc address: %w", err)
-		}
-		body["bcc"] = addrs
-	}
+	for _, field := range []struct {
+		name   string
+		values []string
+	}{{"to", s.to}, {"cc", s.cc}, {"bcc", s.bcc}} {
+		if len(field.values) == 0 {
+			continue
+		}
+		addrs, err := cmdutil.ParseEmailAddrs(field.values)
+		if err != nil {
+			return nil, fmt.Errorf("invalid --%s address: %w", field.name, err)
+		}
+		body[field.name] = addrs
+	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/inbound/messages/send.go` around lines 53 - 73, Collapse
the repeated address parsing in the send command into a loop over the to, cc,
and bcc fields, preserving each field’s existing empty check,
cmdutil.ParseEmailAddrs call, error context, and body assignment. Update the
surrounding request-building method rather than changing parsing behavior.
internal/commands/inbound/messages/messages_test.go (1)

126-158: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test coverage for reply-all.

NewCmdReplyAll posts to a distinct /reply_all endpoint but has no automated test and no manual test-plan row, unlike reply and forward, which both have coverage in each artifact.

  • internal/commands/inbound/messages/messages_test.go#L126-L158: add a TestMessagesReplyAll test mirroring TestMessagesReply, asserting the POST path ends in /reply_all and the request body and response are handled correctly.
  • docs/TEST_PLAN.md#L338-L364: add a table row for mailtrap inbound messages reply-all --inbox-id <INBOX_ID> --id <MESSAGE_ID> --text "..." between rows 22.13 (Reply) and 22.14 (Forward), consistent with the note on line 340 that already describes reply-all's behavior.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/inbound/messages/messages_test.go` around lines 126 - 158,
Add TestMessagesReplyAll in internal/commands/inbound/messages/messages_test.go,
mirroring TestMessagesReply while asserting the POST path ends with /reply_all
and validating the request body, response handling, and output message ID. Add
the corresponding mailtrap inbound messages reply-all command row in
docs/TEST_PLAN.md between rows 22.13 and 22.14.
internal/commands/inbound/messages/reply.go (1)

13-57: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the shared reply/reply-all/forward command boilerplate.

reply.go, reply_all.go, and forward.go each implement the same command shape: validate inbox-id and id, build a client, call send.body(), POST to an inbound endpoint, and print the result with sendResultColumns. The only differences are the Use/Short strings, the endpoint suffix, and the extra --to check in forward.go. A shared constructor removes this duplication and keeps future endpoint or flag changes in one place.

  • internal/commands/inbound/messages/reply.go#L13-L57: replace the body of NewCmdReply with a call to a shared helper, e.g. newSendActionCmd(f, "reply", "Reply to an inbound message (sends a real email to the original sender)", "reply", false).
  • internal/commands/inbound/messages/reply_all.go#L13-L57: replace the body of NewCmdReplyAll with the same helper, passing "reply-all", the reply-all Short text, and endpoint suffix "reply_all".
  • internal/commands/inbound/messages/forward.go#L13-L60: replace the body of NewCmdForward with the same helper, passing "forward", the forward Short text, endpoint suffix "forward", and requireTo=true to keep the --to is required check.
♻️ Proposed shared helper (place in send.go)
func newSendActionCmd(f *cmdutil.Factory, use, short, endpointSuffix string, requireTo bool) *cobra.Command {
	var (
		inboxID   string
		messageID string
		send      sendFlags
	)

	cmd := &cobra.Command{
		Use:   use,
		Short: short,
		RunE: func(cmd *cobra.Command, args []string) error {
			if err := cmdutil.RequireFlag("inbox-id", inboxID); err != nil {
				return err
			}
			if err := cmdutil.RequireFlag("id", messageID); err != nil {
				return err
			}
			if requireTo && len(send.to) == 0 {
				return fmt.Errorf("--to is required")
			}

			c, err := f.NewClient()
			if err != nil {
				return err
			}

			body, err := send.body()
			if err != nil {
				return err
			}

			path := fmt.Sprintf("/api/inbound/inboxes/%s/messages/%s/%s", inboxID, messageID, endpointSuffix)

			var resp SendMessageResult
			if err := c.Post(context.Background(), client.BaseGeneral, path, body, &resp); err != nil {
				return err
			}

			return output.Print(f.IOStreams.Out, cmdutil.GetOutputFormat(), resp, sendResultColumns)
		},
	}

	cmd.Flags().StringVar(&inboxID, "inbox-id", "", "Inbox ID (required)")
	cmd.Flags().StringVar(&messageID, "id", "", "Message ID (required)")
	send.register(cmd)

	return cmd
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/inbound/messages/reply.go` around lines 13 - 57, Extract
the duplicated command flow into a shared newSendActionCmd helper in send.go,
preserving flag validation, client creation, body generation, POST, and output
formatting. Update internal/commands/inbound/messages/reply.go lines 13-57 to
call it with reply metadata and requireTo=false; update
internal/commands/inbound/messages/reply_all.go lines 13-57 with reply-all
metadata and endpoint suffix reply_all; update
internal/commands/inbound/messages/forward.go lines 13-60 with forward metadata,
endpoint suffix forward, and requireTo=true so the existing --to validation
remains.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/cmdutil/address.go`:
- Around line 15-33: Update ParseEmailAddr to validate the extracted email with
net/mail.ParseAddress or equivalent before returning it, rejecting malformed
plain addresses and empty angle-bracket addresses while preserving supported
“Name <email>” parsing; add tests covering invalid values such as “not-an-email”
and “Name <>”.

In `@internal/commands/inbound/messages/list.go`:
- Around line 63-84: Both inbound list commands discard the pagination cursor,
so users cannot discover the next --last-id value. In
internal/commands/inbound/messages/list.go lines 63-84, update the messages list
command after output.Print to emit resp.LastID, using the established output
behavior for non-JSON formats; apply the same resp.LastID output change in
internal/commands/inbound/threads/list.go lines 69-90 for the threads list
command, while preserving the existing data output and JSON behavior.

In `@internal/commands/webhooks/create.go`:
- Around line 94-100: The webhook CLI help text in the flag definitions should
match the API contract: update the `--type` description to include `campaigns`,
and update the `--domain-id` description to indicate it applies to both
`email_sending` and `campaigns` webhooks. Modify only these descriptions in the
command flag setup.

---

Nitpick comments:
In `@internal/commands/inbound/messages/messages_test.go`:
- Around line 126-158: Add TestMessagesReplyAll in
internal/commands/inbound/messages/messages_test.go, mirroring TestMessagesReply
while asserting the POST path ends with /reply_all and validating the request
body, response handling, and output message ID. Add the corresponding mailtrap
inbound messages reply-all command row in docs/TEST_PLAN.md between rows 22.13
and 22.14.

In `@internal/commands/inbound/messages/reply.go`:
- Around line 13-57: Extract the duplicated command flow into a shared
newSendActionCmd helper in send.go, preserving flag validation, client creation,
body generation, POST, and output formatting. Update
internal/commands/inbound/messages/reply.go lines 13-57 to call it with reply
metadata and requireTo=false; update
internal/commands/inbound/messages/reply_all.go lines 13-57 with reply-all
metadata and endpoint suffix reply_all; update
internal/commands/inbound/messages/forward.go lines 13-60 with forward metadata,
endpoint suffix forward, and requireTo=true so the existing --to validation
remains.

In `@internal/commands/inbound/messages/send.go`:
- Around line 53-73: Collapse the repeated address parsing in the send command
into a loop over the to, cc, and bcc fields, preserving each field’s existing
empty check, cmdutil.ParseEmailAddrs call, error context, and body assignment.
Update the surrounding request-building method rather than changing parsing
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 91c9b5fc-15f5-430e-bf94-183ac5c03ad8

📥 Commits

Reviewing files that changed from the base of the PR and between 1c2f517 and 9388c5b.

📒 Files selected for processing (43)
  • README.md
  • cmd/root.go
  • docs/TEST_PLAN.md
  • internal/cmdutil/address.go
  • internal/cmdutil/address_test.go
  • internal/commands/domains/list.go
  • internal/commands/email_logs/list.go
  • internal/commands/inbound/folders/create.go
  • internal/commands/inbound/folders/delete.go
  • internal/commands/inbound/folders/folders.go
  • internal/commands/inbound/folders/folders_test.go
  • internal/commands/inbound/folders/get.go
  • internal/commands/inbound/folders/list.go
  • internal/commands/inbound/folders/update.go
  • internal/commands/inbound/inbound.go
  • internal/commands/inbound/inboxes/create.go
  • internal/commands/inbound/inboxes/delete.go
  • internal/commands/inbound/inboxes/get.go
  • internal/commands/inbound/inboxes/inboxes.go
  • internal/commands/inbound/inboxes/inboxes_test.go
  • internal/commands/inbound/inboxes/list.go
  • internal/commands/inbound/inboxes/update.go
  • internal/commands/inbound/messages/delete.go
  • internal/commands/inbound/messages/forward.go
  • internal/commands/inbound/messages/get.go
  • internal/commands/inbound/messages/list.go
  • internal/commands/inbound/messages/messages.go
  • internal/commands/inbound/messages/messages_test.go
  • internal/commands/inbound/messages/reply.go
  • internal/commands/inbound/messages/reply_all.go
  • internal/commands/inbound/messages/send.go
  • internal/commands/inbound/threads/delete.go
  • internal/commands/inbound/threads/get.go
  • internal/commands/inbound/threads/list.go
  • internal/commands/inbound/threads/threads.go
  • internal/commands/inbound/threads/threads_test.go
  • internal/commands/send/send.go
  • internal/commands/send/transactional.go
  • internal/commands/webhooks/create.go
  • internal/commands/webhooks/list.go
  • internal/commands/webhooks/update.go
  • skills/mailtrap-cli/SKILL.md
  • skills/mailtrap-cli/references/inbound.md
💤 Files with no reviewable changes (1)
  • internal/commands/send/send.go

Comment thread internal/cmdutil/address.go
Comment thread internal/commands/inbound/messages/list.go
Comment thread internal/commands/webhooks/create.go Outdated
mklocek added 7 commits August 4, 2026 16:33
Add the inbound command group with folders and inboxes subcommands
(list/get/create/update/delete). Inbound endpoints are token-scoped
under /api/inbound, so paths are built directly rather than via the
account path helper. Register the group in the root command.
Add the inbound messages group (list/get/delete plus reply, reply-all,
and forward) and the threads group (list/get/delete). Message and thread
list paginate via a manual --last-id cursor flag, matching the
email-logs command.

Extract the shared email-address parser ('Name <email>' / 'email') into
cmdutil and reuse it in the send command and for reply/reply-all/forward,
so inbound recipient parsing matches send. Wire both groups into the
inbound command.
Add --inbound-inbox-id to webhooks create/update and the
inbound_receiving type; add inbound_enabled/inbound_verified to the
domain output; add the inbound threading fields (rfc_message_id,
in_reply_to, references, thread_id) to the email-log output.
Add tests for the inbound folders, inboxes, messages, and threads
commands using an httptest server, asserting the request method,
token-scoped /api/inbound path, request bodies, and rendered output.
Add the inbound command reference (skills/mailtrap-cli/references/inbound.md),
a Commands-table row and usage examples in the README, an Inbound section
in the test plan, and the inbound entry in the skill's command-group table.
Include the campaigns webhook type in --type, and correct --domain-id to
note it scopes email_sending and campaigns (not email_sending only).
inbound messages/threads list and email-logs list now print the next
page cursor (--last-id / --cursor) after the table so it is discoverable
for the next request. JSON output is unchanged.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
internal/commands/inbound/messages/messages_test.go (1)

129-161: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an integration test for reply-all.

TestMessagesReply tests only reply. The PR also adds reply-all, but this file has no test for that command. Add a test that verifies the expected request route, request body, and response output for reply-all.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/inbound/messages/messages_test.go` around lines 129 - 161,
Add a dedicated integration test alongside TestMessagesReply for the messages
reply-all command, using the existing setupTest and command execution patterns.
Verify it sends the expected POST route and request body, then assert Execute
succeeds and the response message ID appears in command output.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/commands/email_logs/list.go`:
- Around line 119-125: Update the pagination notice write in the command’s
output flow to handle the error returned by fmt.Fprintf instead of ignoring it.
Return that write error when printing the “Next page” cursor, while preserving
the existing output.Print handling and nil-success behavior when no pagination
notice is emitted.

In `@internal/commands/inbound/inboxes/inboxes_test.go`:
- Around line 128-130: Check and handle errors from io.ReadAll and
json.Unmarshal in the create request block at
internal/commands/inbound/inboxes/inboxes_test.go:128-130, reporting each error
and returning before assertions. Apply the same error handling to the update
request block at internal/commands/inbound/inboxes/inboxes_test.go:237-239; both
sites require direct changes.
- Around line 56-59: Check each mock response json.Encoder.Encode call in
internal/commands/inbound/inboxes/inboxes_test.go at lines 56-59, 99-102,
139-141, 185-188, and 244-247, and report any returned error with t.Errorf so
encoding failures directly fail the corresponding test.

In `@internal/commands/inbound/messages/messages_test.go`:
- Around line 48-50: Replace suffix-based URL assertions with exact r.URL.Path
comparisons for the list, get, reply, forward, and delete endpoint tests in
internal/commands/inbound/messages/messages_test.go at lines 48-50, 105-107,
134-136, 165-167, and 220-222, preserving each test’s complete expected API
path.

---

Nitpick comments:
In `@internal/commands/inbound/messages/messages_test.go`:
- Around line 129-161: Add a dedicated integration test alongside
TestMessagesReply for the messages reply-all command, using the existing
setupTest and command execution patterns. Verify it sends the expected POST
route and request body, then assert Execute succeeds and the response message ID
appears in command output.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c2d67b1-e2f3-459a-aac1-d821a8624b22

📥 Commits

Reviewing files that changed from the base of the PR and between 9388c5b and 5dda99a.

📒 Files selected for processing (44)
  • README.md
  • cmd/root.go
  • docs/TEST_PLAN.md
  • internal/cmdutil/address.go
  • internal/cmdutil/address_test.go
  • internal/commands/domains/list.go
  • internal/commands/email_logs/email_logs_test.go
  • internal/commands/email_logs/list.go
  • internal/commands/inbound/folders/create.go
  • internal/commands/inbound/folders/delete.go
  • internal/commands/inbound/folders/folders.go
  • internal/commands/inbound/folders/folders_test.go
  • internal/commands/inbound/folders/get.go
  • internal/commands/inbound/folders/list.go
  • internal/commands/inbound/folders/update.go
  • internal/commands/inbound/inbound.go
  • internal/commands/inbound/inboxes/create.go
  • internal/commands/inbound/inboxes/delete.go
  • internal/commands/inbound/inboxes/get.go
  • internal/commands/inbound/inboxes/inboxes.go
  • internal/commands/inbound/inboxes/inboxes_test.go
  • internal/commands/inbound/inboxes/list.go
  • internal/commands/inbound/inboxes/update.go
  • internal/commands/inbound/messages/delete.go
  • internal/commands/inbound/messages/forward.go
  • internal/commands/inbound/messages/get.go
  • internal/commands/inbound/messages/list.go
  • internal/commands/inbound/messages/messages.go
  • internal/commands/inbound/messages/messages_test.go
  • internal/commands/inbound/messages/reply.go
  • internal/commands/inbound/messages/reply_all.go
  • internal/commands/inbound/messages/send.go
  • internal/commands/inbound/threads/delete.go
  • internal/commands/inbound/threads/get.go
  • internal/commands/inbound/threads/list.go
  • internal/commands/inbound/threads/threads.go
  • internal/commands/inbound/threads/threads_test.go
  • internal/commands/send/send.go
  • internal/commands/send/transactional.go
  • internal/commands/webhooks/create.go
  • internal/commands/webhooks/list.go
  • internal/commands/webhooks/update.go
  • skills/mailtrap-cli/SKILL.md
  • skills/mailtrap-cli/references/inbound.md
💤 Files with no reviewable changes (1)
  • internal/commands/send/send.go
🚧 Files skipped from review as they are similar to previous changes (39)
  • internal/commands/inbound/folders/create.go
  • internal/commands/inbound/inboxes/inboxes.go
  • internal/commands/inbound/messages/messages.go
  • cmd/root.go
  • internal/commands/inbound/inboxes/get.go
  • internal/commands/inbound/folders/list.go
  • internal/commands/domains/list.go
  • internal/commands/webhooks/update.go
  • internal/commands/inbound/inboxes/list.go
  • internal/commands/inbound/folders/update.go
  • README.md
  • internal/commands/webhooks/create.go
  • internal/commands/inbound/messages/list.go
  • skills/mailtrap-cli/SKILL.md
  • internal/commands/inbound/messages/delete.go
  • internal/commands/inbound/threads/get.go
  • internal/commands/inbound/messages/reply.go
  • internal/commands/inbound/threads/threads.go
  • internal/commands/inbound/inboxes/update.go
  • internal/commands/webhooks/list.go
  • internal/commands/inbound/messages/send.go
  • internal/commands/inbound/messages/get.go
  • internal/commands/inbound/inboxes/create.go
  • internal/commands/inbound/threads/delete.go
  • internal/commands/inbound/messages/forward.go
  • skills/mailtrap-cli/references/inbound.md
  • internal/commands/inbound/inbound.go
  • internal/commands/inbound/threads/list.go
  • internal/commands/inbound/messages/reply_all.go
  • internal/cmdutil/address_test.go
  • docs/TEST_PLAN.md
  • internal/commands/inbound/folders/get.go
  • internal/commands/inbound/folders/delete.go
  • internal/commands/inbound/threads/threads_test.go
  • internal/commands/send/transactional.go
  • internal/commands/inbound/folders/folders_test.go
  • internal/cmdutil/address.go
  • internal/commands/inbound/folders/folders.go
  • internal/commands/inbound/inboxes/delete.go

Comment on lines +119 to +125
if err := output.Print(f.IOStreams.Out, format, resp.Messages, emailLogColumns); err != nil {
return err
}
if format != output.FormatJSON && resp.NextPageCursor != "" {
fmt.Fprintf(f.IOStreams.Out, "\nNext page: --cursor %s\n", resp.NextPageCursor)
}
return nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Propagate pagination output errors.

fmt.Fprintf can fail after output.Print succeeds, but the command ignores that error and returns success. Return the write error so broken pipes and other output failures are reported.

Proposed fix
-				fmt.Fprintf(f.IOStreams.Out, "\nNext page: --cursor %s\n", resp.NextPageCursor)
+				if _, err := fmt.Fprintf(f.IOStreams.Out, "\nNext page: --cursor %s\n", resp.NextPageCursor); err != nil {
+					return err
+				}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err := output.Print(f.IOStreams.Out, format, resp.Messages, emailLogColumns); err != nil {
return err
}
if format != output.FormatJSON && resp.NextPageCursor != "" {
fmt.Fprintf(f.IOStreams.Out, "\nNext page: --cursor %s\n", resp.NextPageCursor)
}
return nil
if err := output.Print(f.IOStreams.Out, format, resp.Messages, emailLogColumns); err != nil {
return err
}
if format != output.FormatJSON && resp.NextPageCursor != "" {
if _, err := fmt.Fprintf(f.IOStreams.Out, "\nNext page: --cursor %s\n", resp.NextPageCursor); err != nil {
return err
}
}
return nil
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/email_logs/list.go` around lines 119 - 125, Update the
pagination notice write in the command’s output flow to handle the error
returned by fmt.Fprintf instead of ignoring it. Return that write error when
printing the “Next page” cursor, while preserving the existing output.Print
handling and nil-success behavior when no pagination notice is emitted.

Comment on lines +56 to +59
json.NewEncoder(w).Encode([]map[string]interface{}{
{"id": 201, "name": "Support inbox", "address": "support@inbound-mailtrap.io"},
{"id": 202, "name": "Catch-all", "address": "catch-all@example.com", "domain_id": 6},
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Check every mock response encoding error.

The unchecked Encode calls violate errcheck. Report the error through t.Errorf so a failed mock response has a direct test failure.

  • internal/commands/inbound/inboxes/inboxes_test.go#L56-L59: check the list response encoding error.
  • internal/commands/inbound/inboxes/inboxes_test.go#L99-L102: check the get response encoding error.
  • internal/commands/inbound/inboxes/inboxes_test.go#L139-L141: check the create response encoding error.
  • internal/commands/inbound/inboxes/inboxes_test.go#L185-L188: check the JSON list response encoding error.
  • internal/commands/inbound/inboxes/inboxes_test.go#L244-L247: check the update response encoding error.
Proposed fix pattern
- json.NewEncoder(w).Encode(response)
+ if err := json.NewEncoder(w).Encode(response); err != nil {
+     t.Errorf("encode response: %v", err)
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
json.NewEncoder(w).Encode([]map[string]interface{}{
{"id": 201, "name": "Support inbox", "address": "support@inbound-mailtrap.io"},
{"id": 202, "name": "Catch-all", "address": "catch-all@example.com", "domain_id": 6},
})
if err := json.NewEncoder(w).Encode([]map[string]interface{}{
{"id": 201, "name": "Support inbox", "address": "support@inbound-mailtrap.io"},
{"id": 202, "name": "Catch-all", "address": "catch-all@example.com", "domain_id": 6},
}); err != nil {
t.Errorf("encode response: %v", err)
}
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 56-56: Error return value of (*encoding/json.Encoder).Encode is not checked

(errcheck)

📍 Affects 1 file
  • internal/commands/inbound/inboxes/inboxes_test.go#L56-L59 (this comment)
  • internal/commands/inbound/inboxes/inboxes_test.go#L99-L102
  • internal/commands/inbound/inboxes/inboxes_test.go#L139-L141
  • internal/commands/inbound/inboxes/inboxes_test.go#L185-L188
  • internal/commands/inbound/inboxes/inboxes_test.go#L244-L247
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/inbound/inboxes/inboxes_test.go` around lines 56 - 59,
Check each mock response json.Encoder.Encode call in
internal/commands/inbound/inboxes/inboxes_test.go at lines 56-59, 99-102,
139-141, 185-188, and 244-247, and report any returned error with t.Errorf so
encoding failures directly fail the corresponding test.

Source: Linters/SAST tools

Comment on lines +128 to +130
body, _ := io.ReadAll(r.Body)
var reqBody map[string]interface{}
json.Unmarshal(body, &reqBody)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Check request body read and JSON decode errors.

The handlers continue after request decoding fails. Report the error and return before assertions so the test identifies the failed operation.

  • internal/commands/inbound/inboxes/inboxes_test.go#L128-L130: check io.ReadAll and json.Unmarshal for the create request.
  • internal/commands/inbound/inboxes/inboxes_test.go#L237-L239: check io.ReadAll and json.Unmarshal for the update request.
Proposed fix pattern
- body, _ := io.ReadAll(r.Body)
+ body, err := io.ReadAll(r.Body)
+ if err != nil {
+     t.Errorf("read request body: %v", err)
+     return
+ }
  var reqBody map[string]interface{}
- json.Unmarshal(body, &reqBody)
+ if err := json.Unmarshal(body, &reqBody); err != nil {
+     t.Errorf("decode request body: %v", err)
+     return
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
body, _ := io.ReadAll(r.Body)
var reqBody map[string]interface{}
json.Unmarshal(body, &reqBody)
body, err := io.ReadAll(r.Body)
if err != nil {
t.Errorf("read request body: %v", err)
return
}
var reqBody map[string]interface{}
if err := json.Unmarshal(body, &reqBody); err != nil {
t.Errorf("decode request body: %v", err)
return
}
🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 130-130: Error return value of json.Unmarshal is not checked

(errcheck)

📍 Affects 1 file
  • internal/commands/inbound/inboxes/inboxes_test.go#L128-L130 (this comment)
  • internal/commands/inbound/inboxes/inboxes_test.go#L237-L239
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/inbound/inboxes/inboxes_test.go` around lines 128 - 130,
Check and handle errors from io.ReadAll and json.Unmarshal in the create request
block at internal/commands/inbound/inboxes/inboxes_test.go:128-130, reporting
each error and returning before assertions. Apply the same error handling to the
update request block at
internal/commands/inbound/inboxes/inboxes_test.go:237-239; both sites require
direct changes.

Source: Linters/SAST tools

Comment on lines +48 to +50
if !strings.HasSuffix(r.URL.Path, "/api/inbound/inboxes/201/messages") {
t.Errorf("unexpected path: %s", r.URL.Path)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Compare complete API paths in endpoint tests.

strings.HasSuffix accepts an unexpected path prefix. This can let an incorrect client route pass these tests. Compare r.URL.Path with the complete expected path.

  • internal/commands/inbound/messages/messages_test.go#L48-L50: compare the list path exactly.
  • internal/commands/inbound/messages/messages_test.go#L105-L107: compare the get path exactly.
  • internal/commands/inbound/messages/messages_test.go#L134-L136: compare the reply path exactly.
  • internal/commands/inbound/messages/messages_test.go#L165-L167: compare the forward path exactly.
  • internal/commands/inbound/messages/messages_test.go#L220-L222: compare the delete path exactly.
📍 Affects 1 file
  • internal/commands/inbound/messages/messages_test.go#L48-L50 (this comment)
  • internal/commands/inbound/messages/messages_test.go#L105-L107
  • internal/commands/inbound/messages/messages_test.go#L134-L136
  • internal/commands/inbound/messages/messages_test.go#L165-L167
  • internal/commands/inbound/messages/messages_test.go#L220-L222
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/commands/inbound/messages/messages_test.go` around lines 48 - 50,
Replace suffix-based URL assertions with exact r.URL.Path comparisons for the
list, get, reply, forward, and delete endpoint tests in
internal/commands/inbound/messages/messages_test.go at lines 48-50, 105-107,
134-136, 165-167, and 220-222, preserving each test’s complete expected API
path.

@mklocek
mklocek merged commit 0597ec6 into main Aug 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants