Add comprehensive API v1 with OAuth and API key authentication (#2389)
* OAuth
* Add API test routes and update Doorkeeper token handling for test environment
- Introduced API namespace with test routes for controller testing in the test environment.
- Updated Doorkeeper configuration to allow fallback to plain tokens in the test environment for easier testing.
- Modified schema to change resource_owner_id type from bigint to string.
* Implement API key authentication and enhance access control
- Replaced Doorkeeper OAuth authentication with a custom method supporting both OAuth and API keys in the BaseController.
- Added methods for API key authentication, including validation and logging.
- Introduced scope-based authorization for API keys in the TestController.
- Updated routes to include API key management endpoints.
- Enhanced logging for API access to include authentication method details.
- Added tests for API key functionality, including validation, scope checks, and access control enforcement.
* Add API key rate limiting and usage tracking
- Implemented rate limiting for API key authentication in BaseController.
- Added methods to check rate limits, render appropriate responses, and include rate limit headers in responses.
- Updated routes to include a new usage resource for tracking API usage.
- Enhanced tests to verify rate limit functionality, including exceeding limits and per-key tracking.
- Cleaned up Redis data in tests to ensure isolation between test cases.
* Add Jbuilder for JSON rendering and refactor AccountsController
- Added Jbuilder gem for improved JSON response handling.
- Refactored index action in AccountsController to utilize Jbuilder for rendering JSON.
- Removed manual serialization of accounts and streamlined response structure.
- Implemented a before_action in BaseController to enforce JSON format for all API requests.
* Add transactions resource to API routes
- Added routes for transactions, allowing index, show, create, update, and destroy actions.
- This enhancement supports comprehensive transaction management within the API.
* Enhance API authentication and onboarding handling
- Updated BaseController to skip onboarding requirements for API endpoints and added manual token verification for OAuth authentication.
- Improved error handling and logging for invalid access tokens.
- Introduced a method to set up the current context for API requests, ensuring compatibility with session-like behavior.
- Excluded API paths from onboarding redirects in the Onboardable concern.
- Updated database schema to change resource_owner_id type from bigint to string for OAuth access grants.
* Fix rubocop offenses
- Fix indentation and spacing issues
- Convert single quotes to double quotes
- Add spaces inside array brackets
- Fix comment alignment
- Add missing trailing newlines
- Correct else/end alignment
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* 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>
* Add API transactions controller warning to brakeman ignore
The account_id parameter in the API transactions controller is properly
validated on line 79: family.accounts.find(transaction_params[:account_id])
This ensures users can only create transactions in accounts belonging to
their family, making this a false positive.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Signed-off-by: Josh Pigford <josh@joshpigford.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-17 15:57:05 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
require "test_helper"
|
|
|
|
|
|
|
|
class Api::V1::TransactionsControllerTest < ActionDispatch::IntegrationTest
|
|
|
|
setup do
|
|
|
|
@user = users(:family_admin)
|
|
|
|
@family = @user.family
|
|
|
|
@account = @family.accounts.first
|
|
|
|
@transaction = @family.transactions.first
|
|
|
|
@api_key = api_keys(:active_key) # Has read_write scope
|
|
|
|
@read_only_api_key = api_keys(:one) # Has read scope
|
|
|
|
end
|
|
|
|
|
|
|
|
# INDEX action tests
|
|
|
|
test "should get index with valid API key" do
|
|
|
|
get api_v1_transactions_url, headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
assert response_data.key?("transactions")
|
|
|
|
assert response_data.key?("pagination")
|
|
|
|
assert response_data["pagination"].key?("page")
|
|
|
|
assert response_data["pagination"].key?("per_page")
|
|
|
|
assert response_data["pagination"].key?("total_count")
|
|
|
|
assert response_data["pagination"].key?("total_pages")
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should get index with read-only API key" do
|
|
|
|
get api_v1_transactions_url, headers: api_headers(@read_only_api_key)
|
|
|
|
assert_response :success
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should filter transactions by account_id" do
|
|
|
|
get api_v1_transactions_url, params: { account_id: @account.id }, headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
response_data["transactions"].each do |transaction|
|
|
|
|
assert_equal @account.id, transaction["account"]["id"]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should filter transactions by date range" do
|
|
|
|
start_date = 1.month.ago.to_date
|
|
|
|
end_date = Date.current
|
|
|
|
|
|
|
|
get api_v1_transactions_url,
|
|
|
|
params: { start_date: start_date, end_date: end_date },
|
|
|
|
headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
response_data["transactions"].each do |transaction|
|
|
|
|
transaction_date = Date.parse(transaction["date"])
|
|
|
|
assert transaction_date >= start_date
|
|
|
|
assert transaction_date <= end_date
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should search transactions" do
|
|
|
|
# Create a transaction with a specific name for testing
|
|
|
|
entry = @account.entries.create!(
|
|
|
|
name: "Test Coffee Purchase",
|
|
|
|
amount: 5.50,
|
|
|
|
currency: "USD",
|
|
|
|
date: Date.current,
|
|
|
|
entryable: Transaction.new
|
|
|
|
)
|
|
|
|
|
|
|
|
get api_v1_transactions_url,
|
|
|
|
params: { search: "Coffee" },
|
|
|
|
headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
found_transaction = response_data["transactions"].find { |t| t["id"] == entry.transaction.id }
|
|
|
|
assert_not_nil found_transaction, "Should find the coffee transaction"
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should paginate transactions" do
|
|
|
|
get api_v1_transactions_url,
|
|
|
|
params: { page: 1, per_page: 5 },
|
|
|
|
headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
assert response_data["transactions"].size <= 5
|
|
|
|
assert_equal 1, response_data["pagination"]["page"]
|
|
|
|
assert_equal 5, response_data["pagination"]["per_page"]
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject index request without API key" do
|
|
|
|
get api_v1_transactions_url
|
|
|
|
assert_response :unauthorized
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject index request with invalid API key" do
|
|
|
|
get api_v1_transactions_url, headers: { "X-Api-Key" => "invalid-key" }
|
|
|
|
assert_response :unauthorized
|
|
|
|
end
|
|
|
|
|
|
|
|
# SHOW action tests
|
|
|
|
test "should show transaction with valid API key" do
|
|
|
|
get api_v1_transaction_url(@transaction), headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
assert_equal @transaction.id, response_data["id"]
|
|
|
|
assert response_data.key?("name")
|
|
|
|
assert response_data.key?("amount")
|
|
|
|
assert response_data.key?("date")
|
|
|
|
assert response_data.key?("account")
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should show transaction with read-only API key" do
|
|
|
|
get api_v1_transaction_url(@transaction), headers: api_headers(@read_only_api_key)
|
|
|
|
assert_response :success
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should return 404 for non-existent transaction" do
|
|
|
|
get api_v1_transaction_url(999999), headers: api_headers(@api_key)
|
|
|
|
assert_response :not_found
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject show request without API key" do
|
|
|
|
get api_v1_transaction_url(@transaction)
|
|
|
|
assert_response :unauthorized
|
|
|
|
end
|
|
|
|
|
|
|
|
# CREATE action tests
|
|
|
|
test "should create transaction with valid parameters" do
|
|
|
|
transaction_params = {
|
|
|
|
transaction: {
|
|
|
|
account_id: @account.id,
|
|
|
|
name: "Test Transaction",
|
|
|
|
amount: 25.00,
|
|
|
|
date: Date.current,
|
|
|
|
currency: "USD",
|
|
|
|
nature: "expense"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_difference("@account.entries.count", 1) do
|
|
|
|
post api_v1_transactions_url,
|
|
|
|
params: transaction_params,
|
|
|
|
headers: api_headers(@api_key)
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_response :created
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
assert_equal "Test Transaction", response_data["name"]
|
|
|
|
assert_equal @account.id, response_data["account"]["id"]
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject create with read-only API key" do
|
|
|
|
transaction_params = {
|
|
|
|
transaction: {
|
|
|
|
account_id: @account.id,
|
|
|
|
name: "Test Transaction",
|
|
|
|
amount: 25.00,
|
|
|
|
date: Date.current
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
post api_v1_transactions_url,
|
|
|
|
params: transaction_params,
|
|
|
|
headers: api_headers(@read_only_api_key)
|
|
|
|
assert_response :forbidden
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject create with invalid parameters" do
|
|
|
|
transaction_params = {
|
|
|
|
transaction: {
|
|
|
|
# Missing required fields
|
|
|
|
name: "Test Transaction"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
post api_v1_transactions_url,
|
|
|
|
params: transaction_params,
|
|
|
|
headers: api_headers(@api_key)
|
|
|
|
assert_response :unprocessable_entity
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject create without API key" do
|
|
|
|
post api_v1_transactions_url, params: { transaction: { name: "Test" } }
|
|
|
|
assert_response :unauthorized
|
|
|
|
end
|
|
|
|
|
|
|
|
# UPDATE action tests
|
|
|
|
test "should update transaction with valid parameters" do
|
|
|
|
update_params = {
|
|
|
|
transaction: {
|
|
|
|
name: "Updated Transaction Name",
|
|
|
|
amount: 30.00
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
put api_v1_transaction_url(@transaction),
|
|
|
|
params: update_params,
|
|
|
|
headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
assert_equal "Updated Transaction Name", response_data["name"]
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject update with read-only API key" do
|
|
|
|
update_params = {
|
|
|
|
transaction: {
|
|
|
|
name: "Updated Transaction Name"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
put api_v1_transaction_url(@transaction),
|
|
|
|
params: update_params,
|
|
|
|
headers: api_headers(@read_only_api_key)
|
|
|
|
assert_response :forbidden
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject update for non-existent transaction" do
|
|
|
|
put api_v1_transaction_url(999999),
|
|
|
|
params: { transaction: { name: "Test" } },
|
|
|
|
headers: api_headers(@api_key)
|
|
|
|
assert_response :not_found
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject update without API key" do
|
|
|
|
put api_v1_transaction_url(@transaction), params: { transaction: { name: "Test" } }
|
|
|
|
assert_response :unauthorized
|
|
|
|
end
|
|
|
|
|
|
|
|
# DESTROY action tests
|
|
|
|
test "should destroy transaction" do
|
|
|
|
entry_to_delete = @account.entries.create!(
|
|
|
|
name: "Transaction to Delete",
|
|
|
|
amount: 10.00,
|
|
|
|
currency: "USD",
|
|
|
|
date: Date.current,
|
|
|
|
entryable: Transaction.new
|
|
|
|
)
|
|
|
|
transaction_to_delete = entry_to_delete.transaction
|
|
|
|
|
|
|
|
assert_difference("@account.entries.count", -1) do
|
|
|
|
delete api_v1_transaction_url(transaction_to_delete), headers: api_headers(@api_key)
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_response :success
|
|
|
|
response_data = JSON.parse(response.body)
|
|
|
|
assert response_data.key?("message")
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject destroy with read-only API key" do
|
|
|
|
delete api_v1_transaction_url(@transaction), headers: api_headers(@read_only_api_key)
|
|
|
|
assert_response :forbidden
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject destroy for non-existent transaction" do
|
|
|
|
delete api_v1_transaction_url(999999), headers: api_headers(@api_key)
|
|
|
|
assert_response :not_found
|
|
|
|
end
|
|
|
|
|
|
|
|
test "should reject destroy without API key" do
|
|
|
|
delete api_v1_transaction_url(@transaction)
|
|
|
|
assert_response :unauthorized
|
|
|
|
end
|
|
|
|
|
|
|
|
# JSON structure tests
|
|
|
|
test "transaction JSON should have expected structure" do
|
|
|
|
get api_v1_transaction_url(@transaction), headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
transaction_data = JSON.parse(response.body)
|
|
|
|
|
|
|
|
# Basic fields
|
|
|
|
assert transaction_data.key?("id")
|
|
|
|
assert transaction_data.key?("date")
|
|
|
|
assert transaction_data.key?("amount")
|
|
|
|
assert transaction_data.key?("currency")
|
|
|
|
assert transaction_data.key?("name")
|
|
|
|
assert transaction_data.key?("classification")
|
|
|
|
assert transaction_data.key?("created_at")
|
|
|
|
assert transaction_data.key?("updated_at")
|
|
|
|
|
|
|
|
# Account information
|
|
|
|
assert transaction_data.key?("account")
|
|
|
|
assert transaction_data["account"].key?("id")
|
|
|
|
assert transaction_data["account"].key?("name")
|
|
|
|
assert transaction_data["account"].key?("account_type")
|
|
|
|
|
|
|
|
# Optional fields should be present (even if nil)
|
|
|
|
assert transaction_data.key?("category")
|
|
|
|
assert transaction_data.key?("merchant")
|
|
|
|
assert transaction_data.key?("tags")
|
|
|
|
assert transaction_data.key?("transfer")
|
|
|
|
assert transaction_data.key?("notes")
|
|
|
|
end
|
|
|
|
|
|
|
|
test "transactions with transfers should include transfer information" do
|
|
|
|
# Create a transfer between two accounts to test transfer rendering
|
|
|
|
from_account = @family.accounts.create!(
|
|
|
|
name: "Transfer From Account",
|
|
|
|
balance: 1000,
|
|
|
|
currency: "USD",
|
|
|
|
accountable: Depository.new
|
|
|
|
)
|
|
|
|
|
|
|
|
to_account = @family.accounts.create!(
|
|
|
|
name: "Transfer To Account",
|
|
|
|
balance: 0,
|
|
|
|
currency: "USD",
|
|
|
|
accountable: Depository.new
|
|
|
|
)
|
|
|
|
|
2025-06-20 13:31:58 -04:00
|
|
|
transfer = Transfer::Creator.new(
|
|
|
|
family: @family,
|
|
|
|
source_account_id: from_account.id,
|
|
|
|
destination_account_id: to_account.id,
|
Add comprehensive API v1 with OAuth and API key authentication (#2389)
* OAuth
* Add API test routes and update Doorkeeper token handling for test environment
- Introduced API namespace with test routes for controller testing in the test environment.
- Updated Doorkeeper configuration to allow fallback to plain tokens in the test environment for easier testing.
- Modified schema to change resource_owner_id type from bigint to string.
* Implement API key authentication and enhance access control
- Replaced Doorkeeper OAuth authentication with a custom method supporting both OAuth and API keys in the BaseController.
- Added methods for API key authentication, including validation and logging.
- Introduced scope-based authorization for API keys in the TestController.
- Updated routes to include API key management endpoints.
- Enhanced logging for API access to include authentication method details.
- Added tests for API key functionality, including validation, scope checks, and access control enforcement.
* Add API key rate limiting and usage tracking
- Implemented rate limiting for API key authentication in BaseController.
- Added methods to check rate limits, render appropriate responses, and include rate limit headers in responses.
- Updated routes to include a new usage resource for tracking API usage.
- Enhanced tests to verify rate limit functionality, including exceeding limits and per-key tracking.
- Cleaned up Redis data in tests to ensure isolation between test cases.
* Add Jbuilder for JSON rendering and refactor AccountsController
- Added Jbuilder gem for improved JSON response handling.
- Refactored index action in AccountsController to utilize Jbuilder for rendering JSON.
- Removed manual serialization of accounts and streamlined response structure.
- Implemented a before_action in BaseController to enforce JSON format for all API requests.
* Add transactions resource to API routes
- Added routes for transactions, allowing index, show, create, update, and destroy actions.
- This enhancement supports comprehensive transaction management within the API.
* Enhance API authentication and onboarding handling
- Updated BaseController to skip onboarding requirements for API endpoints and added manual token verification for OAuth authentication.
- Improved error handling and logging for invalid access tokens.
- Introduced a method to set up the current context for API requests, ensuring compatibility with session-like behavior.
- Excluded API paths from onboarding redirects in the Onboardable concern.
- Updated database schema to change resource_owner_id type from bigint to string for OAuth access grants.
* Fix rubocop offenses
- Fix indentation and spacing issues
- Convert single quotes to double quotes
- Add spaces inside array brackets
- Fix comment alignment
- Add missing trailing newlines
- Correct else/end alignment
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* 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>
* Add API transactions controller warning to brakeman ignore
The account_id parameter in the API transactions controller is properly
validated on line 79: family.accounts.find(transaction_params[:account_id])
This ensures users can only create transactions in accounts belonging to
their family, making this a false positive.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Signed-off-by: Josh Pigford <josh@joshpigford.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-17 15:57:05 -05:00
|
|
|
date: Date.current,
|
|
|
|
amount: 100
|
2025-06-20 13:31:58 -04:00
|
|
|
).create
|
Add comprehensive API v1 with OAuth and API key authentication (#2389)
* OAuth
* Add API test routes and update Doorkeeper token handling for test environment
- Introduced API namespace with test routes for controller testing in the test environment.
- Updated Doorkeeper configuration to allow fallback to plain tokens in the test environment for easier testing.
- Modified schema to change resource_owner_id type from bigint to string.
* Implement API key authentication and enhance access control
- Replaced Doorkeeper OAuth authentication with a custom method supporting both OAuth and API keys in the BaseController.
- Added methods for API key authentication, including validation and logging.
- Introduced scope-based authorization for API keys in the TestController.
- Updated routes to include API key management endpoints.
- Enhanced logging for API access to include authentication method details.
- Added tests for API key functionality, including validation, scope checks, and access control enforcement.
* Add API key rate limiting and usage tracking
- Implemented rate limiting for API key authentication in BaseController.
- Added methods to check rate limits, render appropriate responses, and include rate limit headers in responses.
- Updated routes to include a new usage resource for tracking API usage.
- Enhanced tests to verify rate limit functionality, including exceeding limits and per-key tracking.
- Cleaned up Redis data in tests to ensure isolation between test cases.
* Add Jbuilder for JSON rendering and refactor AccountsController
- Added Jbuilder gem for improved JSON response handling.
- Refactored index action in AccountsController to utilize Jbuilder for rendering JSON.
- Removed manual serialization of accounts and streamlined response structure.
- Implemented a before_action in BaseController to enforce JSON format for all API requests.
* Add transactions resource to API routes
- Added routes for transactions, allowing index, show, create, update, and destroy actions.
- This enhancement supports comprehensive transaction management within the API.
* Enhance API authentication and onboarding handling
- Updated BaseController to skip onboarding requirements for API endpoints and added manual token verification for OAuth authentication.
- Improved error handling and logging for invalid access tokens.
- Introduced a method to set up the current context for API requests, ensuring compatibility with session-like behavior.
- Excluded API paths from onboarding redirects in the Onboardable concern.
- Updated database schema to change resource_owner_id type from bigint to string for OAuth access grants.
* Fix rubocop offenses
- Fix indentation and spacing issues
- Convert single quotes to double quotes
- Add spaces inside array brackets
- Fix comment alignment
- Add missing trailing newlines
- Correct else/end alignment
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* 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>
* Add API transactions controller warning to brakeman ignore
The account_id parameter in the API transactions controller is properly
validated on line 79: family.accounts.find(transaction_params[:account_id])
This ensures users can only create transactions in accounts belonging to
their family, making this a false positive.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Signed-off-by: Josh Pigford <josh@joshpigford.com>
Co-authored-by: Claude <noreply@anthropic.com>
2025-06-17 15:57:05 -05:00
|
|
|
|
|
|
|
get api_v1_transaction_url(transfer.inflow_transaction), headers: api_headers(@api_key)
|
|
|
|
assert_response :success
|
|
|
|
|
|
|
|
transaction_data = JSON.parse(response.body)
|
|
|
|
assert_not_nil transaction_data["transfer"]
|
|
|
|
assert transaction_data["transfer"].key?("id")
|
|
|
|
assert transaction_data["transfer"].key?("amount")
|
|
|
|
assert transaction_data["transfer"].key?("currency")
|
|
|
|
assert transaction_data["transfer"].key?("other_account")
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def api_headers(api_key)
|
|
|
|
{ "X-Api-Key" => api_key.plain_key }
|
|
|
|
end
|
|
|
|
end
|