-
Notifications
You must be signed in to change notification settings - Fork 7
Add Sandbox Projects API #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds 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 Changes
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~28 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this 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 callsBoth
#createand#updateexamples useproject_api.create(**request)andproject_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 redefinelet(: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
subjectin 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 endApply the same fix to the
#updatecontext (lines 129–142), substitutingsubject(:update) { project_api.update(project_id, request) }.Also applies to: 129-142
lib/mailtrap/project.rb (1)
25-32: Use safe navigation (&.) for inbox mappingRubocop flags this code with Style/SafeNavigation. Simplify the initialization of
@inboxesusing 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) + endSemantics remain unchanged (
@inboxesisnilwhen 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
📒 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: trueand the compactedto_hoverride 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: Fixhave_attributesindentation to satisfy Rubocop Layout/FirstArgumentIndentationRubocop is flagging the first arguments to
have_attributesas 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
spec/mailtrap/project_spec.rb
Outdated
| expect(project.inboxes.map(&:to_h)).to eq([ | ||
| { | ||
| id: 456, | ||
| name: 'Test Inbox', | ||
| username: '[email protected]' | ||
| } | ||
| ]) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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).
There was a problem hiding this 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
📒 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
Projectclass properly handles inbox initialization, supporting bothMailtrap::Inboxinstances and raw hashes. Theto_hmethod 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.
| ### 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' | ||
| ) | ||
| ``` |
There was a problem hiding this comment.
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.
There was a problem hiding this 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@seelink points to inbox, not project, docs.The DTO and comments clearly describe an inbox, but the
@seeURL slug ends withcreate-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
📒 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 andto_hoverride look correct and consistent.Member list matches the documented attributes,
keyword_init: truegives a clean API for callers, andsuper.compactaligns with the existing DTO pattern of dropping nils before serialization. No functional issues spotted here; just ensure the gem’s minimum Ruby version supportsStruct.new(..., keyword_init: true).
There was a problem hiding this 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, butbase_deletedelegates toclient.deleteand returns its response. Based on the test expectations inspec/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
📒 Files selected for processing (2)
lib/mailtrap/project.rblib/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_hmethod correctly returns a compacted hash representation.lib/mailtrap/projects_api.rb (1)
70-78: LGTM! Private helper methods are correctly implemented.The
base_pathandwrap_requestmethods properly support the BaseAPI delegation pattern with appropriate path construction and request wrapping.
lib/mailtrap/inbox.rb
Outdated
| ) do | ||
| # @return [Hash] The inbox attributes as a hash | ||
| def to_h | ||
| super.compact |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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
spec/mailtrap/project_spec.rb
Outdated
| end | ||
|
|
||
| it 'creates a project with all attributes' do | ||
| expect(project).to have_attributes( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
lib/mailtrap/projects_api.rb
Outdated
| base_delete(project_id) | ||
| end | ||
|
|
||
| def build_entity(options, response_class) |
There was a problem hiding this comment.
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_responseinstead.- consider using
build_entityfor both projects and inboxes - see if you need to update contacts api
- consider using
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
lib/mailtrap/projects_api.rb
Outdated
| 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) |
There was a problem hiding this comment.
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?
lib/mailtrap/project.rb
Outdated
| ) do | ||
| # @return [Hash] The Project attributes as a hash | ||
| def to_h | ||
| super.compact |
There was a problem hiding this comment.
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)There was a problem hiding this 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 endOptional: Consider consistent access pattern
The matcher checks
respond_to?(key)but uses bracket notationactual_struct[key]. For better consistency, either checkrespond_to?(:[])or useactual_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
📒 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.
There was a problem hiding this 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_hoverride changes the serialization behavior—to_hwill 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
ContactImportinstances 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
📒 Files selected for processing (10)
lib/mailtrap/contact.rblib/mailtrap/contact_field.rblib/mailtrap/contact_import.rblib/mailtrap/contact_list.rblib/mailtrap/email_template.rblib/mailtrap/inbox.rblib/mailtrap/project.rblib/mailtrap/suppression.rbspec/mailtrap/contact_spec.rbspec/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_hbehavior will include nil values, changing serialization output compared to the previous.compactimplementation.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_hoverride 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: truefollows 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.
There was a problem hiding this 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_responsemethod 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 movinghandle_responseto the private section.Since
handle_responseis an internal implementation detail called only byBaseAPImethods, moving it below theprivatekeyword (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
📒 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
listandgetmethods correctly delegate tobase_listandbase_getrespectively, with proper YARD documentation.
30-48: LGTM! Proper delegation with comprehensive documentation.The
createandupdatemethods are well-documented and correctly delegate to their base implementations.
67-76: LGTM! Private helpers are correctly implemented.The
base_pathandwrap_requestmethods follow the expected patterns for BaseAPI integration.
| # Deletes a project | ||
| # @param project_id [Integer] The project ID | ||
| # @return nil | ||
| # @!macro api_errors | ||
| def delete(project_id) | ||
| base_delete(project_id) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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: |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
must be private.
spec/spec_helper.rb
Outdated
|
|
||
| RSpec::Matchers.define :match_struct do |expected_attributes| | ||
| match do |actual_struct| | ||
| expected_attributes.all? do |key, expected_value| |
There was a problem hiding this comment.
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) } |
There was a problem hiding this comment.
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.
spec/mailtrap/project_spec.rb
Outdated
| end | ||
| end | ||
|
|
||
| describe '#to_h' do |
There was a problem hiding this comment.
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.
There was a problem hiding this 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
#gettest now usesmatch_structfor stricter validation (exact structural matching), while#list(line 14),#create(line 92), and#update(lines 165, 183, 196, etc.) continue usinghave_attributes(partial matching).Is this intentional? If
match_structprovides better validation by ensuring no unexpected fields are present, consider applying it consistently to other test methods. Ifhave_attributesis sufficient, this stricter approach may be unnecessary for just#get.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (50)
lib/mailtrap/projects_api.rbspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/maps_response_data_to_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/with_hash_request/maps_response_data_to_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_create/with_hash_request/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_delete/returns_no_content.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_delete/when_template_does_not_exist/raises_not_found_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/maps_response_data_to_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_get/when_template_does_not_exist/raises_not_found_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/maps_response_data_to_EmailTemplate_objects.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/returns_an_array_of_EmailTemplate_objects.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_list/when_api_key_is_incorrect/raises_authorization_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/maps_response_data_to_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_template_does_not_exist/raises_not_found_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/preserves_other_fields_unchanged.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_multiple_specific_fields/updates_only_the_specified_fields.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/preserves_other_fields_unchanged.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_body_html/updates_only_the_body_html_field.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/preserves_other_fields_unchanged.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/when_updating_only_category/updates_only_the_category_field.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/with_hash_request/maps_response_data_to_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_EmailTemplatesAPI/_update/with_hash_request/returns_an_EmailTemplate_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/maps_response_data_to_Project_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/when_API_returns_an_error/raises_a_Mailtrap_Error.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_create/with_hash_request/maps_response_data_to_Project_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/returns_deleted_project_id.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_delete/when_project_does_not_exist/raises_not_found_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/when_project_does_not_exist/raises_not_found_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/when_api_key_is_incorrect/raises_authorization_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/maps_response_data_to_Project_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_project_does_not_exist/raises_not_found_error.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/when_updating_only_name/updates_only_the_name_field.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_update/with_hash_request/maps_response_data_to_Project_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_SuppressionsAPI/vcr_list/maps_response_data_to_Suppression_objects.ymlspec/fixtures/vcr_cassettes/Mailtrap_SuppressionsAPI/vcr_list/when_api_key_is_incorrect/raises_authorization_error.ymlspec/mailtrap/contact_spec.rbspec/mailtrap/email_template_spec.rbspec/mailtrap/email_templates_api_spec.rbspec/mailtrap/project_spec.rbspec/mailtrap/projects_api_spec.rbspec/mailtrap/suppression_spec.rbspec/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.
| expect(project.inboxes).to eq([ | ||
| { | ||
| id: 456, | ||
| name: 'Test Inbox', | ||
| username: '[email protected]' | ||
| } | ||
| ]) |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
IgorDobryn
left a comment
There was a problem hiding this 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') |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this 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
📒 Files selected for processing (4)
spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.ymlspec/mailtrap/projects_api_spec.rbspec/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-Policyheader from recorded responses keeps cassettes cleaner and avoids irrelevant diff noise.
58-86: Well-designed custom matcher.The
match_structmatcher correctly validates both attribute values and ensures no extra keys exist. Good handling of bothStructobjects (viamembers) and hash-like objects (viato_h), with clear failure messages.
| 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}}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.ymlRepository: 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 -10Repository: 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 -20Repository: 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 rbRepository: 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 -10Repository: 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 -20Repository: 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 -20Repository: 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_modulesRepository: mailtrap/mailtrap-ruby
Length of output: 77
🏁 Script executed:
# Read spec_helper.rb to find VCR configuration
cat spec/spec_helper.rbRepository: 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 -5Repository: 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.ymlRepository: 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @DagonWat
There was a problem hiding this comment.
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!
| interaction.response.body.gsub!(/"username":"[^"]*"/, '"username": "railsware"') | ||
| interaction.response.body.gsub!(/"password":"[^"]*"/, '"password": "xxxxxxxx"') | ||
| interaction.response.body.gsub!(/"email":"[^"]*"/, '"email": "[email protected]"') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
There was a problem hiding this 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_linkscontain 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.rbto 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
requesthashes 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
requesthashes and make the same assertions.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
spec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_get/maps_response_data_to_Project_object.ymlspec/fixtures/vcr_cassettes/Mailtrap_ProjectsAPI/_list/maps_response_data_to_Project_objects.ymlspec/mailtrap/projects_api_spec.rbspec/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_structmatcher 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.
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- Re-recording this cassette with sanitized test data
- Adding a broader email redaction pattern to
spec_helper.rb - 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @DagonWat
There was a problem hiding this comment.
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!
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @DagonWat
There was a problem hiding this comment.
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!
Motivation
Add Sandbox Projects API support for Rybu SDK
Changes
Summary by CodeRabbit
New Features
Tests
Documentation
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.