e17a6ddec7
CI / benchmark-regression (push) Failing after 0s
CI / benchmark-publish (push) Failing after 0s
CI / push-validation (push) Successful in 23s
CI / helm (push) Successful in 43s
CI / build (push) Successful in 3m49s
CI / lint (push) Successful in 3m56s
CI / quality (push) Successful in 4m24s
CI / typecheck (push) Successful in 4m53s
CI / security (push) Successful in 4m55s
CI / e2e_tests (push) Successful in 7m0s
CI / integration_tests (push) Successful in 7m44s
CI / unit_tests (push) Successful in 8m37s
CI / docker (push) Successful in 1m37s
CI / coverage (push) Successful in 15m4s
CI / status-check (push) Successful in 3s
CI / docker (pull_request) Successful in 1m38s
CI / coverage (pull_request) Successful in 14m58s
CI / typecheck (pull_request) Successful in 4m24s
CI / push-validation (pull_request) Successful in 23s
CI / integration_tests (pull_request) Successful in 11m54s
CI / build (pull_request) Successful in 3m35s
CI / lint (pull_request) Successful in 3m49s
CI / helm (pull_request) Successful in 29s
CI / quality (pull_request) Successful in 4m13s
CI / security (pull_request) Successful in 4m38s
CI / e2e_tests (pull_request) Successful in 6m54s
CI / unit_tests (pull_request) Successful in 8m56s
CI / status-check (pull_request) Successful in 3s
Introduce NamespacedProjectService in the application layer to encapsulate all NamespacedProject domain model construction. The agents project create CLI command previously imported NamespacedProject and parse_namespaced_name directly from cleveragents.domain.models.core.project, violating Architectural Invariant #3 (CLI layer must only call application services). Changes: - Add NamespacedProjectService with create_project(), get_project(), list_projects(), delete_project(), parse_project_name(), validate_project_name(), and project_to_dict() methods - Wire NamespacedProjectService into the DI container as namespaced_project_service provider - Refactor cli/commands/project.py to use NamespacedProjectService for all project operations (create, list, show, delete, link-resource, unlink-resource) - Add BDD feature file and step definitions for NamespacedProjectService (25 scenarios covering all service methods and edge cases) - Narrow exception handling in get_project() to catch only ProjectNotFoundError instead of bare Exception, preventing infrastructure errors from being masked as NotFoundError - Update CHANGELOG.md with both #7464 and #8232 entries - Update CONTRIBUTORS.md with PR #8297 contribution entry ISSUES CLOSED: #7464
142 lines
6.9 KiB
Gherkin
142 lines
6.9 KiB
Gherkin
Feature: NamespacedProjectService application service
|
|
As a developer maintaining the CleverAgents architecture
|
|
I want the CLI layer to interact with projects only through NamespacedProjectService
|
|
So that Architectural Invariant #3 (CLI → AppService → Domain) is enforced
|
|
|
|
Background:
|
|
Given a NamespacedProjectService with an in-memory database
|
|
|
|
# ── Name parsing ──────────────────────────────────────────────
|
|
|
|
Scenario: Parse a bare project name defaults to local namespace
|
|
When I parse the project name "my-project"
|
|
Then the NPS parsed namespace should be "local"
|
|
And the NPS parsed name should be "my-project"
|
|
And the NPS parsed server should be None
|
|
|
|
Scenario: Parse a namespaced project name
|
|
When I parse the project name "team/my-project"
|
|
Then the NPS parsed namespace should be "team"
|
|
And the NPS parsed name should be "my-project"
|
|
|
|
Scenario: Parse a server-qualified project name
|
|
When I parse the project name "dev:team/my-project"
|
|
Then the NPS parsed namespace should be "team"
|
|
And the NPS parsed name should be "my-project"
|
|
And the NPS parsed server should be "dev"
|
|
|
|
Scenario: Parse an invalid project name raises ValueError
|
|
When I parse the invalid project name "123bad"
|
|
Then the NPS should raise a ValueError
|
|
|
|
Scenario: Parse a reserved namespace raises ValueError
|
|
When I parse the invalid project name "system/bad"
|
|
Then the NPS should raise a ValueError
|
|
|
|
Scenario: Parse a provider namespace raises ValueError
|
|
When I parse the invalid project name "openai/bad"
|
|
Then the NPS should raise a ValueError
|
|
|
|
# ── Validate project name ─────────────────────────────────────
|
|
|
|
Scenario: Validate a valid project name succeeds
|
|
When I validate the project name "valid-name"
|
|
Then the validation should succeed
|
|
|
|
Scenario: Validate an invalid project name raises ValueError
|
|
When I validate the invalid project name "9invalid"
|
|
Then the NPS should raise a ValueError
|
|
|
|
# ── Create project ────────────────────────────────────────────
|
|
|
|
Scenario: Create a project with bare name
|
|
When I create a project named "my-app" via the service
|
|
Then the service should return a project with namespaced name "local/my-app"
|
|
And the project should be persisted in the database
|
|
|
|
Scenario: Create a project with explicit namespace
|
|
When I create a project named "team/my-app" via the service
|
|
Then the service should return a project with namespaced name "team/my-app"
|
|
And the project should be persisted in the database
|
|
|
|
Scenario: Create a project with description
|
|
When I create a project named "my-app" with description "A test project" via the service
|
|
Then the service should return a project with namespaced name "local/my-app"
|
|
And the NPS project description should be "A test project"
|
|
|
|
Scenario: Create a project with invalid name raises ValueError
|
|
When I attempt to create a project named "123bad" via the service
|
|
Then the NPS should raise a ValueError
|
|
|
|
Scenario: Create a duplicate project raises DatabaseError
|
|
Given a project "local/existing-app" already exists in the service
|
|
When I attempt to create a duplicate project named "existing-app" via the service
|
|
Then a database error should be raised
|
|
|
|
# ── Get project ───────────────────────────────────────────────
|
|
|
|
Scenario: Get an existing project by namespaced name
|
|
Given a project "local/get-test" already exists in the service
|
|
When I get the project "local/get-test" via the service
|
|
Then the service should return a project with namespaced name "local/get-test"
|
|
|
|
Scenario: Get a nonexistent project raises NotFoundError
|
|
When I attempt to get the project "local/nonexistent" via the service
|
|
Then a NotFoundError should be raised
|
|
|
|
# ── List projects ─────────────────────────────────────────────
|
|
|
|
Scenario: List all projects returns all created projects
|
|
Given a project "local/proj-a" already exists in the service
|
|
And a project "local/proj-b" already exists in the service
|
|
When I list all projects via the service
|
|
Then the service project list should contain "local/proj-a"
|
|
And the service project list should contain "local/proj-b"
|
|
|
|
Scenario: List projects with namespace filter
|
|
Given a project "local/proj-x" already exists in the service
|
|
And a project "team/proj-y" already exists in the service
|
|
When I list projects with namespace "team" via the service
|
|
Then the service project list should contain "team/proj-y"
|
|
And the service project list should not contain "local/proj-x"
|
|
|
|
Scenario: List projects when empty returns empty list
|
|
When I list all projects via the service
|
|
Then the service project list should be empty
|
|
|
|
# ── Delete project ────────────────────────────────────────────
|
|
|
|
Scenario: Delete an existing project
|
|
Given a project "local/del-test" already exists in the service
|
|
When I delete the project "local/del-test" via the service
|
|
Then the delete should return True
|
|
And the project "local/del-test" should not exist in the service
|
|
|
|
Scenario: Delete a nonexistent project returns False
|
|
When I delete the project "local/never-existed" via the service
|
|
Then the delete should return False
|
|
|
|
# ── project_to_dict ───────────────────────────────────────────
|
|
|
|
Scenario: project_to_dict returns spec-aligned keys
|
|
Given a project "local/dict-test" already exists in the service
|
|
When I convert the project "local/dict-test" to a dict via the service
|
|
Then the dict should have key "namespaced_name"
|
|
And the dict should have key "namespace"
|
|
And the dict should have key "name"
|
|
And the dict should have key "description"
|
|
And the dict should have key "linked_resources"
|
|
And the dict should have key "created_at"
|
|
And the dict should have key "updated_at"
|
|
|
|
Scenario: project_to_dict namespaced_name matches project
|
|
Given a project "team/dict-ns" already exists in the service
|
|
When I convert the project "team/dict-ns" to a dict via the service
|
|
Then the dict value for "namespaced_name" should be "team/dict-ns"
|
|
|
|
# ── CLI architectural invariant ───────────────────────────────
|
|
|
|
Scenario: CLI project create command does not import domain models directly
|
|
When I inspect the project CLI create command source
|
|
Then it should not contain a direct import of "cleveragents.domain.models.core.project"
|