From a2333623b693b508b72f40bd4742a954f3fc0b33 Mon Sep 17 00:00:00 2001 From: Fernando Olivares Date: Fri, 12 Dec 2025 12:45:00 -0600 Subject: [PATCH] Add unit tests for the new endpoints --- test/controllers/api_test.rb | 99 ++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/test/controllers/api_test.rb b/test/controllers/api_test.rb index 1830dc2a3..c4b1f433a 100644 --- a/test/controllers/api_test.rb +++ b/test/controllers/api_test.rb @@ -16,14 +16,85 @@ class ApiTest < ActionDispatch::IntegrationTest test "magic link consumption" do identity = identities(:david) magic_link = identity.send_magic_link + pending_token = pending_authentication_token_for(identity.email_address) + + untenanted do + post session_magic_link_path(format: :json), params: { code: magic_link.code, pending_authentication_token: pending_token } + assert_response :success + assert @response.parsed_body["session_token"].present? + end + end + + test "full JSON authentication flow with pending_authentication_token" do + identity = identities(:david) + + untenanted do + post session_path(format: :json), params: { email_address: identity.email_address } + assert_response :created + pending_token = @response.parsed_body["pending_authentication_token"] + assert pending_token.present? + + magic_link = MagicLink.last + post session_magic_link_path(format: :json), params: { code: magic_link.code, pending_authentication_token: pending_token } + assert_response :success + assert @response.parsed_body["session_token"].present? + end + end + + test "magic link consumption without pending_authentication_token returns unauthorized" do + identity = identities(:david) + magic_link = identity.send_magic_link untenanted do post session_magic_link_path(format: :json), params: { code: magic_link.code } + assert_response :unauthorized + assert_equal "Enter your email address to sign in.", @response.parsed_body["message"] + end + end + + test "magic link consumption with invalid code via JSON" do + identity = identities(:david) + pending_token = pending_authentication_token_for(identity.email_address) + + untenanted do + post session_magic_link_path(format: :json), params: { code: "INVALID", pending_authentication_token: pending_token } + assert_response :unauthorized + assert_equal "Try another code.", @response.parsed_body["message"] + end + end + + test "magic link consumption with cross-user code via JSON creates session for magic link owner" do + identity = identities(:david) + other_identity = identities(:jason) + magic_link = other_identity.send_magic_link + pending_token = pending_authentication_token_for(identity.email_address) + + # Note: Unlike the HTML flow, the JSON flow creates a session for the magic link's identity + # regardless of whose pending_authentication_token was provided. The token only proves + # that *someone* requested a magic link for *some* email address. + untenanted do + post session_magic_link_path(format: :json), params: { code: magic_link.code, pending_authentication_token: pending_token } assert_response :success assert @response.parsed_body["session_token"].present? end end + test "magic link consumption with expired pending_authentication_token" do + identity = identities(:david) + magic_link = identity.send_magic_link + + expired_token = nil + travel_to 15.minutes.ago do + expired_token = pending_authentication_token_for(identity.email_address) + end + + untenanted do + post session_magic_link_path(format: :json), params: { code: magic_link.code, pending_authentication_token: expired_token } + assert_response :unauthorized + assert_equal "Enter your email address to sign in.", @response.parsed_body["message"] + end + end + test "authenticate with valid access token" do get boards_path(format: :json), env: @davids_bearer_token assert_response :success @@ -42,8 +113,36 @@ class ApiTest < ActionDispatch::IntegrationTest assert_response :success end + test "create session for new user via JSON" do + new_email = "new-user-#{SecureRandom.hex(6)}@example.com" + + untenanted do + assert_difference -> { Identity.count }, 1 do + assert_difference -> { MagicLink.count }, 1 do + post session_path(format: :json), params: { email_address: new_email } + end + end + assert_response :created + assert @response.parsed_body["pending_authentication_token"].present? + assert MagicLink.last.for_sign_up? + end + end + + test "create session with invalid email via JSON" do + untenanted do + assert_no_difference -> { Identity.count } do + post session_path(format: :json), params: { email_address: "not-a-valid-email" } + end + assert_response :unprocessable_entity + end + end + private def bearer_token_env(token) { "HTTP_AUTHORIZATION" => "Bearer #{token}" } end + + def pending_authentication_token_for(email_address) + Rails.application.message_verifier(:pending_authentication).generate(email_address, expires_in: 10.minutes) + end end