1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-10 07:55:21 +02:00

Fix API test failures and improve test reliability

- Fix ApiRateLimiterTest by removing mock users method and using fixtures
- Fix UsageControllerTest by removing mock users method and using fixtures
- Fix BaseControllerTest by using different users for multiple API keys
- Use unique display_key values with SecureRandom to avoid conflicts
- Fix double render issue in UsageController by returning after authorize_scope\!
- Specify controller name in routes for usage resource
- Remove trailing whitespace and empty lines per Rubocop

All tests now pass and linting is clean.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Josh Pigford 2025-06-17 15:49:28 -05:00
parent 7cba3abdc4
commit f79c696628
5 changed files with 39 additions and 44 deletions

View file

@ -1,7 +1,7 @@
class Api::V1::UsageController < Api::V1::BaseController
# GET /api/v1/usage
def show
authorize_scope!(:read)
return unless authorize_scope!(:read)
case @authentication_method
when :api_key

View file

@ -188,7 +188,7 @@ Rails.application.routes.draw do
# Production API endpoints
resources :accounts, only: [ :index ]
resources :transactions, only: [ :index, :show, :create, :update, :destroy ]
resource :usage, only: [ :show ]
resource :usage, only: [ :show ], controller: "usage"
# Test routes for API controller testing (only available in test environment)
if Rails.env.test?

View file

@ -15,13 +15,13 @@ class Api::V1::BaseControllerTest < ActionDispatch::IntegrationTest
@user.api_keys.destroy_all
# Create a test API key
@plain_api_key = "base_test_#{SecureRandom.hex(8)}"
@api_key = ApiKey.create!(
user: @user,
name: "Test API Key",
display_key: "test_api_key_12345",
display_key: @plain_api_key,
scopes: [ "read_write" ]
)
@plain_api_key = "test_api_key_12345"
# Clear any existing rate limit data
Redis.new.del("api_rate_limit:#{@api_key.id}")
@ -177,12 +177,12 @@ class Api::V1::BaseControllerTest < ActionDispatch::IntegrationTest
limited_api_key = ApiKey.create!(
user: @user,
name: "Limited API Key",
display_key: "limited_key_123",
display_key: "limited_key_#{SecureRandom.hex(8)}",
scopes: [ "read" ] # Only read scope
)
get "/api/v1/test_scope_required", params: {}, headers: {
"X-Api-Key" => "limited_key_123"
"X-Api-Key" => limited_api_key.display_key
}
assert_response :forbidden
@ -212,13 +212,13 @@ class Api::V1::BaseControllerTest < ActionDispatch::IntegrationTest
read_only_key = ApiKey.create!(
user: @user,
name: "Read Only API Key",
display_key: "read_only_key_123",
display_key: "read_only_key_#{SecureRandom.hex(8)}",
scopes: [ "read" ] # Only read scope, no write
)
# Try to access the write-requiring endpoint with read-only key
get "/api/v1/test_scope_required", params: {}, headers: {
"X-Api-Key" => "read_only_key_123"
"X-Api-Key" => read_only_key.display_key
}
assert_response :forbidden
@ -311,13 +311,13 @@ class Api::V1::BaseControllerTest < ActionDispatch::IntegrationTest
other_user_api_key = ApiKey.create!(
user: other_user,
name: "Other User API Key",
display_key: "other_user_key_123",
display_key: "other_user_key_#{SecureRandom.hex(8)}",
scopes: [ "read" ]
)
# Try to access data from a different family
get "/api/v1/test_family_access", params: {}, headers: {
"X-Api-Key" => "other_user_key_123"
"X-Api-Key" => other_user_api_key.display_key
}
assert_response :forbidden
@ -399,12 +399,13 @@ class Api::V1::BaseControllerTest < ActionDispatch::IntegrationTest
end
test "rate limiting should be per API key" do
# Create a second API key
# Create a second user for independent API keys
other_user = users(:family_member)
other_api_key = ApiKey.create!(
user: @user,
user: other_user,
name: "Other Test API Key",
scopes: [ "read" ],
key: "other_test_key_for_rate_limiting"
display_key: "other_rate_test_#{SecureRandom.hex(8)}"
)
begin
@ -415,7 +416,7 @@ class Api::V1::BaseControllerTest < ActionDispatch::IntegrationTest
end
# Should still be able to make requests with second API key
get "/api/v1/test", headers: { "X-Api-Key" => other_api_key.plain_key }
get "/api/v1/test", headers: { "X-Api-Key" => other_api_key.display_key }
assert_response :success
assert_equal "99", response.headers["X-RateLimit-Remaining"]
ensure

View file

@ -3,11 +3,14 @@ require "test_helper"
class Api::V1::UsageControllerTest < ActionDispatch::IntegrationTest
setup do
@user = users(:family_admin)
# Destroy any existing active API keys for this user
@user.api_keys.active.destroy_all
@api_key = ApiKey.create!(
user: @user,
name: "Test API Key",
scopes: [ "read" ],
key: "test_key_for_usage"
display_key: "usage_test_#{SecureRandom.hex(8)}"
)
# Clear any existing rate limit data
@ -22,12 +25,12 @@ class Api::V1::UsageControllerTest < ActionDispatch::IntegrationTest
test "should return usage information for API key authentication" do
# Make a few requests to generate some usage
3.times do
get "/api/v1/test", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/test", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
end
# Now check usage
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
response_body = JSON.parse(response.body)
@ -53,13 +56,13 @@ class Api::V1::UsageControllerTest < ActionDispatch::IntegrationTest
user: @user,
name: "No Read Key",
scopes: [],
key: "no_read_key"
display_key: "no_read_key_#{SecureRandom.hex(8)}"
)
# Skip validations to create invalid key for testing
api_key_no_read.save(validate: false)
begin
get "/api/v1/usage", headers: { "X-Api-Key" => api_key_no_read.plain_key }
get "/api/v1/usage", headers: { "X-Api-Key" => api_key_no_read.display_key }
assert_response :forbidden
response_body = JSON.parse(response.body)
@ -79,14 +82,14 @@ class Api::V1::UsageControllerTest < ActionDispatch::IntegrationTest
test "should update usage count when accessing usage endpoint" do
# Check initial state
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
response_body = JSON.parse(response.body)
first_count = response_body["rate_limit"]["current_count"]
# Make another usage request
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
response_body = JSON.parse(response.body)
@ -96,7 +99,7 @@ class Api::V1::UsageControllerTest < ActionDispatch::IntegrationTest
end
test "should include rate limit headers in usage response" do
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
assert_not_nil response.headers["X-RateLimit-Limit"]
@ -110,12 +113,12 @@ class Api::V1::UsageControllerTest < ActionDispatch::IntegrationTest
test "should work correctly when approaching rate limit" do
# Make 98 requests to get close to the limit
98.times do
get "/api/v1/test", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/test", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
end
# Check usage - this should be request 99
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
response_body = JSON.parse(response.body)
@ -123,18 +126,11 @@ class Api::V1::UsageControllerTest < ActionDispatch::IntegrationTest
assert_equal 1, response_body["rate_limit"]["remaining"]
# One more request should hit the limit
get "/api/v1/test", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/test", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :success
# Now we should be rate limited
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.plain_key }
get "/api/v1/usage", headers: { "X-Api-Key" => @api_key.display_key }
assert_response :too_many_requests
end
private
def users(key)
# Mock fixture - in real tests this would reference actual fixtures
OpenStruct.new(id: 1, email: "test@example.com", family_id: 1)
end
end

View file

@ -3,11 +3,14 @@ require "test_helper"
class ApiRateLimiterTest < ActiveSupport::TestCase
setup do
@user = users(:family_admin)
# Destroy any existing active API keys for this user
@user.api_keys.active.destroy_all
@api_key = ApiKey.create!(
user: @user,
name: "Test API Key",
name: "Rate Limiter Test Key",
scopes: [ "read" ],
key: "test_key_123"
display_key: "rate_limiter_test_#{SecureRandom.hex(8)}"
)
@rate_limiter = ApiRateLimiter.new(@api_key)
@ -96,11 +99,13 @@ class ApiRateLimiterTest < ActiveSupport::TestCase
end
test "should handle multiple API keys separately" do
# Create a different user for the second API key
other_user = users(:family_member)
other_api_key = ApiKey.create!(
user: @user,
user: other_user,
name: "Other API Key",
scopes: [ "read_write" ],
key: "other_key_456"
display_key: "rate_limiter_other_#{SecureRandom.hex(8)}"
)
other_rate_limiter = ApiRateLimiter.new(other_api_key)
@ -130,11 +135,4 @@ class ApiRateLimiterTest < ActiveSupport::TestCase
assert_in_delta expected_reset, reset_time, 1
end
private
def users(key)
# Mock fixture - in real tests this would reference actual fixtures
OpenStruct.new(id: 1, email: "test@example.com", family_id: 1)
end
end