Skip to main content

testing

Write correct and complete RSpec tests for Doorkeeper. Use when adding specs for new features, writing regression tests for bug fixes, or reviewing test coverage for a change.

Source facts

Repository
doorkeeper-gem/doorkeeper
Last source activity
August 17, 2026 at 13:19
Detected SKILL.md language
English
Stars
5,523
Forks
1,076

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
testing
description
Write correct and complete RSpec tests for Doorkeeper. Use when adding specs for new features, writing regression tests for bug fixes, or reviewing test coverage for a change.
# Testing When writing or modifying tests in Doorkeeper, use this skill to ensure tests are correct, complete, and follow project conventions. ## Test Organization | What you're testing | Where to put the spec | |--------------------|----------------------| | OAuth request/response flow (integration) | `spec/requests/flows/` | | Endpoint behavior (HTTP layer) | `spec/requests/endpoints/` | | Controller logic (unit) | `spec/controllers/` | | Model behavior & mixins | `spec/models/doorkeeper/` | | OAuth protocol logic (unit) | `spec/lib/oauth/` | | Configuration | `spec/lib/config_spec.rb` | | Generators | `spec/generators/` | | Routing | `spec/routing/` | | Grape integration | `spec/grape/` | ## Spec Conventions ### Style ```ruby # frozen_string_literal: true require "spec_helper" RSpec.describe Doorkeeper::OAuth::SomeClass do describe "#method_name" do context "when condition is met" do it "does the expected thing" do # arrange, act, assert end end end end ``` ### Require Line All specs use `require "spec_helper"`. The `spec_helper` loads the dummy Rails app, database, factories, and support helpers — it covers both unit and integration needs. > **Note:** A legacy `spec_helper_integration` file exists but is just a compatibility wrapper around `spec_helper`. Do not use it in new specs. ### Factories Factories are in `spec/factories.rb`. Use FactoryBot: ```ruby let(:application) { FactoryBot.create(:application) } let(:access_token) { FactoryBot.create(:access_token, application: application) } let(:access_grant) { FactoryBot.create(:access_grant, application: application) } ``` ### Helper Methods Reuse helpers from `spec/support/helpers/`: - `model_helper.rb` — `client_exists`, `create_resource_owner`, token/grant existence checks - `request_spec_helper.rb` — `json_response`, `should_have_status`, `url_should_have_param`, `basic_auth_header_for_client` - `url_helper.rb` — `token_endpoint_url`, `authorization_endpoint_url`, `refresh_token_endpoint_url` - `config_helper.rb` — `config_is_set` (temporarily override config in a block) - `authorization_request_helper.rb` — `resource_owner_is_authenticated`, `default_scopes_exist` ### Configuration Changes in Tests Use `config_is_set` or the Doorkeeper.configure block (resets after each test): ```ruby before do config_is_set(:access_token_expires_in, 100) end ``` ## Test Patterns ### Request/Flow Specs (most important) ```ruby RSpec.describe "Authorization Code Flow" do let(:application) { FactoryBot.create(:application) } let(:resource_owner) { User.create!(name: "owner", password: "password") } before do default_scopes_exist :public resource_owner_is_authenticated resource_owner end it "issues an access token" do visit authorization_endpoint_url(client: application) click_on "Authorize" code = current_params["code"] post token_endpoint_url, params: { grant_type: "authorization_code", code: code, redirect_uri: application.redirect_uri, client_id: application.uid, client_secret: application.secret, } expect(response).to have_http_status(:ok) expect(json_response["access_token"]).to be_present end end ``` ### OAuth Unit Specs ```ruby RSpec.describe Doorkeeper::OAuth::AuthorizationCodeRequest do subject(:request) do described_class.new(server, grant, client, params) end let(:server) do double :server, access_token_expires_in: 2.days, refresh_token_enabled?: false, custom_access_token_expires_in: lambda { |context| context.grant_type == Doorkeeper::OAuth::AUTHORIZATION_CODE ? 1234 : nil } end let(:resource_owner) { FactoryBot.create :resource_owner } let(:grant) do FactoryBot.create :access_grant, resource_owner_id: resource_owner.id, resource_owner_type: resource_owner.class.name end let(:client) { grant.application } let(:redirect_uri) { client.redirect_uri } let(:params) { { redirect_uri: redirect_uri } } before do allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true) end describe "#authorize" do it "issues a new token for the client" do expect { request.authorize }.to change { client.reload.access_tokens.count }.by(1) end it "revokes the grant" do expect { request.authorize }.to(change { grant.reload.accessible? }) end end describe "#validate" do it "requires the grant to be accessible" do grant.revoke request.validate expect(request.error).to eq(Doorkeeper::Errors::InvalidGrant) end it "requires the client" do request = described_class.new(server, grant, nil, params) request.validate expect(request.error).to eq(Doorkeeper::Errors::InvalidClient) end end end ``` ## What to Test ### For new features: 1. Happy path — the feature works as intended 2. Error cases — invalid input, missing parameters, unauthorized access 3. Edge cases — nil values, empty strings, boundary conditions 4. Configuration interaction — does it respect relevant config options? 5. Backward compatibility — does existing behavior still pass? ### For bug fixes: 1. Regression test — reproduce the exact bug scenario, verify it's fixed 2. Related edge cases — similar situations that might also be affected ### For security fixes: 1. The vulnerability is no longer exploitable 2. The fix doesn't break legitimate use cases 3. Error responses don't leak information ## Common Pitfalls - **Don't use `sleep`** — use `Timecop.travel` or `Timecop.freeze` instead - **Don't hardcode token values** — let the system generate them - **Don't test private methods directly** — test through the public interface - **Don't forget scopes** — many features are scope-dependent, set them up explicitly - **Use `Timecop`** for time-dependent tests (expiration, token lifetime) ## Verification 1. Run new specs in isolation: `bundle exec rspec spec/path/to/new_spec.rb` 2. Run the full related directory: `bundle exec rspec spec/lib/oauth/` 3. Run full suite: `bundle exec rake spec` 4. Specs must pass in any order (`--order random`)
View on GitHub