diff --git a/lib/zitadel-client/auth/o_auth_authenticator.rb b/lib/zitadel-client/auth/o_auth_authenticator.rb index 949db853..967d3116 100644 --- a/lib/zitadel-client/auth/o_auth_authenticator.rb +++ b/lib/zitadel-client/auth/o_auth_authenticator.rb @@ -84,7 +84,7 @@ def get_grant(auth_client, auth_scopes) def refresh_token @token = get_grant(@auth_session, @auth_scopes) rescue StandardError => e - raise RuntimeError.new("Failed to refresh token: #{e.message}"), cause: e + raise ApiError.new("Failed to refresh token: #{e.message}"), cause: e end end end diff --git a/spec/auth/use_access_token_spec.rb b/spec/auth/use_access_token_spec.rb new file mode 100644 index 00000000..5a1d22c5 --- /dev/null +++ b/spec/auth/use_access_token_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../spec_helper' + +# SettingsService Integration Tests (Personal Access Token) +# +# This suite verifies the Zitadel SettingsService API's general settings +# endpoint works when authenticating via a Personal Access Token: +# +# 1. Retrieve general settings successfully with a valid token +# 2. Expect an ApiError when using an invalid token +# +# Each test runs in isolation: the client is instantiated in each example to +# guarantee a clean, stateless call. +describe 'Zitadel SettingsService (Personal Access Token)' do + let(:base_url) { ENV.fetch('BASE_URL') { raise 'BASE_URL not set' } } + let(:valid_token) { ENV.fetch('AUTH_TOKEN') { raise 'AUTH_TOKEN not set' } } + let(:zitadel_client) do + ZitadelClient::Zitadel.with_access_token( + base_url, + valid_token + ) + end + + it 'retrieves general settings with valid token' do + client = zitadel_client + client.settings.settings_service_get_general_settings + end + + it 'raises an ApiError with invalid token' do + client = ZitadelClient::Zitadel.with_access_token( + base_url, + 'invalid' + ) + assert_raises(ZitadelClient::ApiError) do + client.settings.settings_service_get_general_settings + end + end +end diff --git a/spec/auth/use_client_credentials_spec.rb b/spec/auth/use_client_credentials_spec.rb new file mode 100644 index 00000000..85d236c6 --- /dev/null +++ b/spec/auth/use_client_credentials_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../spec_helper' + +# SettingsService Integration Tests (Client Credentials) +# +# This suite verifies the Zitadel SettingsService API's general settings +# endpoint works when authenticating via Client Credentials: +# +# 1. Retrieve general settings successfully with valid credentials +# 2. Expect an ApiError when using invalid credentials +# +# Each test runs in isolation: the client is instantiated in each example to +# guarantee a clean, stateless call. +describe 'Zitadel SettingsService (Client Credentials)' do + let(:base_url) { ENV.fetch('BASE_URL') { raise 'BASE_URL not set' } } + let(:client_id) { ENV.fetch('CLIENT_ID') { raise 'CLIENT_ID not set' } } + let(:client_secret) { ENV.fetch('CLIENT_SECRET') { raise 'CLIENT_SECRET not set' } } + let(:zitadel_client) do + ZitadelClient::Zitadel.with_client_credentials( + base_url, + client_id, + client_secret + ) + end + + it 'retrieves general settings with valid credentials' do + client = zitadel_client + client.settings.settings_service_get_general_settings + end + + it 'raises an ApiError with invalid credentials' do + client = ZitadelClient::Zitadel.with_client_credentials( + base_url, + 'invalid', + 'invalid' + ) + assert_raises(ZitadelClient::ApiError) do + client.settings.settings_service_get_general_settings + end + end +end diff --git a/spec/auth/use_private_key_spec.rb b/spec/auth/use_private_key_spec.rb new file mode 100644 index 00000000..d8905fc8 --- /dev/null +++ b/spec/auth/use_private_key_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative '../spec_helper' +require 'tempfile' + +# SettingsService Integration Tests (Private Key Assertion) +# +# This suite verifies the Zitadel SettingsService API's general settings +# endpoint works when authenticating via a private key assertion: +# +# 1. Retrieve general settings successfully with a valid private key +# 2. Expect an ApiError when using an invalid private key +# +# Each test runs in isolation: the client is instantiated in each example to +# guarantee a clean, stateless call. +describe 'Zitadel SettingsService (Private Key Assertion)' do + let(:base_url) { ENV.fetch('BASE_URL') { raise 'BASE_URL not set' } } + let(:jwt_file) do + file = Tempfile.new(%w[jwt .json]) + file.write(ENV.fetch('JWT_KEY') { raise 'JWT_KEY not set' }) + file.flush + file.close + file + end + let(:zitadel_client) do + ZitadelClient::Zitadel.with_private_key( + base_url, + jwt_file.path + ) + end + + it 'retrieves general settings with valid private key' do + client = zitadel_client + client.settings.settings_service_get_general_settings + end + + it 'raises an ApiError with invalid private key' do + client = ZitadelClient::Zitadel.with_private_key( + 'https://zitadel.cloud', + jwt_file.path + ) + assert_raises(ZitadelClient::ApiError) do + client.settings.settings_service_get_general_settings + end + end +end diff --git a/spec/check_session_service_spec.rb b/spec/check_session_service_spec.rb new file mode 100644 index 00000000..a2fced1d --- /dev/null +++ b/spec/check_session_service_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require 'minitest/autorun' + +# SessionService Integration Tests +# +# This suite verifies the Zitadel SessionService API's basic operations using a +# personal access token: +# +# 1. Create a session with specified checks and lifetime +# 2. Retrieve the session by ID +# 3. List sessions and ensure the created session appears +# 4. Update the session's lifetime and confirm a new token is returned +# +# Each test runs in isolation: a new session is created in `before` and deleted +# in `after` to ensure a clean state. + +require_relative 'spec_helper' +require 'securerandom' + +describe 'Zitadel SessionService' do + let(:base_url) { ENV.fetch('BASE_URL') { raise 'BASE_URL not set' } } + let(:valid_token) { ENV.fetch('AUTH_TOKEN') { raise 'AUTH_TOKEN not set' } } + let(:client) do + ZitadelClient::Zitadel.with_access_token( + base_url, + valid_token + ) + end + + before do + req = ZitadelClient::SessionServiceCreateSessionRequest.new( + checks: ZitadelClient::SessionServiceChecks.new( + user: ZitadelClient::SessionServiceCheckUser.new(login_name: 'johndoe') + ), + lifetime: '18000s' + ) + resp = client.sessions.session_service_create_session(req) + @session_id = resp.session_id + @session_token = resp.session_token + end + + after do + delete_req = ZitadelClient::SessionServiceDeleteSessionBody.new + begin + client.sessions.session_service_delete_session(@session_id, delete_req) + rescue StandardError + # Ignore cleanup errors + end + end + + it 'retrieves the session details by the session identifier' do + response = client.sessions.session_service_get_session( + @session_id, + session_token: @session_token + ) + _(response.session.id).must_equal @session_id + end + + it 'raises an error when retrieving a non-existent session' do + assert_raises(ZitadelClient::ApiError) do + client.sessions.session_service_get_session( + SecureRandom.uuid, + session_token: @session_token + ) + end + end + + it 'includes the created session when listing all sessions' do + request = ZitadelClient::SessionServiceListSessionsRequest.new(queries: []) + response = client.sessions.session_service_list_sessions(request) + _(response.sessions.map(&:id)).must_include @session_id + end + + it 'updates the session lifetime and returns a new token' do + request = ZitadelClient::SessionServiceSetSessionRequest.new(lifetime: '36000s') + response = client.sessions.session_service_set_session( + @session_id, + request + ) + _(response.session_token).must_be_instance_of String + end +end diff --git a/spec/check_user_service_spec.rb b/spec/check_user_service_spec.rb new file mode 100644 index 00000000..c71c240b --- /dev/null +++ b/spec/check_user_service_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'minitest/autorun' +require_relative 'spec_helper' +require 'securerandom' + +# UserService Integration Tests +# +# This suite verifies the Zitadel UserService API's basic operations using a +# personal access token: +# +# 1. Create a human user +# 2. Retrieve the user by ID +# 3. List users and ensure the created user appears +# 4. Update the user's email and confirm the change +# 5. Error when retrieving a non-existent user +# +# Each test runs in isolation: a new user is created in `before` and deleted in +# `after` to ensure a clean state. + +describe 'Zitadel UserService' do + let(:base_url) { ENV.fetch('BASE_URL') { raise 'BASE_URL not set' } } + let(:valid_token) { ENV.fetch('AUTH_TOKEN') { raise 'AUTH_TOKEN not set' } } + let(:client) do + ZitadelClient::Zitadel.with_access_token( + base_url, + valid_token + ) + end + + before do + request = ZitadelClient::UserServiceAddHumanUserRequest.new( + username: SecureRandom.hex, + profile: ZitadelClient::UserServiceSetHumanProfile.new( + given_name: 'John', + family_name: 'Doe' + ), + email: ZitadelClient::UserServiceSetHumanEmail.new( + email: "johndoe#{SecureRandom.hex}@example.com" + ) + ) + + @user = client.users.user_service_add_human_user(request) + end + + after do + client.users.user_service_delete_user(@user.user_id) + rescue StandardError + # Ignore cleanup errors + end + + it 'retrieves the user details by ID' do + response = client.users.user_service_get_user_by_id(@user.user_id) + _(response.user.user_id).must_equal @user.user_id + end + + it 'raises an error when retrieving a non-existent user' do + assert_raises(ZitadelClient::ApiError) do + client.users.user_service_get_user_by_id(SecureRandom.uuid) + end + end + + it 'includes the created user when listing all users' do + request = ZitadelClient::UserServiceListUsersRequest.new(queries: []) + response = client.users.user_service_list_users(request) + _(response.result.map(&:user_id)).must_include @user.user_id + end + + it "updates the user's email and reflects the change" do + new_email = "updated#{SecureRandom.hex}@example.com" + update_req = ZitadelClient::UserServiceUpdateHumanUserRequest.new( + email: ZitadelClient::UserServiceSetHumanEmail.new(email: new_email) + ) + client.users.user_service_update_human_user(@user.user_id, update_req) + + response = client.users.user_service_get_user_by_id(@user.user_id) + _(response.user.human.email.email).must_equal new_email + end +end diff --git a/spec/sdk_test_using_client_credentials_authentication_spec.rb b/spec/sdk_test_using_client_credentials_authentication_spec.rb deleted file mode 100644 index 6b194714..00000000 --- a/spec/sdk_test_using_client_credentials_authentication_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -require 'minitest/autorun' -require 'securerandom' -require_relative 'spec_helper' - -describe 'Zitadel Client' do - before do - @client_id = ENV.fetch('CLIENT_ID', nil) - @client_secret = ENV.fetch('CLIENT_SECRET', nil) - @base_url = ENV.fetch('BASE_URL', nil) - @user_id = create_user(@base_url, @client_id, @client_secret) - end - - # rubocop:disable Metrics/MethodLength - def create_user(base_url, client_id, client_secret) - client = ZitadelClient::Zitadel.with_client_credentials(base_url, client_id, client_secret) - - begin - response = client.users.user_service_add_human_user( - ZitadelClient::UserServiceAddHumanUserRequest.new( - username: SecureRandom.hex, - profile: ZitadelClient::UserServiceSetHumanProfile.new(given_name: 'John', family_name: 'Doe'), - email: ZitadelClient::UserServiceSetHumanEmail.new(email: "johndoe#{SecureRandom.hex}@caos.ag") - ) - ) - puts "User created: #{response}" - response.user_id - rescue StandardError => e - raise "Exception while creating user: #{e.message}" - end - end - # rubocop:enable Metrics/MethodLength - - describe 'with valid token' do - it 'deactivates and reactivates a user' do - client = ZitadelClient::Zitadel.with_client_credentials(@base_url, @client_id, @client_secret) - - begin - deactivate_response = client.users.user_service_deactivate_user(@user_id) - puts "User deactivated: #{deactivate_response}" - - reactivate_response = client.users.user_service_reactivate_user(@user_id) - puts "User reactivated: #{reactivate_response}" - - # you can add real assertions here, for example: - # _(reactivate_response).must_respond_to :user_id - rescue StandardError => e - flunk "Exception when calling deactivate_user or reactivate_user with valid token: #{e.message}" - end - end - end - - describe 'with invalid token' do - it 'does not deactivate or reactivate a user' do - client = ZitadelClient::Zitadel.with_client_credentials(@base_url, 'id', 'secret') - - # deactivate should raise - assert_raises(StandardError) do - client.users.user_service_deactivate_user(@user_id) - end - - # reactivate should raise - assert_raises(StandardError) do - client.users.user_service_reactivate_user(@user_id) - end - end - end -end diff --git a/spec/sdk_test_using_personal_access_token_authentication_spec.rb b/spec/sdk_test_using_personal_access_token_authentication_spec.rb deleted file mode 100644 index 553e56b2..00000000 --- a/spec/sdk_test_using_personal_access_token_authentication_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -# frozen_string_literal: true - -require_relative 'spec_helper' -require 'securerandom' - -describe 'Zitadel Client (Personal Access Token)' do - before do - @valid_token = ENV.fetch('AUTH_TOKEN', nil) - @invalid_token = 'whoops' - @base_url = ENV.fetch('BASE_URL', nil) - @user_id = create_user(@valid_token, @base_url) - end - - # rubocop:disable Metrics/MethodLength - def create_user(token, base_url) - client = ZitadelClient::Zitadel.with_access_token(base_url, token) - - begin - resp = client.users.user_service_add_human_user( - ZitadelClient::UserServiceAddHumanUserRequest.new( - username: SecureRandom.hex, - profile: ZitadelClient::UserServiceSetHumanProfile.new(given_name: 'John', family_name: 'Doe'), - email: ZitadelClient::UserServiceSetHumanEmail.new(email: "johndoe#{SecureRandom.hex}@caos.ag") - ) - ) - puts "User created: #{resp}" - resp.user_id - rescue StandardError => e - raise "Exception while creating user: #{e.message}" - end - end - # rubocop:enable Metrics/MethodLength - - describe 'with valid token' do - it 'deactivates and reactivates a user without error' do - client = ZitadelClient::Zitadel.with_access_token(@base_url, @valid_token) - - begin - deactivate_resp = client.users.user_service_deactivate_user(@user_id) - puts "User deactivated: #{deactivate_resp}" - - reactivate_resp = client.users.user_service_reactivate_user(@user_id) - puts "User reactivated: #{reactivate_resp}" - rescue StandardError => e - flunk "Exception when calling deactivate_user or reactivate_user with valid token: #{e.message}" - end - end - end - - describe 'with invalid token' do - it 'raises an ApiError when deactivating or reactivating' do - client = ZitadelClient::Zitadel.with_access_token(@base_url, @invalid_token) - - # Expect API authentication errors - assert_raises(ZitadelClient::ApiError) do - client.users.user_service_deactivate_user(@user_id) - end - - assert_raises(ZitadelClient::ApiError) do - client.users.user_service_reactivate_user(@user_id) - end - end - end -end diff --git a/spec/sdk_test_using_web_token_authentication_spec.rb b/spec/sdk_test_using_web_token_authentication_spec.rb deleted file mode 100644 index 26040fc4..00000000 --- a/spec/sdk_test_using_web_token_authentication_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -require_relative 'spec_helper' -require 'securerandom' -require 'tempfile' - -describe 'Zitadel Client (JWT Bearer OAuth)' do - before do - jwt_key = ENV.fetch('JWT_KEY') { raise 'JWT_KEY not set in environment' } - # Create and retain the Tempfile so it isn't GC'd before the test runs - @jwt_file = Tempfile.new(%w[jwt .json]) - @jwt_file.write(jwt_key) - @jwt_file.flush - @jwt_file.close - @key_file = @jwt_file.path - - @base_url = ENV.fetch('BASE_URL', nil) - @user_id = create_user(@key_file, @base_url) - end - - def create_temp_keyfile(jwt_key) - file = Tempfile.new('jwt_') - file.write(jwt_key) - file.close - file.path - end - - # rubocop:disable Metrics/MethodLength - def create_user(key_file, base_url) - client = ZitadelClient::Zitadel.with_private_key(base_url, key_file) - - begin - resp = client.users.user_service_add_human_user( - ZitadelClient::UserServiceAddHumanUserRequest.new( - username: SecureRandom.hex, - profile: ZitadelClient::UserServiceSetHumanProfile.new(given_name: 'John', family_name: 'Doe'), - email: ZitadelClient::UserServiceSetHumanEmail.new(email: "johndoe#{SecureRandom.hex}@caos.ag") - ) - ) - puts "User created: #{resp}" - resp.user_id - rescue StandardError => e - raise "Exception while creating user: #{e.message}" - end - end - # rubocop:enable Metrics/MethodLength - - describe 'with valid token' do - it 'deactivates and reactivates a user without error' do - raise ArgumentError, 'key_file cannot be nil' if @key_file.nil? - - client = ZitadelClient::Zitadel.with_private_key(@base_url, @key_file) - - begin - deactivate_resp = client.users.user_service_deactivate_user(@user_id) - puts "User deactivated: #{deactivate_resp}" - - reactivate_resp = client.users.user_service_reactivate_user(@user_id) - puts "User reactivated: #{reactivate_resp}" - rescue StandardError => e - flunk "Exception when calling deactivate_user or reactivate_user with valid token: #{e.message}" - end - end - end -end