Files
cleveractors-core/features/registry_reference_resolver.feature
CoreRasurae 517dfb4cce
CI / lint (pull_request) Successful in 35s
CI / quality (pull_request) Successful in 45s
CI / integration_tests (pull_request) Successful in 1m2s
CI / typecheck (pull_request) Successful in 1m10s
CI / security (pull_request) Successful in 1m10s
CI / build (pull_request) Successful in 42s
CI / unit_tests (pull_request) Successful in 3m21s
CI / coverage (pull_request) Successful in 3m7s
CI / status-check (pull_request) Successful in 3s
CI / quality (push) Successful in 50s
CI / security (push) Successful in 51s
CI / lint (push) Successful in 54s
CI / typecheck (push) Successful in 58s
CI / integration_tests (push) Successful in 1m0s
CI / build (push) Successful in 43s
CI / unit_tests (push) Successful in 3m23s
CI / coverage (push) Successful in 3m14s
CI / status-check (push) Successful in 3s
feat(registry): implement local namespace reference resolution
Implements the local:<path> reference scheme per Package Registry Standard v1.0.0 §5.3.

Closes #46
2026-06-12 17:11:35 +01:00

275 lines
13 KiB
Gherkin

Feature: Package Registry Reference Resolver
As a developer
I want to parse and resolve package references
So that I can use the Package Registry Standard v1.0.0 reference formats
Background:
Given I have imported the registry resolver module
# ── Reference parsing through ReferenceResolver.parse ─────────────────────
Scenario: Registry reference with version parses all fields
Given a package reference string "registry.example.com:acme/search@v1.2.3"
When I parse it with ReferenceResolver.parse
Then the reference_type should be REGISTRY
And the server should be "registry.example.com"
And the namespace should be "acme"
And the name should be "search"
And the version should be "v1.2.3"
And the original_reference should be "registry.example.com:acme/search@v1.2.3"
Scenario: Registry reference with latest alias
Given a package reference string "registry.cleverthis.com:acme/web_search@latest"
When I parse it with ReferenceResolver.parse
Then the reference_type should be REGISTRY
And the server should be "registry.cleverthis.com"
And the namespace should be "acme"
And the name should be "web_search"
And the version should be "latest"
Scenario: Registry reference without version defaults to latest
Given a package reference string "registry.cleverthis.com:simple/agent"
When I parse it with ReferenceResolver.parse
Then the version should be "latest"
Scenario: ID reference parsing
Given a package reference string "ID:pkg_act_0123456789abcdef0123456789abcdef01234567"
When I parse it with ReferenceResolver.parse
Then the reference_type should be ID
And the id_string should be "pkg_act_0123456789abcdef0123456789abcdef01234567"
And the parsed server should be null
And the parsed namespace should be null
Scenario: ID reference with graph type
Given a package reference string "ID:pkg_grh_deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
When I parse it with ReferenceResolver.parse
Then the reference_type should be ID
And the id_string should be "pkg_grh_deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
Scenario: Local reference parsing
Given a package reference string "local:./my-actor"
When I parse it with ReferenceResolver.parse
Then the reference_type should be LOCAL
And the name should be "./my-actor"
Scenario: Local reference with path
Given a package reference string "local:path/to/component"
When I parse it with ReferenceResolver.parse
Then the reference_type should be LOCAL
And the name should be "path/to/component"
Scenario: original_reference survives round-trip through parsing
Given a package reference string "registry.cleverthis.com:acme/web_search@v1.0.0"
When I parse it with ReferenceResolver.parse
And I convert the PackageReference to str
Then the str result is "registry.cleverthis.com:acme/web_search@v1.0.0"
Scenario: Empty reference string raises InvalidPackageReferenceError
Given an empty package reference string
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "must not be empty"
Scenario: Reference without colon separator raises error
Given a package reference string "nocolonserver"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "must be in format"
Scenario: Registry reference with empty namespace raises error
Given a package reference string "srv:/name"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "must include a namespace"
Scenario: Registry reference with empty name raises error
Given a package reference string "srv:ns/"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "must include a name"
Scenario: Registry reference with empty server raises error
Given a package reference string ":ns/name"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "must include a server name"
Scenario: Registry reference with empty version after at defaults to latest
Given a package reference string "srv:ns/name@"
When I parse it with ReferenceResolver.parse
Then the version should be "latest"
Scenario: Registry reference with @ in namespace raises error
Given a package reference string "registry.com:ns/n@me@v1.0.0"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "@"
Scenario: Registry reference with path traversal in name raises error
Given a package reference string "registry.com:foo/../bar@v1.0.0"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "must contain only"
Scenario: Local reference with directory traversal raises error
Given a package reference string "local:../../../etc/passwd"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing ".."
Scenario: Local reference with absolute path raises error
Given a package reference string "local:/etc/shadow"
When I parse it with ReferenceResolver.parse
Then an InvalidPackageReferenceError should be raised with message containing "absolute"
# ── Reference resolution ──────────────────────────────────────────────────
Scenario: ID reference resolves directly to PackageId
Given a package reference string "ID:pkg_act_0123456789abcdef0123456789abcdef01234567"
And I create a ReferenceResolver without a client
When I resolve the reference
Then the resolved PackageId id_string should be "pkg_act_0123456789abcdef0123456789abcdef01234567"
Scenario: Registry reference resolution requires a client
Given a package reference string "registry.example.com:acme/search@v1.0.0"
And I create a ReferenceResolver without a client
When I resolve the reference
Then an InvalidPackageReferenceError should be raised with message containing "RegistryClient is required"
Scenario: Local reference resolution requires a LocalPackageStore
Given a package reference string "local:./my-actor"
And I create a ReferenceResolver without a client
When I resolve the reference
Then an InvalidPackageReferenceError should be raised with message containing "no LocalPackageStore configured"
Scenario: ReferenceResolver can resolve registry ref with mock client
Given a package reference string "registry.example.com:acme/search@v1.0.0"
And I create a ReferenceResolver with a mock client that returns "pkg_act_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
When I resolve the reference
Then the resolved PackageId id_string should be "pkg_act_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
Scenario: ReferenceResolver wraps client RegistryError in InvalidPackageReferenceError
Given a package reference string "registry.example.com:acme/search@v1.0.0"
And I create a ReferenceResolver with a mock client that raises PackageNotFoundError
When I resolve the reference
Then an InvalidPackageReferenceError should be raised with message containing "not found"
Scenario: ReferenceResolver close closes client when client is present
When I create a ReferenceResolver with a mock close tracking client
And I call close on the ReferenceResolver
Then the mock client close method should have been called
Scenario: ReferenceResolver supports async context manager
When I use ReferenceResolver as an async context manager with a mock client
Then clean-up should have been performed
# ── Version resolution ────────────────────────────────────────────────────
Scenario Outline: Concrete version resolves directly
Given I have available versions "v1.0.0, v1.1.0, v2.0.0"
When I call resolve_version with "<version>"
Then the resolved version is "<expected>"
Examples:
| version | expected |
| v1.0.0 | v1.0.0 |
| v1.1.0 | v1.1.0 |
| v2.0.0 | v2.0.0 |
Scenario: latest alias resolves to newest version
Given I have available versions "v1.0.0, v1.1.0, v2.0.0, v1.0.1"
When I call resolve_version with "latest"
Then the resolved version is "v2.0.0"
Scenario: vx alias resolves same as latest
Given I have available versions "v1.0.0, v1.1.0, v2.0.0"
When I call resolve_version with "vx"
Then the resolved version is "v2.0.0"
Scenario: x alias resolves same as latest
Given I have available versions "v1.0.0, v3.0.0, v2.0.0"
When I call resolve_version with "x"
Then the resolved version is "v3.0.0"
Scenario: Major alias vX.x resolves to latest in major series
Given I have available versions "v1.0.0, v1.1.0, v1.5.0, v2.0.0"
When I call resolve_version with "v1.x"
Then the resolved version is "v1.5.0"
Scenario: Minor alias vX.Y.x resolves to latest patch in series
Given I have available versions "v1.2.0, v1.2.1, v1.2.9, v1.3.0"
When I call resolve_version with "v1.2.x"
Then the resolved version is "v1.2.9"
Scenario: Concrete version not in list raises error
Given I have available versions "v1.0.0, v2.0.0"
When I call resolve_version with "v3.0.0"
Then an InvalidPackageReferenceError should be raised with message containing "not found in available versions"
Scenario: Alias with no matching versions raises error
Given I have available versions "v2.0.0, v2.1.0"
When I call resolve_version with "v1.x"
Then an InvalidPackageReferenceError should be raised with message containing "No versions match alias"
Scenario: Resolving against empty version list raises error
Given I have no available versions
When I call resolve_version with "latest"
Then an InvalidPackageReferenceError should be raised with message containing "No available versions"
Scenario: Unrecognized version format raises error
Given I have available versions "v1.0.0"
When I call resolve_version with "garbage"
Then an InvalidPackageReferenceError should be raised with message containing "Unrecognized version format"
# ── Version classification helpers ─────────────────────────────────────────
Scenario: is_concrete_version identifies concrete versions
Given I check if "v1.2.3" is a concrete version
Then the is_concrete_version result should be true
Scenario: is_concrete_version rejects aliases
Given I check if "latest" is a concrete version
Then the is_concrete_version result should be false
Scenario: is_concrete_version rejects major alias
Given I check if "v2.x" is a concrete version
Then the is_concrete_version result should be false
Scenario: is_concrete_version rejects minor alias
Given I check if "v2.1.x" is a concrete version
Then the is_concrete_version result should be false
Scenario: is_version_alias identifies latest as alias
Given I check if "latest" is a version alias
Then the is_version_alias result should be true
Scenario: is_version_alias identifies concrete as not alias
Given I check if "v1.2.3" is a version alias
Then the is_version_alias result should be false
Scenario Outline: is_version_alias rejects invalid strings
Given I check if "<version>" is a version alias
Then the is_version_alias result should be false
Examples:
| version |
| garbage |
| 1.2.3 |
| v1.2 |
| hello |
Scenario Outline: is_version_alias recognizes valid aliases
Given I check if "<version>" is a version alias
Then the is_version_alias result should be true
Examples:
| version |
| latest |
| vx |
| x |
| v2.x |
| v2.1.x |
# ── ReferenceResolver constructor and properties ──────────────────────────
Scenario: ReferenceResolver can be constructed without a client
When I create a ReferenceResolver without a client
Then the resolver client is null
Scenario: ReferenceResolver preserves original_reference through parse
Given a package reference string "registry.cleverthis.com:acme/search@v1.2.3"
When I parse it with ReferenceResolver.parse
Then the original_reference should be "registry.cleverthis.com:acme/search@v1.2.3"