e8bd348c77
CI / lint (pull_request) Successful in 41s
CI / typecheck (pull_request) Successful in 56s
CI / security (pull_request) Successful in 51s
CI / quality (pull_request) Successful in 53s
CI / integration_tests (pull_request) Successful in 1m10s
CI / build (pull_request) Successful in 43s
CI / unit_tests (pull_request) Successful in 3m30s
CI / coverage (pull_request) Successful in 3m22s
CI / status-check (pull_request) Successful in 4s
CI / quality (push) Successful in 45s
CI / security (push) Successful in 48s
CI / lint (push) Successful in 49s
CI / typecheck (push) Successful in 50s
CI / build (push) Successful in 49s
CI / integration_tests (push) Successful in 1m9s
CI / unit_tests (push) Successful in 3m13s
CI / coverage (push) Successful in 3m7s
CI / status-check (push) Successful in 3s
Add RegistryError base with message, details, and original_reference fields. Implement all 9 typed exception types per Package Registry Standard §13.2: PackageNotFoundError, InvalidPackageIdError, InvalidPackageReferenceError, VersionNotFoundError, ValidationError, AuthenticationRequiredError, AccessDeniedError, ConflictError, and RegistryNetworkError. RegistryNetworkError carries status_code and url for transport failures. __str__ includes original_reference when available. exception_for_status() maps HTTP codes to typed exceptions. _ERROR_TYPE_MAP enables error-type parsing from JSON error bodies. 23 Behave BDD scenarios in features/registry_exceptions.feature. 12 Robot Framework integration tests in robot/exceptions.robot. Existing registry_http_client tests updated for InternalServerError removal. ISSUES CLOSED: #29
184 lines
9.3 KiB
Gherkin
184 lines
9.3 KiB
Gherkin
Feature: Registry Exception Hierarchy
|
|
As a developer using the Package Registry Standard v1.0.0
|
|
I want a typed exception hierarchy per §13.2
|
|
So that I can handle each error type appropriately
|
|
|
|
Background:
|
|
Given I have imported the registry exception module
|
|
|
|
# ── RegistryError construction ────────────────────────────────────────
|
|
|
|
Scenario: RegistryError with message only
|
|
When I raise a plain RegistryError with text "something went wrong"
|
|
Then the error message should be "something went wrong"
|
|
And the error should have no details
|
|
And the error should have no original_reference
|
|
|
|
Scenario: RegistryError with message and details
|
|
When I raise a RegistryError text "invalid input" and detail {"field": "name"}
|
|
Then the error message should be "invalid input"
|
|
And the error details should equal {"field": "name"}
|
|
And the error should have no original_reference
|
|
|
|
Scenario: RegistryError with message and original_reference
|
|
When I raise a RegistryError text "not found" and reference "pkg_act_unknown"
|
|
Then the error message should be "not found"
|
|
And the error original_reference should be "pkg_act_unknown"
|
|
And str of the error should be "not found [ref: pkg_act_unknown]"
|
|
|
|
Scenario: RegistryError with message, details, and original_reference
|
|
When I raise a RegistryError text "conflict" detail {"dup": true} and reference "pkg_act_abc"
|
|
Then the error message should be "conflict"
|
|
And the error details should equal {"dup": true}
|
|
And the error original_reference should be "pkg_act_abc"
|
|
And str of the error should be "conflict [ref: pkg_act_abc]"
|
|
|
|
# ── __str__ without original_reference ────────────────────────────────
|
|
|
|
Scenario: RegistryError __str__ is just the message when no reference
|
|
When I raise a plain RegistryError with text "plain error"
|
|
Then str of the error should be "plain error"
|
|
|
|
# ── RegistryNetworkError specific fields ──────────────────────────────
|
|
|
|
Scenario: RegistryNetworkError carries status_code and url
|
|
When I raise a simple network error message "server error" status 500 url "https://registry.example.com/packages/pkg_act_test"
|
|
Then the error message should be "server error"
|
|
And the error status_code should be 500
|
|
And the error url should be "https://registry.example.com/packages/pkg_act_test"
|
|
And str of the error should be "server error [status: 500] [url: https://registry.example.com/packages/pkg_act_test]"
|
|
|
|
Scenario: RegistryNetworkError with all fields
|
|
When I raise a fully detailed network error message "timeout" detail {"retries": 3} ref "pkg_act_x" status None url "https://bad.example.com"
|
|
Then the error message should be "timeout"
|
|
And the error details should equal {"retries": 3}
|
|
And the error original_reference should be "pkg_act_x"
|
|
And the error status_code should be None
|
|
And the error url should be "https://bad.example.com"
|
|
And str of the error should be "timeout [ref: pkg_act_x] [url: https://bad.example.com]"
|
|
|
|
# ── Typed subclass construction ──────────────────────────────────────
|
|
|
|
Scenario: PackageNotFoundError construction
|
|
When I raise a PackageNotFoundError with message "pkg not found"
|
|
Then the error message should be "pkg not found"
|
|
And the error should be an instance of PackageNotFoundError
|
|
|
|
Scenario: InvalidPackageIdError construction
|
|
When I raise an InvalidPackageIdError with message "bad id"
|
|
Then the error message should be "bad id"
|
|
And the error should be an instance of InvalidPackageIdError
|
|
|
|
Scenario: InvalidPackageReferenceError construction
|
|
When I raise an InvalidPackageReferenceError with message "bad ref"
|
|
Then the error message should be "bad ref"
|
|
And the error should be an instance of InvalidPackageReferenceError
|
|
|
|
Scenario: VersionNotFoundError construction
|
|
When I raise a VersionNotFoundError with message "version missing"
|
|
Then the error message should be "version missing"
|
|
And the error should be an instance of VersionNotFoundError
|
|
|
|
Scenario: ValidationError construction
|
|
When I raise a ValidationError with message "invalid"
|
|
Then the error message should be "invalid"
|
|
And the error should be an instance of ValidationError
|
|
|
|
Scenario: AuthenticationRequiredError construction
|
|
When I raise an AuthenticationRequiredError with message "auth needed"
|
|
Then the error message should be "auth needed"
|
|
And the error should be an instance of AuthenticationRequiredError
|
|
|
|
Scenario: AccessDeniedError construction
|
|
When I raise an AccessDeniedError with message "no access"
|
|
Then the error message should be "no access"
|
|
And the error should be an instance of AccessDeniedError
|
|
|
|
Scenario: ConflictError construction
|
|
When I raise a ConflictError with message "duplicate"
|
|
Then the error message should be "duplicate"
|
|
And the error should be an instance of ConflictError
|
|
|
|
# ── HTTP status code mapping (§13.2) ─────────────────────────────────
|
|
|
|
Scenario: 400 maps to InvalidPackageIdError
|
|
When I call exception_for_status with code 400 and message "bad request"
|
|
Then the result should be an instance of InvalidPackageIdError
|
|
And the error message should be "bad request"
|
|
|
|
Scenario: 401 maps to AuthenticationRequiredError
|
|
When I call exception_for_status with code 401 and message "auth required"
|
|
Then the result should be an instance of AuthenticationRequiredError
|
|
And the error message should be "auth required"
|
|
|
|
Scenario: 403 maps to AccessDeniedError
|
|
When I call exception_for_status with code 403 and message "denied"
|
|
Then the result should be an instance of AccessDeniedError
|
|
And the error message should be "denied"
|
|
|
|
Scenario: 404 maps to PackageNotFoundError
|
|
When I call exception_for_status with code 404 and message "gone"
|
|
Then the result should be an instance of PackageNotFoundError
|
|
And the error message should be "gone"
|
|
|
|
Scenario: 409 maps to ConflictError
|
|
When I call exception_for_status with code 409 and message "conflict"
|
|
Then the result should be an instance of ConflictError
|
|
And the error message should be "conflict"
|
|
|
|
Scenario: 500 maps to RegistryNetworkError
|
|
When I call exception_for_status with code 500 and message "server error"
|
|
Then the result should be an instance of RegistryNetworkError
|
|
And the error message should be "server error"
|
|
And the error status_code should be 500
|
|
|
|
Scenario: 503 maps to RegistryNetworkError
|
|
When I call exception_for_status with code 503 and message "unavailable"
|
|
Then the result should be an instance of RegistryNetworkError
|
|
And the error message should be "unavailable"
|
|
And the error status_code should be 503
|
|
|
|
Scenario: non-integer status_code falls back to RegistryError
|
|
When I call exception_for_status with non-integer code "not-a-number" and message "bad status"
|
|
Then the result should be an instance of RegistryError
|
|
And the error message should be "bad status"
|
|
|
|
Scenario: Unknown status falls back to RegistryError
|
|
When I call exception_for_status with code 418 and message "teapot"
|
|
Then the result should be an instance of RegistryError
|
|
And the error message should be "teapot"
|
|
|
|
# ── Inheritance chain ─────────────────────────────────────────────────
|
|
|
|
Scenario: All typed errors inherit from RegistryError
|
|
When I check that PackageNotFoundError is a subclass of RegistryError
|
|
And I check that InvalidPackageIdError is a subclass of RegistryError
|
|
And I check that InvalidPackageReferenceError is a subclass of RegistryError
|
|
And I check that VersionNotFoundError is a subclass of RegistryError
|
|
And I check that ValidationError is a subclass of RegistryError
|
|
And I check that AuthenticationRequiredError is a subclass of RegistryError
|
|
And I check that AccessDeniedError is a subclass of RegistryError
|
|
And I check that ConflictError is a subclass of RegistryError
|
|
And I check that RegistryNetworkError is a subclass of RegistryError
|
|
Then all 9 checks should pass
|
|
|
|
Scenario: RegistryError inherits from CleverAgentsException
|
|
When I check that RegistryError is a subclass of CleverAgentsException
|
|
Then all 1 checks should pass
|
|
|
|
# ── Error type map completeness ──────────────────────────────────────
|
|
|
|
Scenario: _ERROR_TYPE_MAP contains all error type keys
|
|
When I inspect the _ERROR_TYPE_MAP
|
|
Then the map should contain key "PackageNotFound"
|
|
And the map should contain key "InvalidPackageId"
|
|
And the map should contain key "InvalidPackageReference"
|
|
And the map should contain key "VersionNotFound"
|
|
And the map should contain key "ValidationError"
|
|
And the map should contain key "AuthenticationRequired"
|
|
And the map should contain key "AccessDenied"
|
|
And the map should contain key "Conflict"
|
|
And the map should contain key "InternalServerError"
|
|
And the value for key "InternalServerError" should be RegistryNetworkError
|
|
And the map should have exactly 9 keys
|