Skip to content

Conversation

@DagonWat
Copy link

@DagonWat DagonWat commented Dec 9, 2025

Motivation

Add Sandbox Projects API support for Rybu SDK

Changes

  • Adding 2 new classes for Projects and Inboxes
  • Adding API support for Projects queries
  • Add new examples and specs

Summary by CodeRabbit

  • New Features

    • Added Projects API with full CRUD and new Project and Inbox data models.
  • Tests

    • Added comprehensive test suite and many recorded HTTP fixtures for Projects API flows.
    • Introduced a new test matcher and improved fixture sanitization (expanded sensitive-field redaction and removal of large CSP header blocks).
  • Documentation

    • Updated README with Projects API usage and contribution guidance; added example script.
  • Refactor

    • Simplified several DTOs by removing custom hash-serialization methods; adjusted related tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

📝 Walkthrough

Walkthrough

Adds a Projects API: new DTOs (Project, Inbox), a ProjectsAPI client with CRUD methods and response mapping, VCR fixtures and specs, example usage, spec helper changes, removal of several DTO to_h overrides, and README updates to include Projects API.

Changes

Cohort / File(s) Summary
Entrypoint
lib/mailtrap.rb
Added require_relative 'mailtrap/projects_api'.
New DTOs
lib/mailtrap/inbox.rb, lib/mailtrap/project.rb
Introduced Mailtrap::Inbox and Mailtrap::Project as Structs with keyword_init: true.
API Wrapper
lib/mailtrap/projects_api.rb
Added Mailtrap::ProjectsAPI with list, get, create, update, delete, wrap_request, handle_response, and response_class/supported_options configuration; uses base_* helpers.
VCR Fixtures (ProjectsAPI)
spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/...
Added many cassettes covering list/get/create/update/delete success and error scenarios (401, 404, 422).
Specs (ProjectsAPI & Project DTO)
spec/mailtrap/projects_api_spec.rb, spec/mailtrap/project_spec.rb
New tests exercising ProjectsAPI flows and Project DTO initialization/mapping.
Examples & Docs
examples/projects_api.rb, README.md
Added example script and README entries for Projects API; updated contributing notes and removed old Email Templates usage block.
Test infra & helpers
spec/spec_helper.rb
Added VCR before_record hook to strip CSP header, extended sensitive data redaction, and introduced match_struct RSpec matcher.
Removed DTO to_h overrides
lib/mailtrap/contact.rb, lib/mailtrap/contact_field.rb, lib/mailtrap/contact_import.rb, lib/mailtrap/contact_list.rb, lib/mailtrap/email_template.rb, lib/mailtrap/suppression.rb
Removed custom to_h methods from several DTO structs (now rely on default Struct#to_h).
Fixture cleanup & test updates
spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/*, spec/fixtures/vcr_cassettes/Mailtrap_SuppressionsAPI/*, spec/mailtrap/contact_spec.rb, spec/mailtrap/email_template_spec.rb, spec/mailtrap/suppression_spec.rb, spec/mailtrap/email_templates_api_spec.rb
Removed Content-Security-Policy headers from many cassettes; removed/updated tests that referenced removed to_h; updated EmailTemplatesAPI spec to use match_struct with expanded expectations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant ProjectsAPI as ProjectsAPI
  participant HTTPClient as HTTP Layer
  participant MailtrapAPI as mailtrap.io

  Client->>ProjectsAPI: list/get/create/update/delete(...)
  ProjectsAPI->>ProjectsAPI: wrap_request (create/update)
  ProjectsAPI->>HTTPClient: send HTTP request (Bearer token)
  HTTPClient->>MailtrapAPI: request
  MailtrapAPI-->>HTTPClient: response (JSON or error)
  HTTPClient-->>ProjectsAPI: parsed response
  ProjectsAPI->>ProjectsAPI: handle_response -> build DTOs (Inbox, Project)
  ProjectsAPI-->>Client: return DTO(s) or raise Error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

  • Add Suppressions API #73: Related changes to Suppression DTO and to_h behavior; touches similar DTO files and entrypoint requires.

Suggested reviewers

  • IgorDobryn
  • mklocek
  • igorlvicente

"I hopped through code with a twitchy nose,
New Projects sprang where fixture gardens grow.
Structs in burrows, APIs hum a tune,
Tests and cassettes light up like the moon.
— 🐇"

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description is incomplete. It lacks the 'How to test' section with checkboxes and the 'Images and GIFs' comparison table required by the template. Add the missing 'How to test' section with specific test instructions and checkboxes, and include the 'Images and GIFs' section if applicable to this change.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Sandbox Projects API' directly and clearly summarizes the main change: introducing Projects API support to the SDK.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
spec/mailtrap/projects_api_spec.rb (1)

60-89: "with hash request" contexts don't exercise hash-argument calls

Both #create and #update examples use project_api.create(**request) and project_api.update(project_id, **request) in their parent subjects (lines 61 and 108). The nested "with hash request" contexts (lines 76–89 and 129–142) only redefine let(:request) but inherit the parent subject, so they still unpack the hash with ** and hit the same code path.

To verify both calling conventions, redefine subject in the nested contexts to pass the raw Hash argument:

   context 'with hash request' do
+    subject(:create) { project_api.create(request) }
+
     let(:request) do
       {
         name: 'New Project'
       }
     end
     
     it 'maps response data to Project object' do
       expect(create).to be_a(Mailtrap::Project)
       expect(create).to have_attributes(
-                          name: 'New Project'
+        name: 'New Project'
       )
     end
   end

Apply the same fix to the #update context (lines 129–142), substituting subject(:update) { project_api.update(project_id, request) }.

Also applies to: 129-142

lib/mailtrap/project.rb (1)

25-32: Use safe navigation (&.) for inbox mapping

Rubocop flags this code with Style/SafeNavigation. Simplify the initialization of @inboxes using safe navigation:

-      raw_inboxes = args[:inboxes]
-      @inboxes =
-        if raw_inboxes
-          raw_inboxes.map do |inbox|
-            inbox.is_a?(Mailtrap::Inbox) ? inbox : Mailtrap::Inbox.new(**inbox)
-          end
-        end
+      raw_inboxes = args[:inboxes]
+      @inboxes = raw_inboxes&.map do |inbox|
+        inbox.is_a?(Mailtrap::Inbox) ? inbox : Mailtrap::Inbox.new(**inbox)
+      end

Semantics remain unchanged (@inboxes is nil when no inboxes are provided), but this reduces nesting and aligns with idiomatic Ruby style.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4259c5 and 7a51baf.

📒 Files selected for processing (19)
  • lib/mailtrap.rb (1 hunks)
  • lib/mailtrap/inbox.rb (1 hunks)
  • lib/mailtrap/project.rb (1 hunks)
  • lib/mailtrap/projects_api.rb (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/maps_response_data_to_Project_object.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/with_hash_request/maps_response_data_to_Project_object.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/returns_deleted_project_id.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/when_project_does_not_exist/raises_not_found_error.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/when_project_does_not_exist/raises_not_found_error.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/maps_response_data_to_Project_object.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_project_does_not_exist/raises_not_found_error.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_updating_only_name/updates_only_the_name_field.yml (1 hunks)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/with_hash_request/maps_response_data_to_Project_object.yml (1 hunks)
  • spec/mailtrap/project_spec.rb (1 hunks)
  • spec/mailtrap/projects_api_spec.rb (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
lib/mailtrap/inbox.rb (1)
lib/mailtrap/project.rb (1)
  • to_h (35-43)
spec/mailtrap/project_spec.rb (2)
lib/mailtrap/inbox.rb (1)
  • to_h (58-60)
lib/mailtrap/project.rb (1)
  • to_h (35-43)
spec/mailtrap/projects_api_spec.rb (1)
lib/mailtrap/projects_api.rb (6)
  • list (18-20)
  • include (8-67)
  • get (26-28)
  • create (36-38)
  • update (46-48)
  • delete (54-56)
lib/mailtrap/project.rb (1)
lib/mailtrap/inbox.rb (1)
  • to_h (58-60)
lib/mailtrap/projects_api.rb (1)
lib/mailtrap/base_api.rb (7)
  • supported_options (27-29)
  • response_class (31-33)
  • base_list (67-70)
  • base_get (46-49)
  • base_create (51-55)
  • base_update (57-61)
  • base_delete (63-65)
🪛 GitHub Actions: Ruby
spec/mailtrap/project_spec.rb

[error] 54-54: Rubocop: Layout/FirstArrayElementIndentation: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis. ({ ...


[error] 59-59: Rubocop: Layout/FirstArrayElementIndentation: Indent the right bracket the same as the first position after the preceding left parenthesis. })

spec/mailtrap/projects_api_spec.rb

[error] 42-42: Rubocop: Layout/FirstArgumentIndentation: Indent the first argument one step more than the start of the previous line. (id: project_id, ...)


[error] 72-72: Rubocop: Layout/FirstArgumentIndentation: Indent the first argument one step more than the start of the previous line. (name: 'New Project')


[error] 86-86: Rubocop: Layout/FirstArgumentIndentation: Indent the first argument one step more than the start of the previous line. (name: 'New Project')


[error] 125-125: Rubocop: Layout/FirstArgumentIndentation: Indent the first argument one step more than the start of the previous line. (name: 'Updated Project')


[error] 139-139: Rubocop: Layout/FirstArgumentIndentation: Indent the first argument one step more than the start of the previous line. (name: 'Updated Project')


[error] 150-150: Rubocop: Layout/FirstArgumentIndentation: Indent the first argument one step more than the start of the previous line. (name: 'New Name Only')

lib/mailtrap/project.rb

[error] 15-15: Rubocop: Style/SymbolArray: Use %i or %I for an array of symbols. [:id, :name, :share_links, :inboxes, :permissions]


[error] 27-27: Rubocop: Style/SafeNavigation: Use safe navigation (&.) instead of checking if an object exists before calling the method. (if raw_inboxes ...)

🔇 Additional comments (9)
spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/when_project_does_not_exist/raises_not_found_error.yml (1)

1-331: LGTM! Test fixture properly captures the not-found error scenario.

The VCR cassette correctly records the project creation setup and the subsequent 404 response when attempting to retrieve a non-existent project, with sensitive data appropriately redacted.

spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_updating_only_name/updates_only_the_name_field.yml (1)

1-333: LGTM! Fixture correctly validates partial update behavior.

This cassette appropriately tests updating only the name field, demonstrating that the API supports partial updates without requiring all fields.

spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/with_hash_request/maps_response_data_to_Project_object.yml (1)

1-333: LGTM! Test fixture correctly captures the update flow.

The cassette appropriately records the project creation and update lifecycle, verifying the hash request format and response mapping.

lib/mailtrap/inbox.rb (1)

30-61: LGTM! Clean DTO implementation with appropriate serialization.

The Struct-based approach with keyword_init: true and the compacted to_h override provide a clean, idiomatic Ruby DTO implementation.

spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_project_does_not_exist/raises_not_found_error.yml (1)

1-331: LGTM! Fixture properly tests update error handling.

Correctly captures the 404 response when attempting to update a non-existent project.

lib/mailtrap.rb (1)

13-13: LGTM! Projects API properly wired into the module.

The require statement is correctly positioned and follows the existing pattern for other API modules.

spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml (1)

1-166: LGTM! Fixture correctly captures validation error response.

Appropriately tests the API's validation behavior when creating a project with an invalid (empty) name, returning a 422 status with detailed error messages.

spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/with_hash_request/maps_response_data_to_Project_object.yml (1)

1-168: LGTM! Fixture properly records successful project creation.

Correctly captures the hash request format and the successful 200 response with full project details.

spec/mailtrap/projects_api_spec.rb (1)

39-45: Fix have_attributes indentation to satisfy Rubocop Layout/FirstArgumentIndentation

Rubocop is flagging the first arguments to have_attributes as over‑indented. Align them one indentation step past the previous line, e.g.:

-      expect(get).to have_attributes(
-                       id: project_id,
-                       name: 'Test Project'
-                     )
+      expect(get).to have_attributes(
+        id: project_id,
+        name: 'Test Project'
+      )
@@
-      expect(create).to have_attributes(
-                          name: 'New Project'
-                        )
+      expect(create).to have_attributes(
+        name: 'New Project'
+      )
@@
-        expect(create).to have_attributes(
-                            name: 'New Project'
-                          )
+        expect(create).to have_attributes(
+          name: 'New Project'
+        )
@@
-      expect(update).to have_attributes(
-                          name: 'Updated Project'
-                        )
+      expect(update).to have_attributes(
+        name: 'Updated Project'
+      )
@@
-        expect(update).to have_attributes(
-                            name: 'Updated Project'
-                          )
+        expect(update).to have_attributes(
+          name: 'Updated Project'
+        )
@@
-        expect(update).to have_attributes(
-                            name: 'New Name Only'
-                          )
+        expect(update).to have_attributes(
+          name: 'New Name Only'
+        )

This should clear the Layout/FirstArgumentIndentation offenses reported in CI. Based on pipeline failures, this is currently blocking.

Also applies to: 69-74, 83-88, 122-127, 136-141, 147-151

Comment on lines 53 to 60
expect(project.inboxes.map(&:to_h)).to eq([
{
id: 456,
name: 'Test Inbox',
username: '[email protected]'
}
])
end
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix array indentation to resolve Rubocop violations.

The array starting at line 54 and closing at line 59 has incorrect indentation, causing CI failures.

Apply this diff to fix the indentation:

-      expect(project.inboxes.map(&:to_h)).to eq([
-        {
-          id: 456,
-          name: 'Test Inbox',
-          username: '[email protected]'
-        }
-      ])
+      expect(project.inboxes.map(&:to_h)).to eq(
+        [{
+          id: 456,
+          name: 'Test Inbox',
+          username: '[email protected]'
+        }]
+      )
📝 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
expect(project.inboxes.map(&:to_h)).to eq([
{
id: 456,
name: 'Test Inbox',
username: '[email protected]'
}
])
end
expect(project.inboxes.map(&:to_h)).to eq(
[{
id: 456,
name: 'Test Inbox',
username: '[email protected]'
}]
)
end
🧰 Tools
🪛 GitHub Actions: Ruby

[error] 54-54: Rubocop: Layout/FirstArrayElementIndentation: Use 2 spaces for indentation in an array, relative to the first position after the preceding left parenthesis. ({ ...


[error] 59-59: Rubocop: Layout/FirstArrayElementIndentation: Indent the right bracket the same as the first position after the preceding left parenthesis. })

🤖 Prompt for AI Agents
In spec/mailtrap/project_spec.rb around lines 53 to 60, the multi-line array
used in the expectation is mis-indented causing RuboCop failures; fix it by
aligning the array elements and closing bracket with the expect call indent
level (indent the inner hash one additional level so each key/value and the
closing brace line up consistently and the final closing ]) aligns with the
expect(...).to eq([ line).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a51baf and 657ad0a.

📒 Files selected for processing (4)
  • lib/mailtrap/inbox.rb (1 hunks)
  • lib/mailtrap/project.rb (1 hunks)
  • spec/mailtrap/project_spec.rb (1 hunks)
  • spec/mailtrap/projects_api_spec.rb (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • spec/mailtrap/projects_api_spec.rb
  • spec/mailtrap/project_spec.rb
🧰 Additional context used
🧬 Code graph analysis (1)
lib/mailtrap/inbox.rb (1)
lib/mailtrap/project.rb (1)
  • to_h (33-41)
🔇 Additional comments (1)
lib/mailtrap/project.rb (1)

4-41: Project DTO and inbox normalization are correctly implemented.

The Project class properly handles inbox initialization, supporting both Mailtrap::Inbox instances and raw hashes. The to_h method correctly serializes inboxes via safe navigation and mapping. Tests confirm the implementation works as expected with symbol-keyed inbox hashes, which is the standard pattern throughout the codebase.

@DagonWat DagonWat requested review from IgorDobryn and i7an December 10, 2025 11:30
Comment on lines -108 to -123
### Email Templates API

```ruby
require 'mailtrap'

client = Mailtrap::Client.new(api_key: 'your-api-key')
templates = Mailtrap::EmailTemplatesAPI.new 3229, client

templates.create(
name: 'Welcome Email',
subject: 'Welcome to Mailtrap!',
body_html: '<h1>Hello</h1>',
body_text: 'Hello',
category: 'welcome'
)
```
Copy link
Author

Choose a reason for hiding this comment

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

Email templates are already a big chunk of examples, I see no reason to have it here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
lib/mailtrap/inbox.rb (1)

4-29: Double‑check the @see link points to inbox, not project, docs.

The DTO and comments clearly describe an inbox, but the @see URL slug ends with create-project. If there is an inbox‑specific endpoint doc, consider linking that instead so consumers don’t confuse project vs inbox semantics. If this URL is intentionally shared between project and inbox, a brief note in the comment could clarify that.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 657ad0a and c5132a5.

📒 Files selected for processing (3)
  • README.md (3 hunks)
  • examples/projects_api.rb (1 hunks)
  • lib/mailtrap/inbox.rb (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • README.md
  • examples/projects_api.rb
🧰 Additional context used
🧬 Code graph analysis (1)
lib/mailtrap/inbox.rb (1)
lib/mailtrap/project.rb (1)
  • to_h (33-41)
🔇 Additional comments (1)
lib/mailtrap/inbox.rb (1)

30-61: Inbox struct definition and to_h override look correct and consistent.

Member list matches the documented attributes, keyword_init: true gives a clean API for callers, and super.compact aligns with the existing DTO pattern of dropping nils before serialization. No functional issues spotted here; just ensure the gem’s minimum Ruby version supports Struct.new(..., keyword_init: true).

@DagonWat DagonWat requested a review from i7an December 23, 2025 12:23
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
lib/mailtrap/projects_api.rb (1)

50-56: Update YARD documentation to reflect actual return value.

The documentation states @return nil, but base_delete delegates to client.delete and returns its response. Based on the test expectations in spec/mailtrap/projects_api_spec.rb, this returns a hash like { id: project_id }.

🔎 Proposed documentation fix
 # Deletes a project
 # @param project_id [Integer] The project ID
-# @return nil
+# @return [Hash] The deleted project response, e.g. { id: project_id }
 # @!macro api_errors
 def delete(project_id)
   base_delete(project_id)
 end
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5132a5 and 3cf8e26.

📒 Files selected for processing (2)
  • lib/mailtrap/project.rb
  • lib/mailtrap/projects_api.rb
🧰 Additional context used
🧬 Code graph analysis (2)
lib/mailtrap/project.rb (1)
lib/mailtrap/inbox.rb (1)
  • to_h (58-60)
lib/mailtrap/projects_api.rb (1)
lib/mailtrap/base_api.rb (7)
  • supported_options (27-29)
  • response_class (31-33)
  • base_list (67-70)
  • base_get (46-49)
  • base_create (51-55)
  • base_update (57-61)
  • base_delete (63-65)
🪛 GitHub Actions: Ruby
lib/mailtrap/projects_api.rb

[error] 60-60: RuboCop: Style/KeywordArgumentsMerging - Provide additional arguments directly rather than using merge. Autocorrectable. Command: 'bundle exec rubocop'.

🔇 Additional comments (2)
lib/mailtrap/project.rb (1)

1-25: LGTM! Clean and simple DTO implementation.

The Project struct follows the established pattern (consistent with Inbox), provides appropriate documentation, and keeps the implementation minimal as intended. The to_h method correctly returns a compacted hash representation.

lib/mailtrap/projects_api.rb (1)

70-78: LGTM! Private helper methods are correctly implemented.

The base_path and wrap_request methods properly support the BaseAPI delegation pattern with appropriate path construction and request wrapping.

) do
# @return [Hash] The inbox attributes as a hash
def to_h
super.compact
Copy link
Contributor

Choose a reason for hiding this comment

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

last_message_sent_at can be nil. Compacting will remove it. Is it necessary?

Copy link
Author

Choose a reason for hiding this comment

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

if it's nil and you call inbox.to_h[:last_message_sent_at] it will return nil, the only difference is that when you visually go through the fields in console output - its not seen.

If we do it in all other Struct's I think we should either live with it or create a task for future to remove compact everywhere.

- '142'
Cache-Control:
- no-cache
Content-Security-Policy:
Copy link
Contributor

Choose a reason for hiding this comment

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

We can configure VCR to skip this header. Cassettes will be clearer

end

it 'creates a project with all attributes' do
expect(project).to have_attributes(
Copy link
Contributor

Choose a reason for hiding this comment

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

This kind of assertion creates a risk for some attrs to remain unchecked. You can use match assertion

Copy link
Author

Choose a reason for hiding this comment

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

while on it I am curious whats the best way to match a struct with some fields? a regular match is not working as such, I fear.

I can create a custom matcher for structs, but it will utilize our to_h which is also removing some fields.

base_delete(project_id)
end

def build_entity(options, response_class)
Copy link
Contributor

Choose a reason for hiding this comment

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

  • there is no test that fails if you change the implementation of the method
  • your code will fail if we add a new field to the inbox response
  • please override handle_response instead.
    • consider using build_entity for both projects and inboxes
    • see if you need to update contacts api
Mailtrap::Inbox.new new_field: 1
# => (irb):2:in 'Struct#initialize': unknown keywords: new_field (ArgumentError)


def handle_response(response)
  build_entity(
    response.merge(
      inboxes: options[:inboxes]&.map { |inbox| build_entity(inbox, Mailtrap::Inbox) }
    ),
    response_class
  )
end

response_class.new(
**options.slice(*(response_class.members - [:inboxes])),
inboxes: options[:inboxes]&.map do |inbox|
inbox.is_a?(Mailtrap::Inbox) ? inbox : Mailtrap::Inbox.new(**inbox)
Copy link
Contributor

Choose a reason for hiding this comment

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

build_entity is called for the response. could you please explain when do you expect the inbox to be an instance of Inbox class?

) do
# @return [Hash] The Project attributes as a hash
def to_h
super.compact
Copy link
Contributor

Choose a reason for hiding this comment

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

Mailtrap::Project.new(inboxes:[Mailtrap::Inbox.new]).to_h[:inboxes][0].class
# => Mailtrap::Inbox(keyword_init: true)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
spec/spec_helper.rb (1)

55-69: Enhance failure messages to improve debugging experience.

The custom matcher is functional but the failure messages are too generic. When a test fails, developers won't know which specific attribute didn't match or what the actual vs. expected values were, making debugging significantly harder.

🔎 Proposed improvement with detailed failure messages
 RSpec::Matchers.define :match_struct do |expected_attributes|
   match do |actual_struct|
     expected_attributes.all? do |key, expected_value|
       actual_struct.respond_to?(key) && actual_struct[key] == expected_value
     end
   end
 
   failure_message do |actual_struct|
-    "expected #{actual_struct.inspect} to match #{expected_attributes}, but did not match"
+    mismatches = expected_attributes.reject do |key, expected_value|
+      actual_struct.respond_to?(key) && actual_struct[key] == expected_value
+    end
+    
+    details = mismatches.map do |key, expected_value|
+      if !actual_struct.respond_to?(key)
+        "  #{key}: missing attribute"
+      else
+        "  #{key}: expected #{expected_value.inspect}, got #{actual_struct[key].inspect}"
+      end
+    end.join("\n")
+    
+    "expected #{actual_struct.inspect} to match all attributes, but found mismatches:\n#{details}"
   end
 
   failure_message_when_negated do |actual_struct|
     "expected #{actual_struct.inspect} not to match #{expected_attributes}, but it did match"
   end
 end

Optional: Consider consistent access pattern

The matcher checks respond_to?(key) but uses bracket notation actual_struct[key]. For better consistency, either check respond_to?(:[]) or use actual_struct.public_send(key) for access. The current implementation works for Struct objects but is conceptually mixed.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2173be8 and 107f2ba.

📒 Files selected for processing (1)
  • spec/spec_helper.rb
🧰 Additional context used
🧬 Code graph analysis (1)
spec/spec_helper.rb (2)
lib/mailtrap/client.rb (1)
  • delete (214-220)
lib/mailtrap/suppressions_api.rb (1)
  • delete (27-29)
🔇 Additional comments (1)
spec/spec_helper.rb (1)

14-16: LGTM! Clean approach to reducing cassette brittleness.

Removing the Content-Security-Policy header from recorded responses is a reasonable approach for API testing, where CSP headers are typically irrelevant. This prevents test failures from CSP policy changes and keeps cassettes cleaner.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/mailtrap/contact_import.rb (1)

10-17: Verify that removing compact behavior doesn't break existing integrations.

The removal of the to_h override changes the serialization behavior—to_h will now include nil values in the hash, whereas the previous implementation excluded them via .compact. This could affect API request serialization or downstream consumers expecting compacted hashes.

Run the following script to verify how ContactImport instances are serialized and whether the compact behavior is relied upon:

#!/bin/bash
# Description: Check for serialization patterns and usages of ContactImport#to_h

# Search for ContactImport instantiation and to_h calls
rg -nP --type=rb -C3 'ContactImport\.(new|to_h)'

# Search for JSON serialization of ContactImport
rg -nP --type=rb -C3 'ContactImport.*\.to_json'

# Check if ContactImport is used in API request bodies
rg -nP --type=rb -C5 'ContactImport.*body|request.*ContactImport'
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 107f2ba and dee41d1.

📒 Files selected for processing (10)
  • lib/mailtrap/contact.rb
  • lib/mailtrap/contact_field.rb
  • lib/mailtrap/contact_import.rb
  • lib/mailtrap/contact_list.rb
  • lib/mailtrap/email_template.rb
  • lib/mailtrap/inbox.rb
  • lib/mailtrap/project.rb
  • lib/mailtrap/suppression.rb
  • spec/mailtrap/contact_spec.rb
  • spec/mailtrap/project_spec.rb
💤 Files with no reviewable changes (2)
  • lib/mailtrap/contact.rb
  • spec/mailtrap/contact_spec.rb
🚧 Files skipped from review as they are similar to previous changes (1)
  • spec/mailtrap/project_spec.rb
🔇 Additional comments (4)
lib/mailtrap/contact_field.rb (1)

12-12: Verify that removing compact behavior doesn't break existing integrations.

Same concern applies: the default to_h behavior will include nil values, changing serialization output compared to the previous .compact implementation.

Likely an incorrect or invalid review comment.

lib/mailtrap/contact_list.rb (1)

8-8: Verify that removing compact behavior doesn't break existing integrations.

Same concern: removing the to_h override changes serialization to include nil values, which could affect consumers expecting compacted hashes.

Likely an incorrect or invalid review comment.

lib/mailtrap/inbox.rb (1)

1-57: LGTM! Clean and well-documented DTO.

The Inbox struct is properly defined with comprehensive YARD documentation for all 25 fields. Past documentation issues (inbox vs project terminology, type annotations) have been resolved. The use of keyword_init: true follows Ruby best practices for struct initialization.

lib/mailtrap/project.rb (1)

1-20: LGTM! Clean and simple DTO.

The Project struct is well-defined with clear documentation. The design keeps the DTO simple as a plain struct with keyword_init: true, following the feedback from past reviews to avoid custom methods and maintain simplicity. All fields are properly documented with appropriate type annotations.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI Agents
In @lib/mailtrap/projects_api.rb:
- Around line 50-56: The YARD @return is incorrect: the delete(project_id)
method calls base_delete(project_id) which returns the client response (e.g., a
Hash like { id: project_id }), not nil; update the comment for delete to reflect
the actual return type and shape (for example "@return [Hash] response hash
containing the deleted project id" or similar) and ensure any related docs/macro
references match the new return description; keep the method body unchanged (it
should still call base_delete(project_id)).
🧹 Nitpick comments (2)
lib/mailtrap/projects_api.rb (2)

58-65: Consider adding YARD documentation for this method.

The handle_response method implements important transformation logic (mapping nested inboxes) but lacks documentation explaining its purpose, parameters, and return value.

📝 Suggested documentation
# Transforms the API response into a Project entity with nested Inbox objects
# @param response [Hash] The raw API response data
# @return [Project] The constructed Project entity with mapped Inbox instances
def handle_response(response)
  # ... existing implementation
end

58-66: Consider moving handle_response to the private section.

Since handle_response is an internal implementation detail called only by BaseAPI methods, moving it below the private keyword (line 67) would better encapsulate the class's public API surface.

📝 Suggested change
     base_delete(project_id)
   end

+  private
+
   def handle_response(response)
     build_entity(
       response.merge(
         inboxes: response[:inboxes]&.map { |inbox| build_entity(inbox, Mailtrap::Inbox) }
       ),
       response_class
     )
   end

-  private
-
   def base_path
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dee41d1 and 886a763.

📒 Files selected for processing (1)
  • lib/mailtrap/projects_api.rb
🧰 Additional context used
🧬 Code graph analysis (1)
lib/mailtrap/projects_api.rb (1)
lib/mailtrap/base_api.rb (8)
  • supported_options (27-29)
  • response_class (31-33)
  • base_list (67-70)
  • base_get (46-49)
  • base_create (51-55)
  • base_update (57-61)
  • base_delete (63-65)
  • build_entity (42-44)
🔇 Additional comments (3)
lib/mailtrap/projects_api.rb (3)

15-28: LGTM! Clean delegation to base methods.

The list and get methods correctly delegate to base_list and base_get respectively, with proper YARD documentation.


30-48: LGTM! Proper delegation with comprehensive documentation.

The create and update methods are well-documented and correctly delegate to their base implementations.


67-76: LGTM! Private helpers are correctly implemented.

The base_path and wrap_request methods follow the expected patterns for BaseAPI integration.

Comment on lines +50 to +56
# Deletes a project
# @param project_id [Integer] The project ID
# @return nil
# @!macro api_errors
def delete(project_id)
base_delete(project_id)
end
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Update the return value documentation.

The YARD comment states @return nil, but base_delete returns the response from client.delete, which the specs expect to be a hash like { id: project_id }.

📝 Suggested fix
-    # @return nil
+    # @return [Hash] The deleted project response, e.g., { id: project_id }
🤖 Prompt for AI Agents
In @lib/mailtrap/projects_api.rb around lines 50 - 56, The YARD @return is
incorrect: the delete(project_id) method calls base_delete(project_id) which
returns the client response (e.g., a Hash like { id: project_id }), not nil;
update the comment for delete to reflect the actual return type and shape (for
example "@return [Hash] response hash containing the deleted project id" or
similar) and ensure any related docs/macro references match the new return
description; keep the method body unchanged (it should still call
base_delete(project_id)).

- W/"068e7482a443be1cfa101ad7b01feb49"
Cache-Control:
- max-age=0, private, must-revalidate
Content-Security-Policy:
Copy link
Contributor

Choose a reason for hiding this comment

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

you added

interaction.response.headers.delete('Content-Security-Policy')

Please make sure the header is removed from ALL cassettes.

base_delete(project_id)
end

def handle_response(response)
Copy link
Contributor

Choose a reason for hiding this comment

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

must be private.


RSpec::Matchers.define :match_struct do |expected_attributes|
match do |actual_struct|
expected_attributes.all? do |key, expected_value|
Copy link
Contributor

Choose a reason for hiding this comment

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

  • how is it different from have_attributes?
  • it is used in one place. do you need to extract it to a helper?

def handle_response(response)
build_entity(
response.merge(
inboxes: response[:inboxes]&.map { |inbox| build_entity(inbox, Mailtrap::Inbox) }
Copy link
Contributor

Choose a reason for hiding this comment

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

it is still not covered by any spec.

end
end

describe '#to_h' do
Copy link
Contributor

Choose a reason for hiding this comment

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

do you need to test it? please simplify because you call the standard method.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @spec/mailtrap/project_spec.rb:
- Around line 60-66: Remove the redundant expectation that asserts
project.inboxes; the structure of inboxes is already validated by the prior
match_struct matcher, so delete the expect(project.inboxes).to eq([...]) block
in spec/mailtrap/project_spec.rb (the assertion referencing project.inboxes) and
keep only the match_struct-based assertion to avoid duplicate checks.
🧹 Nitpick comments (1)
spec/mailtrap/email_templates_api_spec.rb (1)

51-61: Consider matcher consistency across test methods.

The #get test now uses match_struct for stricter validation (exact structural matching), while #list (line 14), #create (line 92), and #update (lines 165, 183, 196, etc.) continue using have_attributes (partial matching).

Is this intentional? If match_struct provides better validation by ensuring no unexpected fields are present, consider applying it consistently to other test methods. If have_attributes is sufficient, this stricter approach may be unnecessary for just #get.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 886a763 and 471f831.

📒 Files selected for processing (50)
  • lib/mailtrap/projects_api.rb
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/with_hash_request/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/with_hash_request/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_delete/returns_no_content.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_delete/when_template_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/when_template_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/maps_response_data_to_EmailTemplate_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/returns_an_array_of_EmailTemplate_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_template_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/preserves_other_fields_unchanged.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/updates_only_the_specified_fields.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/preserves_other_fields_unchanged.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/updates_only_the_body_html_field.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/preserves_other_fields_unchanged.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/updates_only_the_category_field.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/with_hash_request/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/with_hash_request/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/with_hash_request/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/returns_deleted_project_id.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/when_project_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/when_project_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_project_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_updating_only_name/updates_only_the_name_field.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/with_hash_request/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_SuppressionsAPI/vcr_list/maps_response_data_to_Suppression_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_SuppressionsAPI/vcr_list/when_api_key_is_incorrect/raises_authorization_error.yml
  • spec/mailtrap/contact_spec.rb
  • spec/mailtrap/email_template_spec.rb
  • spec/mailtrap/email_templates_api_spec.rb
  • spec/mailtrap/project_spec.rb
  • spec/mailtrap/projects_api_spec.rb
  • spec/mailtrap/suppression_spec.rb
  • spec/spec_helper.rb
💤 Files with no reviewable changes (32)
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/with_hash_request/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/maps_response_data_to_EmailTemplate_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_SuppressionsAPI/vcr_list/when_api_key_is_incorrect/raises_authorization_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_template_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/with_hash_request/returns_an_EmailTemplate_object.yml
  • spec/mailtrap/email_template_spec.rb
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml
  • spec/mailtrap/suppression_spec.rb
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/preserves_other_fields_unchanged.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_delete/when_template_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/preserves_other_fields_unchanged.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/with_hash_request/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_delete/returns_no_content.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/preserves_other_fields_unchanged.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/updates_only_the_body_html_field.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/with_hash_request/maps_response_data_to_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/updates_only_the_category_field.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/updates_only_the_specified_fields.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/maps_response_data_to_EmailTemplate_object.yml
  • spec/mailtrap/contact_spec.rb
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/returns_an_EmailTemplate_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/returns_an_array_of_EmailTemplate_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_SuppressionsAPI/vcr_list/maps_response_data_to_Suppression_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/when_template_does_not_exist/raises_not_found_error.yml
✅ Files skipped from review due to trivial changes (4)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/with_hash_request/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/when_project_does_not_exist/raises_not_found_error.yml
🚧 Files skipped from review as they are similar to previous changes (9)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/when_api_key_is_incorrect/raises_authorization_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_project_does_not_exist/raises_not_found_error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/returns_deleted_project_id.yml
  • lib/mailtrap/projects_api.rb
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/with_hash_request/maps_response_data_to_Project_object.yml
  • spec/spec_helper.rb
  • spec/mailtrap/projects_api_spec.rb
🧰 Additional context used
🧬 Code graph analysis (1)
spec/mailtrap/email_templates_api_spec.rb (3)
lib/mailtrap/projects_api.rb (1)
  • get (26-28)
lib/mailtrap/client.rb (1)
  • get (173-180)
lib/mailtrap/email_templates_api.rb (1)
  • get (25-27)
🪛 Gitleaks (8.30.0)
spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml

[high] 52-52: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (2)
spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_updating_only_name/updates_only_the_name_field.yml (1)

1-155: LGTM! Well-secured VCR fixture.

The fixture correctly records a two-step interaction for testing the project name update operation. Sensitive data is properly redacted (Bearer tokens at lines 19, 95 and share links at lines 77, 153), which is essential for test fixtures committed to version control.

spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml (1)

78-80: Verify that credentials in the response body are sandbox/test data.

The recorded response contains passwords, share link tokens, and other potentially sensitive credentials. While the Bearer token is properly redacted (line 19), the response body includes values like "password":"8a0b5f0ce91239" and admin/viewer share links with embedded tokens.

Please confirm these are sandbox or test credentials that are safe to commit to the repository.

Comment on lines +60 to +66
expect(project.inboxes).to eq([
{
id: 456,
name: 'Test Inbox',
username: '[email protected]'
}
])
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove redundant inboxes assertion.

The project.inboxes assertion duplicates the verification already performed by the match_struct matcher at lines 51-57. Since match_struct already validates the inboxes field structure, this additional expectation is unnecessary and should be removed to simplify the test.

♻️ Proposed refactor
       )
-
-      expect(project.inboxes).to eq([
-                                      {
-                                        id: 456,
-                                        name: 'Test Inbox',
-                                        username: '[email protected]'
-                                      }
-                                    ])
     end
📝 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
expect(project.inboxes).to eq([
{
id: 456,
name: 'Test Inbox',
username: '[email protected]'
}
])
)
end
🤖 Prompt for AI Agents
In @spec/mailtrap/project_spec.rb around lines 60 - 66, Remove the redundant
expectation that asserts project.inboxes; the structure of inboxes is already
validated by the prior match_struct matcher, so delete the
expect(project.inboxes).to eq([...]) block in spec/mailtrap/project_spec.rb (the
assertion referencing project.inboxes) and keep only the match_struct-based
assertion to avoid duplicate checks.

recorded_at: Thu, 06 Nov 2025 17:22:46 GMT
encoding: ASCII-8BIT
string: '{"id":2379735,"name":"Ruby SDK Project","share_links":{"admin":"https://mailtrap.io/projects/2379735/share/QEVuQwEA4L8h5hP_6bzwo0asWC1hzsSRHJJPQujPwhAhI961iBL7hn64PK1h6oHx6mD8UqHFkk29lPOrF8sLi6g6Hp1-qnHlCHM_80E4g-lzsGuxm9lJJYBxUFvyHTb9JxQ_xjua","viewer":"https://mailtrap.io/projects/2379735/share/QEVuQwEA4L8h5hP_6bzwo0asWC1hzsSRHJJPQujPwhAhI961iBL7hn64PK1h6oHx6mD8UqHFveri5ZpxVpC5y8XZGL4dCVGQTEAb7XXpm-EuVLFIEJimnAuJzsZ1-PJfrU2Y8CjM"},"inboxes":[{"id":4288340,"name":"main
sandbox","username":"bd18922c6cb70d","status":"active","email_username":"0c45dfcf78-45ee5d","email_username_enabled":true,"used":true,"forward_from_email_address":"[email protected]","project_id":2379735,"password":"8a0b5f0ce91239","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","sent_messages_count":0,"forwarded_messages_count":0,"emails_count":5,"emails_unread_count":5,"last_message_sent_at":"2026-01-05T10:44:19.839Z","max_size":200,"max_message_size":10485760,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":4293932,"name":"New
Copy link
Author

Choose a reason for hiding this comment

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

interestingly I got a message from GitGuardian that this username and password was leaked. Even though I changed them.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please clean those up from the YML files, add a rule to vcr and make sure to rotate passwords in prod.

@DagonWat DagonWat requested review from IgorDobryn and i7an January 7, 2026 11:59
Copy link
Contributor

@IgorDobryn IgorDobryn left a comment

Choose a reason for hiding this comment

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

We might need to improve structs matching

config.configure_rspec_metadata!

config.before_record do |interaction|
interaction.response.headers.delete('Content-Security-Policy')
Copy link
Contributor

Choose a reason for hiding this comment

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

👍


keys_ok = struct_keys.sort == expected_keys.sort

expected_ok && keys_ok
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if you can @actual = actual_struct.to_h and @expected = expected_struct.to and then match actual to expected as hashes and then use diffable?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @spec/spec_helper.rb:
- Around line 23-25: The three gsub! calls on interaction.response.body (the
calls that redact "username", "password", and "email") use regexes that require
no space after the colon; update those regexes to allow optional whitespace
around the colon (e.g., use patterns like '"username"\s*:\s*"[^"]*"' etc.) and
keep the replacement strings formatted with a single space after the colon
(e.g., '"username": "railsware"') so the redaction works whether or not there is
whitespace in the JSON.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 471f831 and 605cc2e.

📒 Files selected for processing (4)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml
  • spec/mailtrap/projects_api_spec.rb
  • spec/spec_helper.rb
🚧 Files skipped from review as they are similar to previous changes (2)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml
  • spec/mailtrap/projects_api_spec.rb
🔇 Additional comments (2)
spec/spec_helper.rb (2)

14-16: LGTM!

Removing the Content-Security-Policy header from recorded responses keeps cassettes cleaner and avoids irrelevant diff noise.


58-86: Well-designed custom matcher.

The match_struct matcher correctly validates both attribute values and ensures no extra keys exist. Good handling of both Struct objects (via members) and hash-like objects (via to_h), with clear failure messages.

Comment on lines 78 to 80
string: '{"id":2379735,"name":"Ruby SDK Project","share_links":{"admin":"https://mailtrap.io/projects/2379735/share/QEVuQwEA4L8h5hP_6bzwo0asWC1hzsSRHJJPQujPwhAhI961iBL7hn64PK1h6oHx6mD8UqHFkk29lPOrF8sLi6g6Hp1-qnHlCHM_80E4g-lzsGuxm9lJJYBxUFvyHTb9JxQ_xjua","viewer":"https://mailtrap.io/projects/2379735/share/QEVuQwEA4L8h5hP_6bzwo0asWC1hzsSRHJJPQujPwhAhI961iBL7hn64PK1h6oHx6mD8UqHFveri5ZpxVpC5y8XZGL4dCVGQTEAb7XXpm-EuVLFIEJimnAuJzsZ1-PJfrU2Y8CjM"},"inboxes":[{"id":4294015,"name":"Main
sandbox","username": "railsware","status":"active","email_username":"7e24d4e21f-d92640","email_username_enabled":true,"used":false,"forward_from_email_address":"[email protected]","project_id":2379735,"password":
"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","sent_messages_count":0,"forwarded_messages_count":0,"emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":200,"max_message_size":10485760,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}}],"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}}'
Copy link

@coderabbitai coderabbitai bot Jan 8, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the actual VCR cassette file
cat spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml

Repository: mailtrap/mailtrap-ruby

Length of output: 3186


🏁 Script executed:

# Search for other cassettes that contain share_links
rg -l "share_link" spec/fixtures/vcr_cassettes/ | head -10

Repository: mailtrap/mailtrap-ruby

Length of output: 1110


🏁 Script executed:

# Look for VCR configuration files that might define sanitization
find spec -name "*vcr*" -o -name "*cassette*" | grep -E "\.(rb|yml|yaml|json)$" | head -20

Repository: mailtrap/mailtrap-ruby

Length of output: 48


🏁 Script executed:

# Check for VCR setup and filter configuration in the codebase
rg -n "VCR.configure|filter_sensitive_data|before_record" spec/ --type rb

Repository: mailtrap/mailtrap-ruby

Length of output: 92


🏁 Script executed:

# Search for VCR setup in all ruby files
find spec -type f -name "*.rb" -exec grep -l "VCR\|cassette" {} \; | head -10

Repository: mailtrap/mailtrap-ruby

Length of output: 85


🏁 Script executed:

# Look for spec_helper or test configuration
find spec -type f \( -name "spec_helper*" -o -name "*vcr*" \) | head -20

Repository: mailtrap/mailtrap-ruby

Length of output: 85


🏁 Script executed:

# Check one of the other cassettes to see if share_links are also unmasked
head -100 spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/maps_response_data_to_Project_object.yml | grep -A 5 -B 5 "share_link"

Repository: mailtrap/mailtrap-ruby

Length of output: 536


🏁 Script executed:

# Look for any sanitization or masking patterns in cassettes
rg "xxxxxxxx|<[A-Z_]+>|REDACTED|MASKED" spec/fixtures/vcr_cassettes/ | head -20

Repository: mailtrap/mailtrap-ruby

Length of output: 14659


🏁 Script executed:

# Check if there's documentation about test account credentials
find . -type f \( -name "README*" -o -name "SECURITY*" -o -name "TEST*" \) | grep -v node_modules

Repository: mailtrap/mailtrap-ruby

Length of output: 77


🏁 Script executed:

# Read spec_helper.rb to find VCR configuration
cat spec/spec_helper.rb

Repository: mailtrap/mailtrap-ruby

Length of output: 2817


🏁 Script executed:

# Also check if there's any git history showing when this cassette was created/modified
git log --oneline spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml 2>/dev/null | head -5

Repository: mailtrap/mailtrap-ruby

Length of output: 104


🏁 Script executed:

# Compare the exact content: check if _get cassette has different sanitization approach
grep -o "share_links.*REDACTED\|share_links.*https" spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml

Repository: mailtrap/mailtrap-ruby

Length of output: 285


Replace unredacted share link tokens with REDACTED to match other cassettes.

The share link tokens in this cassette are exposed as real credentials (e.g., QEVuQwEA4L8h5hP_6bzwo0asWC1hzsSRHJJPQujPwhAhI961iBL7hn64PK1h6oHx6mD8UqHFkk29lPOrF8sLi6g6Hp1-qnHlCHM_80E4g-lzsGuxm9lJJYBxUFvyHTb9JxQ_xjua), while other cassettes in the same API test suite properly redact them as REDACTED. The VCR filter configuration does not currently include a pattern for sanitizing share_links, which allows these tokens to persist in the fixture.

Update this cassette to mask share link tokens consistently with the rest of the test fixtures, and add a filter to the VCR configuration to prevent this in future cassettes.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +23 to +25
interaction.response.body.gsub!(/"username":"[^"]*"/, '"username": "railsware"')
interaction.response.body.gsub!(/"password":"[^"]*"/, '"password": "xxxxxxxx"')
interaction.response.body.gsub!(/"email":"[^"]*"/, '"email": "[email protected]"')
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Regex patterns may fail to match if JSON has whitespace after colons.

These patterns assume no space between the colon and the value (e.g., "username":"value"), but JSON responses often include a space (e.g., "username": "value"). If the regex doesn't match, sensitive credentials could leak into recorded cassettes.

🔒 Proposed fix to handle optional whitespace
-    interaction.response.body.gsub!(/"username":"[^"]*"/, '"username": "railsware"')
-    interaction.response.body.gsub!(/"password":"[^"]*"/, '"password": "xxxxxxxx"')
-    interaction.response.body.gsub!(/"email":"[^"]*"/, '"email": "[email protected]"')
+    interaction.response.body.gsub!(/"username":\s*"[^"]*"/, '"username": "railsware"')
+    interaction.response.body.gsub!(/"password":\s*"[^"]*"/, '"password": "xxxxxxxx"')
+    interaction.response.body.gsub!(/"email":\s*"[^"]*"/, '"email": "[email protected]"')

Note: The existing pattern on line 22 (/"account_id":\d+/) has the same issue, but that's outside the scope of this PR.

📝 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
interaction.response.body.gsub!(/"username":"[^"]*"/, '"username": "railsware"')
interaction.response.body.gsub!(/"password":"[^"]*"/, '"password": "xxxxxxxx"')
interaction.response.body.gsub!(/"email":"[^"]*"/, '"email": "[email protected]"')
interaction.response.body.gsub!(/"username":\s*"[^"]*"/, '"username": "railsware"')
interaction.response.body.gsub!(/"password":\s*"[^"]*"/, '"password": "xxxxxxxx"')
interaction.response.body.gsub!(/"email":\s*"[^"]*"/, '"email": "[email protected]"')
🤖 Prompt for AI Agents
In @spec/spec_helper.rb around lines 23 - 25, The three gsub! calls on
interaction.response.body (the calls that redact "username", "password", and
"email") use regexes that require no space after the colon; update those regexes
to allow optional whitespace around the colon (e.g., use patterns like
'"username"\s*:\s*"[^"]*"' etc.) and keep the replacement strings formatted with
a single space after the colon (e.g., '"username": "railsware"') so the
redaction works whether or not there is whitespace in the JSON.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml:
- Around line 80-86: The VCR cassette contains unredacted inbox passwords;
re-record the affected cassette so the spec_helper.rb filter_sensitive_data hook
actually redacts those values (or manually replace the cleartext password values
with a placeholder like "<REDACTED>" if re-recording is not possible), then run
the test suite to regenerate/verify the cassette and confirm
filter_sensitive_data is applied for keys like "password" in the ProjectsAPI
list mapping.
- Around line 78-88: The VCR cassette maps_response_data_to_Project_objects.yml
contains real email addresses embedded in inbox "name" fields (PII), so update
your test hygiene: add a broader email-redaction rule in spec_helper.rb (extend
the existing filter_sensitive_data configuration to redact any email-like
pattern across request/response bodies), then either re-record the cassette with
sanitized test data or manually replace the email-like substrings in the
cassette's inbox "name" values; refer to the existing filter_sensitive_data
setup in spec_helper.rb and the problematic cassette and inbox "name" entries
when making the change.
🧹 Nitpick comments (4)
spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml (1)

78-80: Consider redacting share_links tokens in VCR cassettes.

The share_links contain access tokens (the long base64-like strings in the URLs) that could grant unauthorized access to projects. While the Bearer token and other sensitive fields are properly redacted, these share link tokens appear to be left in plain form.

Consider adding a filter in spec_helper.rb to redact these tokens:

interaction.response.body.gsub!(
  %r{"(admin|viewer)":"https://mailtrap\.io/projects/\d+/share/[^"]+"},
  '"\1":"https://mailtrap.io/projects/REDACTED/share/REDACTED"'
)
spec/spec_helper.rb (1)

23-30: Inconsistent spacing in redaction replacements.

The replacement strings have inconsistent spacing after the colon. Some use ": " (with space) while the original JSON likely uses ":" (without space). Looking at the existing pattern on line 22, it uses ": 1111111 (with space), but this may not match the actual JSON format.

Compare the VCR cassette fixture which shows "username":"xxxxxxxx" (no space) vs your replacement '"username": "railsware"' (with space). This inconsistency could cause test matching issues or confusing diffs.

♻️ Suggested fix for consistent formatting
-    interaction.response.body.gsub!(/"username":"[^"]*"/, '"username": "railsware"')
-    interaction.response.body.gsub!(/"password":"[^"]*"/, '"password": "xxxxxxxx"')
-    interaction.response.body.gsub!(/"email":"[^"]*"/, '"email": "[email protected]"')
-    interaction.response.body.gsub!(
-      /"forward_from_email_address":"[^"]*"/,
-      '"forward_from_email_address": "[email protected]"'
-    )
-    interaction.response.body.gsub!(/"email_username":"[^"]*"/, '"email_username": "1234abcd"')
+    interaction.response.body.gsub!(/"username":"[^"]*"/, '"username":"railsware"')
+    interaction.response.body.gsub!(/"password":"[^"]*"/, '"password":"xxxxxxxx"')
+    interaction.response.body.gsub!(/"email":"[^"]*"/, '"email":"[email protected]"')
+    interaction.response.body.gsub!(
+      /"forward_from_email_address":"[^"]*"/,
+      '"forward_from_email_address":"[email protected]"'
+    )
+    interaction.response.body.gsub!(/"email_username":"[^"]*"/, '"email_username":"1234abcd"')
spec/mailtrap/projects_api_spec.rb (2)

99-112: Redundant test context - duplicates the parent test.

The "with hash request" context (lines 99-112) is testing the exact same scenario as the parent test (lines 86-97). Both use identical request hashes with { name: 'New Project' } and assert the same expectations. This adds no additional coverage.

Consider removing this redundant context or, if the intent was to test different input formats, modify it to test a genuinely different case (e.g., positional arguments vs keyword arguments).


152-165: Same redundancy as in #create - consider consolidating.

This "with hash request" context duplicates the parent test case. Both define identical request hashes and make the same assertions.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 605cc2e and e12ab39.

📒 Files selected for processing (4)
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.yml
  • spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml
  • spec/mailtrap/projects_api_spec.rb
  • spec/spec_helper.rb
🧰 Additional context used
🧬 Code graph analysis (1)
spec/mailtrap/projects_api_spec.rb (1)
lib/mailtrap/projects_api.rb (6)
  • list (18-20)
  • include (8-76)
  • get (26-28)
  • create (36-38)
  • update (46-48)
  • delete (54-56)
🔇 Additional comments (3)
spec/spec_helper.rb (1)

63-91: LGTM - Well-designed custom matcher.

The match_struct matcher correctly validates both presence and absence of keys, with proper handling for Struct and hash-like objects. The failure messages are clear and helpful for debugging.

spec/mailtrap/projects_api_spec.rb (2)

1-27: LGTM - Good test structure with proper VCR integration.

The test setup with VCR metadata and environment variable fallbacks for local testing is well done. The authorization error test properly validates both the error type and message content.


191-216: LGTM - Delete tests cover success and not-found scenarios.

The delete tests properly verify the return value and error handling for non-existent projects.

Comment on lines +78 to +88
string: '[{"id":1536451,"name":"My Project","share_links":{"admin":"https://mailtrap.io/projects/1536451/share/REDACTED","viewer":"https://mailtrap.io/projects/1536451/share/REDACTED"},"inboxes":[{"id":2117479,"name":"My
Inbox","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1536451,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":87,"emails_unread_count":0,"last_message_sent_at":"2025-01-03T16:37:44.583Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2598195,"name":"New
Inbox 3","username":"xxxxxxxx","status":"active","email_username":"new-inbox-3-b9d2b9","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1536451,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":2,"emails_unread_count":0,"last_message_sent_at":"2024-03-30T18:02:12.295Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}}],"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":1886601,"name":"Freelance
per dev","share_links":{"admin":"https://mailtrap.io/projects/1886601/share/REDACTED","viewer":"https://mailtrap.io/projects/1886601/share/REDACTED"},"inboxes":[{"id":2639689,"name":"#php-developer-artur-edamov","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":2,"emails_unread_count":0,"last_message_sent_at":"2024-05-09T21:21:24.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2750032,"name":"IT-248","username":"0a5db6d04b5567","status":"active","email_username":"c7884b9b27-2a5e4e","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"3f75964c517555","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":10,"emails_unread_count":10,"last_message_sent_at":"2024-06-01T22:57:16.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2803763,"name":"IT-316:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2827176,"name":"IT-348:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":3,"emails_unread_count":3,"last_message_sent_at":"2024-05-24T09:01:52.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2869179,"name":"IT-395:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":44,"emails_unread_count":44,"last_message_sent_at":"2025-02-13T13:36:02.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3244543,"name":"IT-973:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3447218,"name":"#freelance-dev-oleksandr-bukhtiiarov","username":"82eda9bf7c1b3b","status":"active","email_username":"d3b391d162-05b895","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"0bf7ff67b64d93","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3447223,"name":"#ext-js-developer-artem-litvinenko","username":"5f144f668d427e","status":"active","email_username":"cc57d3ca6e-292fc7","email_username_enabled":true,"sent_messages_count":8,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"c689b190526d93","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":325,"emails_unread_count":152,"last_message_sent_at":"2025-11-04T11:36:49.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3950294,"name":"IT-1923:
#mailtrap-developer-python-sdk","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3960195,"name":"IT-1834: [email protected]","username":"9fa00a713daaab","status":"active","email_username":"b3ce3a93a7-5cb603","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"eefcda4580d160","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}}],"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2486140,"name":"project
591","share_links":{"admin":"https://mailtrap.io/projects/2486140/share/REDACTED","viewer":"https://mailtrap.io/projects/2486140/share/REDACTED"},"inboxes":[{"id":3976586,"name":"IT-1834:
[email protected]#2","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":2486140,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}}],"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2486206,"name":"new
Copy link

@coderabbitai coderabbitai bot Jan 8, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

PII exposure: Real email addresses visible in inbox names.

The cassette contains what appear to be real email addresses embedded in inbox name fields:

These are not caught by the current filter_sensitive_data regex patterns since they appear in the name field, not a dedicated email field.

Consider either:

  1. Re-recording this cassette with sanitized test data
  2. Adding a broader email redaction pattern to spec_helper.rb
  3. Manually sanitizing these inbox names in the cassette
🤖 Prompt for AI Agents
In
@spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml
around lines 78 - 88, The VCR cassette maps_response_data_to_Project_objects.yml
contains real email addresses embedded in inbox "name" fields (PII), so update
your test hygiene: add a broader email-redaction rule in spec_helper.rb (extend
the existing filter_sensitive_data configuration to redact any email-like
pattern across request/response bodies), then either re-record the cassette with
sanitized test data or manually replace the email-like substrings in the
cassette's inbox "name" values; refer to the existing filter_sensitive_data
setup in spec_helper.rb and the problematic cassette and inbox "name" entries
when making the change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +80 to +86
Inbox 3","username":"xxxxxxxx","status":"active","email_username":"new-inbox-3-b9d2b9","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1536451,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":2,"emails_unread_count":0,"last_message_sent_at":"2024-03-30T18:02:12.295Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}}],"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":1886601,"name":"Freelance
per dev","share_links":{"admin":"https://mailtrap.io/projects/1886601/share/REDACTED","viewer":"https://mailtrap.io/projects/1886601/share/REDACTED"},"inboxes":[{"id":2639689,"name":"#php-developer-artur-edamov","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":2,"emails_unread_count":0,"last_message_sent_at":"2024-05-09T21:21:24.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2750032,"name":"IT-248","username":"0a5db6d04b5567","status":"active","email_username":"c7884b9b27-2a5e4e","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"3f75964c517555","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":10,"emails_unread_count":10,"last_message_sent_at":"2024-06-01T22:57:16.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2803763,"name":"IT-316:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2827176,"name":"IT-348:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":3,"emails_unread_count":3,"last_message_sent_at":"2024-05-24T09:01:52.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2869179,"name":"IT-395:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":44,"emails_unread_count":44,"last_message_sent_at":"2025-02-13T13:36:02.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3244543,"name":"IT-973:
[email protected]","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3447218,"name":"#freelance-dev-oleksandr-bukhtiiarov","username":"82eda9bf7c1b3b","status":"active","email_username":"d3b391d162-05b895","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"0bf7ff67b64d93","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3447223,"name":"#ext-js-developer-artem-litvinenko","username":"5f144f668d427e","status":"active","email_username":"cc57d3ca6e-292fc7","email_username_enabled":true,"sent_messages_count":8,"forwarded_messages_count":0,"used":true,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"c689b190526d93","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":325,"emails_unread_count":152,"last_message_sent_at":"2025-11-04T11:36:49.000Z","max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3950294,"name":"IT-1923:
#mailtrap-developer-python-sdk","username":"xxxxxxxx","status":"active","email_username":"1234abcd","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"xxxxxxxx","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":3960195,"name":"IT-1834: [email protected]","username":"9fa00a713daaab","status":"active","email_username":"b3ce3a93a7-5cb603","email_username_enabled":true,"sent_messages_count":0,"forwarded_messages_count":0,"used":false,"forward_from_email_address":"[email protected]","project_id":1886601,"password":"eefcda4580d160","domain":"sandbox.smtp.mailtrap.io","pop3_domain":"pop3.mailtrap.io","email_domain":"inbox.mailtrap.io","smtp_ports":[25,465,587,2525],"pop3_ports":[1100,9950],"api_domain":"sandbox.api.mailtrap.io","emails_count":0,"emails_unread_count":0,"last_message_sent_at":null,"max_size":1000,"max_message_size":26214400,"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}}],"permissions":{"can_read":true,"can_update":true,"can_destroy":true,"can_leave":false}},{"id":2486140,"name":"project
Copy link

@coderabbitai coderabbitai bot Jan 8, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unredacted passwords in cassette.

Several inbox passwords appear to be unredacted in this cassette (e.g., "password":"3f75964c517555", "password":"c689b190526d93"). The filter_sensitive_data hook in spec_helper.rb should redact these, but it appears this cassette was either recorded before the filter was added or needs to be re-recorded.

Re-record this cassette to ensure the password filter is applied consistently.

🤖 Prompt for AI Agents
In
@spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.yml
around lines 80 - 86, The VCR cassette contains unredacted inbox passwords;
re-record the affected cassette so the spec_helper.rb filter_sensitive_data hook
actually redacts those values (or manually replace the cleartext password values
with a placeholder like "<REDACTED>" if re-recording is not possible), then run
the test suite to regenerate/verify the cassette and confirm
filter_sensitive_data is applied for keys like "password" in the ProjectsAPI
list mapping.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@i7an i7an self-requested a review January 8, 2026 17:24
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.

4 participants