|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +$LOAD_PATH.unshift File.join(__dir__, '../lib') |
| 4 | + |
| 5 | +require 'acme/client' |
| 6 | + |
| 7 | +RSpec.describe 'Full problem document (acme_error_body) support' do |
| 8 | + let(:problem_document) do |
| 9 | + { |
| 10 | + 'type' => 'urn:ietf:params:acme:error:rateLimited', |
| 11 | + 'detail' => 'too many certificates already issued for "example.com"', |
| 12 | + 'status' => 429, |
| 13 | + 'instance' => 'https://ca.example.com/acme/error/abc123' |
| 14 | + } |
| 15 | + end |
| 16 | + |
| 17 | + describe Acme::Client::Error do |
| 18 | + it 'exposes the full problem document when provided' do |
| 19 | + error = Acme::Client::Error.new('rate limited', acme_error_body: problem_document) |
| 20 | + expect(error.acme_error_body).to eq(problem_document) |
| 21 | + end |
| 22 | + |
| 23 | + it 'defaults to nil when not provided' do |
| 24 | + error = Acme::Client::Error.new('some error') |
| 25 | + expect(error.acme_error_body).to be_nil |
| 26 | + end |
| 27 | + |
| 28 | + it 'works with no arguments' do |
| 29 | + error = Acme::Client::Error.new |
| 30 | + expect(error.acme_error_body).to be_nil |
| 31 | + end |
| 32 | + |
| 33 | + it 'preserves CA-specific extension fields' do |
| 34 | + body = problem_document.merge('boulder-requester' => '12345') |
| 35 | + error = Acme::Client::Error.new('error', acme_error_body: body) |
| 36 | + expect(error.acme_error_body['boulder-requester']).to eq('12345') |
| 37 | + end |
| 38 | + |
| 39 | + it 'provides access to status from the problem document' do |
| 40 | + error = Acme::Client::Error.new('error', acme_error_body: problem_document) |
| 41 | + expect(error.acme_error_body['status']).to eq(429) |
| 42 | + end |
| 43 | + |
| 44 | + it 'provides access to instance URL from the problem document' do |
| 45 | + error = Acme::Client::Error.new('error', acme_error_body: problem_document) |
| 46 | + expect(error.acme_error_body['instance']).to eq('https://ca.example.com/acme/error/abc123') |
| 47 | + end |
| 48 | + end |
| 49 | + |
| 50 | + describe 'acme_error_body on error subclasses' do |
| 51 | + it 'works on ServerError subclasses' do |
| 52 | + error = Acme::Client::Error::Unauthorized.new('unauthorized', acme_error_body: problem_document) |
| 53 | + expect(error.acme_error_body).to eq(problem_document) |
| 54 | + end |
| 55 | + |
| 56 | + it 'works on RateLimited with positional args preserved' do |
| 57 | + error = Acme::Client::Error::RateLimited.new('rate limited', 60, acme_error_body: problem_document) |
| 58 | + expect(error.retry_after).to eq(60) |
| 59 | + expect(error.acme_error_body).to eq(problem_document) |
| 60 | + end |
| 61 | + |
| 62 | + it 'works on RateLimited with defaults' do |
| 63 | + error = Acme::Client::Error::RateLimited.new |
| 64 | + expect(error.retry_after).to eq(10) |
| 65 | + expect(error.acme_error_body).to be_nil |
| 66 | + end |
| 67 | + |
| 68 | + it 'all ACME_ERRORS subclasses accept acme_error_body keyword' do |
| 69 | + Acme::Client::Error::ACME_ERRORS.each_value do |error_class| |
| 70 | + next if error_class == Acme::Client::Error::RateLimited |
| 71 | + |
| 72 | + error = error_class.new('test', acme_error_body: problem_document) |
| 73 | + expect(error.acme_error_body).to eq(problem_document), |
| 74 | + "#{error_class} did not accept acme_error_body correctly" |
| 75 | + end |
| 76 | + end |
| 77 | + end |
| 78 | + |
| 79 | + describe 'problem documents with subproblems field' do |
| 80 | + it 'preserves subproblems in the raw body for downstream parsing' do |
| 81 | + body = problem_document.merge( |
| 82 | + 'subproblems' => [ |
| 83 | + { |
| 84 | + 'type' => 'urn:ietf:params:acme:error:rejectedIdentifier', |
| 85 | + 'detail' => 'CA will not issue for "example.net"', |
| 86 | + 'identifier' => { 'type' => 'dns', 'value' => 'example.net' } |
| 87 | + } |
| 88 | + ] |
| 89 | + ) |
| 90 | + error = Acme::Client::Error::Malformed.new('rejected', acme_error_body: body) |
| 91 | + expect(error.acme_error_body['subproblems'].length).to eq(1) |
| 92 | + expect(error.acme_error_body['subproblems'].first['identifier']['value']).to eq('example.net') |
| 93 | + end |
| 94 | + end |
| 95 | +end |
0 commit comments