Files
cleveragents-core/features/namespaced_project_service.feature
T
HAL9000 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
fix(arch): route CLI project create through NamespacedProjectService
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
2026-04-21 03:38:05 +00:00

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"