|
7 | 7 | use CoyoteCert\Enums\AuthorizationChallengeEnum; |
8 | 8 | use CoyoteCert\Enums\KeyType; |
9 | 9 | use CoyoteCert\Exceptions\AcmeException; |
| 10 | +use CoyoteCert\Exceptions\AuthException; |
10 | 11 | use CoyoteCert\Exceptions\OrderNotFoundException; |
11 | 12 | use CoyoteCert\Exceptions\RateLimitException; |
12 | 13 | use CoyoteCert\Http\Response; |
@@ -429,7 +430,7 @@ function makeTestCerts(): array |
429 | 430 | ), withKeyStorage()); |
430 | 431 |
|
431 | 432 | expect(fn() => $api->order()->new(makeAccountData(), ['example.com'])) |
432 | | - ->toThrow(AcmeException::class, 'Creating new order failed'); |
| 433 | + ->toThrow(AcmeException::class, 'Bad Request'); |
433 | 434 | }); |
434 | 435 |
|
435 | 436 | it('Order::new() returns OrderData on 201 response', function () { |
@@ -583,6 +584,78 @@ function makeTestCerts(): array |
583 | 584 | ->toThrow(AcmeException::class, 'Internal error'); |
584 | 585 | }); |
585 | 586 |
|
| 587 | +it('Order::get() throws RateLimitException with parsed Retry-After seconds', function () { |
| 588 | + $storage = withKeyStorage(); |
| 589 | + |
| 590 | + $mock = closureMock( |
| 591 | + getHandler: fn($url) => new Response([], $url, 200, directoryBody()), |
| 592 | + postHandler: fn($url) => str_contains($url, 'new-account') |
| 593 | + ? new Response(['location' => 'https://acme.example/account/1'], $url, 200, accountBody()) |
| 594 | + : new Response(['retry-after' => '30'], $url, 429, ['detail' => 'Rate limited']), |
| 595 | + ); |
| 596 | + |
| 597 | + $caught = null; |
| 598 | + try { |
| 599 | + makeEndpointApi($mock, $storage)->order()->get('spam'); |
| 600 | + } catch (RateLimitException $e) { |
| 601 | + $caught = $e; |
| 602 | + } |
| 603 | + |
| 604 | + expect($caught)->toBeInstanceOf(RateLimitException::class); |
| 605 | + expect($caught->getRetryAfter())->toBe(30); |
| 606 | +}); |
| 607 | + |
| 608 | +it('Order::get() throws AuthException on 401', function () { |
| 609 | + $storage = withKeyStorage(); |
| 610 | + |
| 611 | + $mock = closureMock( |
| 612 | + getHandler: fn($url) => new Response([], $url, 200, directoryBody()), |
| 613 | + postHandler: fn($url) => str_contains($url, 'new-account') |
| 614 | + ? new Response(['location' => 'https://acme.example/account/1'], $url, 200, accountBody()) |
| 615 | + : new Response([], $url, 401, ['detail' => 'Unauthorized']), |
| 616 | + ); |
| 617 | + |
| 618 | + expect(fn() => makeEndpointApi($mock, $storage)->order()->get('auth-fail')) |
| 619 | + ->toThrow(AuthException::class, 'Unauthorized'); |
| 620 | +}); |
| 621 | + |
| 622 | +it('Order::new() throws RateLimitException on 429', function () { |
| 623 | + $api = makeEndpointApi(endpointMock( |
| 624 | + getBody: directoryBody(), |
| 625 | + postBody: ['detail' => 'Too many certificates already issued'], |
| 626 | + postCode: 429, |
| 627 | + ), withKeyStorage()); |
| 628 | + |
| 629 | + expect(fn() => $api->order()->new(makeAccountData(), ['example.com'])) |
| 630 | + ->toThrow(RateLimitException::class, 'Too many certificates already issued'); |
| 631 | +}); |
| 632 | + |
| 633 | +it('Order::new() attaches RFC 8555 §6.7 subproblems to the thrown exception', function () { |
| 634 | + $subproblems = [ |
| 635 | + [ |
| 636 | + 'type' => 'urn:ietf:params:acme:error:rejectedIdentifier', |
| 637 | + 'detail' => 'This CA will not issue for "bad.example.com"', |
| 638 | + 'identifier' => ['type' => 'dns', 'value' => 'bad.example.com'], |
| 639 | + ], |
| 640 | + ]; |
| 641 | + |
| 642 | + $api = makeEndpointApi(endpointMock( |
| 643 | + getBody: directoryBody(), |
| 644 | + postBody: ['detail' => 'Some identifiers were rejected', 'subproblems' => $subproblems], |
| 645 | + postCode: 400, |
| 646 | + ), withKeyStorage()); |
| 647 | + |
| 648 | + $caught = null; |
| 649 | + try { |
| 650 | + $api->order()->new(makeAccountData(), ['example.com', 'bad.example.com']); |
| 651 | + } catch (AcmeException $e) { |
| 652 | + $caught = $e; |
| 653 | + } |
| 654 | + |
| 655 | + expect($caught)->toBeInstanceOf(AcmeException::class); |
| 656 | + expect($caught->getSubproblems())->toBe($subproblems); |
| 657 | +}); |
| 658 | + |
586 | 659 | it('Order::get() returns OrderData on success', function () { |
587 | 660 | $storage = withKeyStorage(); |
588 | 661 |
|
|
0 commit comments