forked from cleveragents/cleveragents-core
362 lines
12 KiB
Python
362 lines
12 KiB
Python
"""Step definitions for Project domain model extension tests (B1.1)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
from pydantic import ValidationError
|
|
|
|
from cleveragents.domain.models.core.project import Project
|
|
from cleveragents.domain.models.core.resource import (
|
|
Resource,
|
|
ResourceType,
|
|
)
|
|
|
|
VALID_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
VALID_ULID_2 = "01ARZ3NDEKTSV4RRFFQ69G5FAW"
|
|
VALID_ULID_3 = "01ARZ3NDEKTSV4RRFFQ69G5FAX"
|
|
|
|
|
|
def _make_resource(
|
|
name: str,
|
|
is_remote: bool = False,
|
|
resource_id: str = VALID_ULID_2,
|
|
) -> Resource:
|
|
"""Helper to create a test Resource."""
|
|
return Resource(
|
|
resource_id=resource_id,
|
|
name=name,
|
|
type=ResourceType.GIT_REPOSITORY,
|
|
location="/tmp/repo" if not is_remote else "https://github.com/test",
|
|
is_remote=is_remote,
|
|
)
|
|
|
|
|
|
# Project creation steps
|
|
|
|
|
|
@given('a Project with project_id "{pid}" name "{name}" namespace "{namespace}"')
|
|
def step_create_project_with_namespace(
|
|
context: Context, pid: str, name: str, namespace: str
|
|
) -> None:
|
|
"""Create a Project with explicit namespace."""
|
|
context.project = Project(
|
|
project_id=pid,
|
|
name=name,
|
|
namespace=namespace,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
|
|
|
|
@given('a Project with project_id "{pid}" and name "{name}"')
|
|
def step_create_project_minimal(context: Context, pid: str, name: str) -> None:
|
|
"""Create a Project with minimal fields."""
|
|
context.project = Project(
|
|
project_id=pid,
|
|
name=name,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
|
|
|
|
@given('a Project with project_id "{pid}" name "{name}" and description "{desc}"')
|
|
def step_create_project_with_description(
|
|
context: Context, pid: str, name: str, desc: str
|
|
) -> None:
|
|
"""Create a Project with description."""
|
|
context.project = Project(
|
|
project_id=pid,
|
|
name=name,
|
|
description=desc,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
|
|
|
|
@given('a Project with project_id "{pid}" name "{name}" and tags "{tags_str}"')
|
|
def step_create_project_with_tags(
|
|
context: Context, pid: str, name: str, tags_str: str
|
|
) -> None:
|
|
"""Create a Project with tags."""
|
|
tags = [t.strip() for t in tags_str.split(",")]
|
|
context.project = Project(
|
|
project_id=pid,
|
|
name=name,
|
|
tags=tags,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
|
|
|
|
@given('a Project with project_id "{pid}" name "{name}" and all remote resources')
|
|
def step_create_project_all_remote(context: Context, pid: str, name: str) -> None:
|
|
"""Create a Project where all resources are remote."""
|
|
resources = [
|
|
_make_resource("api-svc", is_remote=True, resource_id=VALID_ULID_2),
|
|
_make_resource("cloud-db", is_remote=True, resource_id=VALID_ULID_3),
|
|
]
|
|
context.project = Project(
|
|
project_id=pid,
|
|
name=name,
|
|
resources=resources,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
|
|
|
|
@given('a Project with project_id "{pid}" name "{name}" and mixed resources')
|
|
def step_create_project_mixed_resources(context: Context, pid: str, name: str) -> None:
|
|
"""Create a Project with both local and remote resources."""
|
|
resources = [
|
|
_make_resource("local-repo", is_remote=False, resource_id=VALID_ULID_2),
|
|
_make_resource("api-svc", is_remote=True, resource_id=VALID_ULID_3),
|
|
]
|
|
context.project = Project(
|
|
project_id=pid,
|
|
name=name,
|
|
resources=resources,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
|
|
|
|
@given('a Project with project_id "{pid}" name "{name}" and a resource named "{rname}"')
|
|
def step_create_project_with_resource(
|
|
context: Context, pid: str, name: str, rname: str
|
|
) -> None:
|
|
"""Create a Project with one resource."""
|
|
resource = _make_resource(rname)
|
|
context.project = Project(
|
|
project_id=pid,
|
|
name=name,
|
|
resources=[resource],
|
|
path=Path("/tmp/test"),
|
|
)
|
|
|
|
|
|
# Namespace validation steps
|
|
|
|
|
|
@when('I try to create a Project with namespace "{namespace}"')
|
|
def step_try_create_project_bad_namespace(context: Context, namespace: str) -> None:
|
|
"""Attempt to create a Project with an invalid namespace."""
|
|
context.error = None
|
|
try:
|
|
context.project = Project(
|
|
project_id=VALID_ULID,
|
|
name="test-project",
|
|
namespace=namespace,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
except (ValidationError, ValueError) as e:
|
|
context.error = e
|
|
|
|
|
|
@then("a namespace validation error should be raised")
|
|
def step_check_namespace_error(context: Context) -> None:
|
|
"""Verify a namespace validation error was raised."""
|
|
assert context.error is not None, (
|
|
"Expected a namespace validation error but none was raised"
|
|
)
|
|
|
|
|
|
# Resource manipulation steps
|
|
|
|
|
|
@when('I add a resource named "{rname}" to the project')
|
|
def step_add_resource(context: Context, rname: str) -> None:
|
|
"""Add a resource to the project."""
|
|
resource = _make_resource(rname)
|
|
context.project = context.project.add_resource(resource)
|
|
|
|
|
|
@when('I remove the resource named "{rname}" from the project')
|
|
def step_remove_resource(context: Context, rname: str) -> None:
|
|
"""Remove a resource from the project."""
|
|
context.project = context.project.remove_resource(rname)
|
|
|
|
|
|
@when('I get the resource named "{rname}" from the project')
|
|
def step_get_resource(context: Context, rname: str) -> None:
|
|
"""Get a resource by name from the project."""
|
|
context.retrieved_resource = context.project.get_resource(rname)
|
|
|
|
|
|
# Project field assertions
|
|
|
|
|
|
@then('the project project_id should be "{expected}"')
|
|
def step_check_project_id(context: Context, expected: str) -> None:
|
|
"""Verify project_id."""
|
|
assert context.project.project_id == expected
|
|
|
|
|
|
# Note: "the project name should be" step is defined in service_steps.py
|
|
|
|
|
|
@then('the project namespace should be "{expected}"')
|
|
def step_check_project_namespace(context: Context, expected: str) -> None:
|
|
"""Verify project namespace."""
|
|
assert context.project.namespace == expected
|
|
|
|
|
|
@then("the project description should be none")
|
|
def step_check_project_description_none(context: Context) -> None:
|
|
"""Verify description is None."""
|
|
assert context.project.description is None
|
|
|
|
|
|
@then('the project description should be "{expected}"')
|
|
def step_check_project_description(context: Context, expected: str) -> None:
|
|
"""Verify description value."""
|
|
assert context.project.description == expected
|
|
|
|
|
|
@then("the project tags should be empty")
|
|
def step_check_project_tags_empty(context: Context) -> None:
|
|
"""Verify tags is empty list."""
|
|
assert context.project.tags == []
|
|
|
|
|
|
@then('the project tags should contain "{tag}"')
|
|
def step_check_project_tag(context: Context, tag: str) -> None:
|
|
"""Verify tags contains expected value."""
|
|
assert tag in context.project.tags
|
|
|
|
|
|
@then("the project resources should be empty")
|
|
def step_check_project_resources_empty(context: Context) -> None:
|
|
"""Verify resources is empty list."""
|
|
assert context.project.resources == []
|
|
|
|
|
|
@then("the project validation_config should be none")
|
|
def step_check_project_validation_config_none(context: Context) -> None:
|
|
"""Verify validation_config is None."""
|
|
assert context.project.validation_config is None
|
|
|
|
|
|
@then("the project context_config should not be none")
|
|
def step_check_project_context_config_not_none(context: Context) -> None:
|
|
"""Verify context_config is set."""
|
|
assert context.project.context_config is not None
|
|
|
|
|
|
@then("the project created_at should be set")
|
|
def step_check_project_created_at(context: Context) -> None:
|
|
"""Verify created_at is set."""
|
|
assert context.project.created_at is not None
|
|
|
|
|
|
@then("the project updated_at should be set")
|
|
def step_check_project_updated_at(context: Context) -> None:
|
|
"""Verify updated_at is set."""
|
|
assert context.project.updated_at is not None
|
|
|
|
|
|
@then("the project is_remote should be false")
|
|
def step_check_project_not_remote(context: Context) -> None:
|
|
"""Verify project is not remote."""
|
|
assert context.project.is_remote is False
|
|
|
|
|
|
@then("the project is_remote should be true")
|
|
def step_check_project_is_remote(context: Context) -> None:
|
|
"""Verify project is remote."""
|
|
assert context.project.is_remote is True
|
|
|
|
|
|
@then('the project namespaced_name should be "{expected}"')
|
|
def step_check_project_namespaced_name(context: Context, expected: str) -> None:
|
|
"""Verify namespaced_name property."""
|
|
assert context.project.namespaced_name == expected
|
|
|
|
|
|
@then("the project should have {count:d} resource")
|
|
def step_check_project_resource_count(context: Context, count: int) -> None:
|
|
"""Verify resource count."""
|
|
assert len(context.project.resources) == count
|
|
|
|
|
|
@then('the project should have a resource named "{rname}"')
|
|
def step_check_project_has_resource(context: Context, rname: str) -> None:
|
|
"""Verify project has a resource with the given name."""
|
|
found = context.project.get_resource(rname)
|
|
assert found is not None, f"Expected resource '{rname}' not found"
|
|
|
|
|
|
@then('the retrieved resource name should be "{expected}"')
|
|
def step_check_retrieved_resource_name(context: Context, expected: str) -> None:
|
|
"""Verify retrieved resource name."""
|
|
assert context.retrieved_resource is not None
|
|
assert context.retrieved_resource.name == expected
|
|
|
|
|
|
@then("the retrieved resource should be none")
|
|
def step_check_retrieved_resource_none(context: Context) -> None:
|
|
"""Verify retrieved resource is None."""
|
|
assert context.retrieved_resource is None
|
|
|
|
|
|
# parse_namespaced_name steps
|
|
|
|
|
|
@when('I parse the project namespaced name "{namespaced}"')
|
|
def step_parse_project_namespaced_name(context: Context, namespaced: str) -> None:
|
|
"""Parse a namespaced name string via Project.parse_namespaced_name."""
|
|
namespace, name = Project.parse_namespaced_name(namespaced)
|
|
context.parsed_project_namespace = namespace
|
|
context.parsed_project_name = name
|
|
|
|
|
|
@when('I try to parse the project namespaced name "{namespaced}"')
|
|
def step_try_parse_project_namespaced_name(context: Context, namespaced: str) -> None:
|
|
"""Attempt to parse an invalid namespaced name string."""
|
|
context.error = None
|
|
try:
|
|
Project.parse_namespaced_name(namespaced)
|
|
except ValueError as e:
|
|
context.error = e
|
|
|
|
|
|
@then('the parsed project namespace should be "{expected}"')
|
|
def step_check_parsed_project_namespace(context: Context, expected: str) -> None:
|
|
"""Verify parsed project namespace."""
|
|
assert context.parsed_project_namespace == expected
|
|
|
|
|
|
@then('the parsed project name should be "{expected}"')
|
|
def step_check_parsed_project_name(context: Context, expected: str) -> None:
|
|
"""Verify parsed project name."""
|
|
assert context.parsed_project_name == expected
|
|
|
|
|
|
@then("a project namespaced name parse error should be raised")
|
|
def step_check_project_namespaced_name_error(context: Context) -> None:
|
|
"""Verify a namespaced name parse error was raised."""
|
|
assert context.error is not None, (
|
|
"Expected a ValueError for invalid namespaced name but none was raised"
|
|
)
|
|
|
|
|
|
# Name validation steps
|
|
|
|
|
|
@when('I try to create a Project with invalid name "{name}"')
|
|
def step_try_create_project_bad_name(context: Context, name: str) -> None:
|
|
"""Attempt to create a Project with an invalid name."""
|
|
context.error = None
|
|
try:
|
|
context.project = Project(
|
|
project_id=VALID_ULID,
|
|
name=name,
|
|
path=Path("/tmp/test"),
|
|
)
|
|
except (ValidationError, ValueError) as e:
|
|
context.error = e
|
|
|
|
|
|
@then("a name validation error should be raised")
|
|
def step_check_name_error(context: Context) -> None:
|
|
"""Verify a name validation error was raised."""
|
|
assert context.error is not None, (
|
|
"Expected a name validation error but none was raised"
|
|
)
|