fix: avoid literal {{WORKSPACE_NAME}} directories in built images
When a manifest mount target uses ~/{{WORKSPACE_NAME}}, the Dockerfile was
building a literal directory named {{WORKSPACE_NAME}} into the image and
creating a broken /workspace symlink. The runtime mount then created the
correct repo-named folder alongside the placeholder folder.
- Only create static mount target directories in the Dockerfile; skip any
target containing {{WORKSPACE_NAME}}
- Only create the /workspace compatibility symlink at image-build time when
the workspace name is known; otherwise let the entrypoint create it from
the WORKSPACE_NAME environment variable
- Update unit tests to cover both build-time workspace names and runtime
placeholders
Quality gates:
- pytest tests/unit: 211 passed
- ruff: clean on changed files
- mypy: clean on changed files
This commit is contained in:
+1
-2
@@ -16,7 +16,7 @@ dir: .
|
||||
Trust boundary: index routes, map orients, source decides.
|
||||
|
||||
## role
|
||||
A self-hosted web-based project management and git repository platform with OAuth2 SSO authentication, containerized for Docker deployment.
|
||||
Infrastructure and deployment configuration package for a self-hosted project management platform with containerized services, SSO integration, and reverse proxy support.
|
||||
## parent
|
||||
-
|
||||
## children
|
||||
@@ -69,7 +69,6 @@ A self-hosted web-based project management and git repository platform with OAut
|
||||
- CHANGELOG.md
|
||||
- Makefile
|
||||
- README.md
|
||||
- context.md
|
||||
- docker-compose.traefik.yml
|
||||
- docker-compose.yml
|
||||
- progress.md
|
||||
|
||||
+3
-4
@@ -18,7 +18,7 @@ index: ./.pi-map.index.md
|
||||
Trust boundary: index routes, map orients, source decides.
|
||||
|
||||
## role
|
||||
A self-hosted web-based project management and git repository platform with OAuth2 SSO authentication, containerized for Docker deployment.
|
||||
Infrastructure and deployment configuration package for a self-hosted project management platform with containerized services, SSO integration, and reverse proxy support.
|
||||
## files
|
||||
- .env.example | Provides a template of environment variables for configuring a Headquarter application with PostgreSQL, Redis, Authentik SSO, and Docker/Traefik deployment
|
||||
- .gitignore | Specifies files and directories for Git to ignore across a multi-language project with Python, Node, and custom tooling | dep: Git
|
||||
@@ -26,15 +26,14 @@ A self-hosted web-based project management and git repository platform with OAut
|
||||
- CHANGELOG.md | Documents version history and notable changes for a Git-based project management web application
|
||||
- Makefile | Provides standard development commands for containerized web application lifecycle management via Docker Compose | dep: docker compose, alembic, pytest, ruff, mypy, playwright, npm, postgres, redis
|
||||
- README.md | A self-hosted platform for managing projects, git repositories, and development tools with OAuth2 authentication. | dep: FastAPI, SQLAlchemy, Pydantic, Alembic, python-jose, React, TypeScript, Vite, React Router, Docker, PostgreSQL, Traefik, Authentik, Git
|
||||
- context.md | Documents a bug investigation where crashed Docker containers incorrectly display "running" status on the dashboard due to optimistic status writes, auto-restart policies, and health monitor logic that preserves running status for restarting containers. | dep: docker, docker-compose, FastAPI/SQLAlchemy, SSE event bus, React/TypeScript frontend
|
||||
- docker-compose.traefik.yml | Deploys a multi-service web application (frontend, API, PostgreSQL, Redis) behind an existing Traefik reverse proxy with TLS termination and environment-configurable domains. | dep: docker, traefik, postgres, redis, authentik, docker-compose
|
||||
- docker-compose.yml | Defines a multi-service Docker Compose stack with PostgreSQL, Redis, API backend, and web frontend services for a "headquarter" application | dep: Docker, PostgreSQL, Redis, Vite, asyncpg, nginx
|
||||
- progress.md | Tracks completed and remaining tasks for a backend-frontend code refactoring project organized in 7 phases
|
||||
- swap-pane | Empty file with no functionality
|
||||
## arch
|
||||
Full-stack containerized architecture with separate frontend/API services, PostgreSQL/Redis data layer, Traefik reverse proxy with TLS, environment-driven configuration, and health monitoring with auto-restart policies.
|
||||
Docker Compose-based microservices architecture with environment-driven configuration, separating PostgreSQL persistence, Redis caching, API backend, and web frontend behind Traefik reverse proxy with TLS termination.
|
||||
## tags
|
||||
docker, redis, git, compose, application, postgresql, traefik, project
|
||||
docker, redis, git, application, postgresql, compose, traefik, project
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
dir: apps
|
||||
|
||||
## role
|
||||
Contains the deployable application entry points and executable configurations for the project.
|
||||
Contains the main deployable application modules that compose the complete system.
|
||||
## parent
|
||||
index: ./.pi-map.index.md
|
||||
map: ./.pi-map.md
|
||||
|
||||
+2
-2
@@ -4,10 +4,10 @@ dir: apps
|
||||
index: apps/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Contains the deployable application entry points and executable configurations for the project.
|
||||
Contains the main deployable application modules that compose the complete system.
|
||||
## files
|
||||
## arch
|
||||
Modular application structure where each subdirectory represents a separate deployable unit or service, following a monorepo or multi-app pattern.
|
||||
Monolithic or modular monolith architecture with domain-separated application boundaries.
|
||||
## tags
|
||||
-
|
||||
## symbols
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
dir: apps/api
|
||||
|
||||
## role
|
||||
FastAPI backend API that manages projects, git repositories, and development tools via Docker instances with PostgreSQL persistence.
|
||||
FastAPI backend API that manages projects, git repositories, and development tools through Docker-based instances.
|
||||
## parent
|
||||
index: apps/.pi-map.index.md
|
||||
map: apps/.pi-map.md
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ dir: apps/api
|
||||
index: apps/api/.pi-map.index.md
|
||||
|
||||
## role
|
||||
FastAPI backend API that manages projects, git repositories, and development tools via Docker instances with PostgreSQL persistence.
|
||||
FastAPI backend API that manages projects, git repositories, and development tools through Docker-based instances.
|
||||
## files
|
||||
- .dockerignore | Specifies files and directories to exclude from Docker build context to reduce image size and avoid copying unnecessary files into containers. | dep: Docker
|
||||
- Dockerfile | Multi-stage Docker build for a Python application with Docker socket access, Cloudflare tunneling, and database dependency waiting | dep: python:3.11-slim, gcc, libpq-dev, docker-ce-cli, docker-compose-plugin, cloudflared, uvicorn, pyproject.toml dependencies
|
||||
@@ -14,7 +14,7 @@ FastAPI backend API that manages projects, git repositories, and development too
|
||||
- uv.lock | Lock file for the uv Python package manager that pins exact dependency versions and their artifact hashes for reproducible installations | dep: uv, Python 3.11+, aiosqlite, alembic, annotated-doc, annotated-types, anyio, ast-serialize, asyncpg, and many other PyPI packages
|
||||
- wait-for-db.sh | Wait for a PostgreSQL database to become available before executing a command, with configurable retry logic. | dep: nc (netcat), sh (POSIX shell), sleep
|
||||
## arch
|
||||
Async Python/FastAPI service with multi-stage Docker containerization, Alembic database migrations, uv package management, and Docker socket access for container orchestration.
|
||||
Async Python/FastAPI service with PostgreSQL database, Alembic migrations, multi-stage Docker deployment with Cloudflare tunneling, and uv package management.
|
||||
## tags
|
||||
docker, alembic, python, database, fastapi, postgresql, asyncpg, uvicorn
|
||||
## symbols
|
||||
|
||||
@@ -221,27 +221,34 @@ def compile_dockerfile(manifest: dict) -> str:
|
||||
lines.append(f"RUN chown -R {name}:{name} {home_dir}")
|
||||
lines.append("")
|
||||
|
||||
# Create mount target directories and /workspace compatibility symlink.
|
||||
# The symlink target includes the workspace/repo name so legacy scripts
|
||||
# that cd into /workspace still land on the right project.
|
||||
# Create mount target directories that do NOT depend on runtime variables.
|
||||
# Targets containing {{WORKSPACE_NAME}} will be created at container
|
||||
# startup by the entrypoint, once the actual workspace/repo name is known.
|
||||
mounts = manifest.get("mounts", [])
|
||||
if mounts:
|
||||
dirs = [mount["target"] for mount in mounts]
|
||||
dir_str = " ".join(dirs)
|
||||
static_dirs = [
|
||||
mount["target"] for mount in mounts
|
||||
if "{{WORKSPACE_NAME}}" not in mount.get("target", "")
|
||||
]
|
||||
if static_dirs:
|
||||
dir_str = " ".join(static_dirs)
|
||||
lines.append(f"RUN mkdir -p {dir_str}")
|
||||
if user:
|
||||
lines.append(f"RUN chown -R {user['name']}:{user['name']} {dir_str}")
|
||||
lines.append("")
|
||||
|
||||
# Create the /workspace compatibility symlink only when the workspace name
|
||||
# is known at image-build time. Otherwise the entrypoint creates it at
|
||||
# runtime from the WORKSPACE_NAME environment variable.
|
||||
workspace_target = f"{home_dir}/{workspace_name}"
|
||||
lines.append(f"RUN mkdir -p {workspace_target}")
|
||||
if user:
|
||||
lines.append(
|
||||
f"RUN ln -sfn {workspace_target} /workspace && chown -R {user['name']}:{user['name']} {home_dir}"
|
||||
)
|
||||
else:
|
||||
lines.append(f"RUN ln -sfn {workspace_target} /workspace")
|
||||
lines.append("")
|
||||
if "{{WORKSPACE_NAME}}" not in workspace_name:
|
||||
lines.append(f"RUN mkdir -p {workspace_target}")
|
||||
if user:
|
||||
lines.append(
|
||||
f"RUN ln -sfn {workspace_target} /workspace && chown -R {user['name']}:{user['name']} {home_dir}"
|
||||
)
|
||||
else:
|
||||
lines.append(f"RUN ln -sfn {workspace_target} /workspace")
|
||||
lines.append("")
|
||||
|
||||
# Entrypoint for startup scripts
|
||||
startup_scripts = manifest.get("scripts", {}).get("startup", [])
|
||||
|
||||
@@ -292,9 +292,8 @@ class HealthMonitor:
|
||||
return
|
||||
# Skip "not_found" errors for containers that were never running
|
||||
# (e.g. still starting, or intentionally stopped/deleted).
|
||||
if (
|
||||
snapshot.container_status == "not_found"
|
||||
and (previous is None or previous.container_status != "running")
|
||||
if snapshot.container_status == "not_found" and (
|
||||
previous is None or previous.container_status != "running"
|
||||
):
|
||||
return
|
||||
category = "instance"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
dir: apps/api/tests
|
||||
|
||||
## role
|
||||
Provides shared testing infrastructure and fixtures for the FastAPI API application tests.
|
||||
Provides shared test infrastructure and fixtures for the API application's test suite.
|
||||
## parent
|
||||
index: apps/api/.pi-map.index.md
|
||||
map: apps/api/.pi-map.md
|
||||
|
||||
@@ -4,11 +4,11 @@ dir: apps/api/tests
|
||||
index: apps/api/tests/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides shared testing infrastructure and fixtures for the FastAPI API application tests.
|
||||
Provides shared test infrastructure and fixtures for the API application's test suite.
|
||||
## files
|
||||
- conftest.py | Provides shared pytest fixtures for testing a FastAPI application with async SQLite database, authenticated clients, and test data setup. | exp: func:test_client() → Generator[TestClient, None, None], call:create_async_engine, call:engine.begin, call:conn.run_sync, call:asyncio.run, call:init_db, call:async_sessionmaker, call:patch, call:TestClient, call:app.dependency_overrides.pop, call:engine.dispose, func:init_db(), call:engine.begin, call:conn.run_sync, func:override_get_db_session() → AsyncGenerator[AsyncSession, None], call:async_sessionmaker, func:db_session(test_client) → AsyncGenerator[AsyncSession, None], call:app.dependency_overrides.get, call:override_fn, call:gen.asend, call:gen.aclose, call:create_async_engine, call:engine.begin, call:conn.run_sync, call:async_sessionmaker, call:engine.dispose, func:authenticated_client(test_client) → Generator[TestClient, None, None], call:str, call:uuid.uuid4, call:Settings, call:app.dependency_overrides.get, call:override_fn, call:gen.asend, call:User, call:uuid.UUID, call:session.add, call:session.commit, call:gen.aclose, call:asyncio.run, call:create_test_user, call:create_session_cookie, call:test_client.cookies.set, func:create_test_user(), call:app.dependency_overrides.get, call:override_fn, call:gen.asend, call:User, call:uuid.UUID, call:session.add, call:session.commit, call:gen.aclose, func:test_project_and_repo(authenticated_client) → tuple[str, str], call:uuid.uuid4, call:Settings, call:authenticated_client.cookies.get, call:decode_session_cookie, call:uuid.UUID, call:asyncio.run, call:get_user_id, call:app.dependency_overrides.get, call:override_fn, call:gen.asend, call:Project, call:session.add, call:GitRepository, call:session.commit, call:gen.aclose, call:create_project_and_repo, call:str, raise:RuntimeError, func:get_user_id(), call:Settings, call:authenticated_client.cookies.get, call:decode_session_cookie, call:uuid.UUID, func:create_project_and_repo(), call:app.dependency_overrides.get, call:override_fn, call:gen.asend, call:Project, call:session.add, call:GitRepository, call:session.commit, call:gen.aclose, func:admin_client(test_client) → Generator[TestClient, None, None], call:str, call:uuid.uuid4, call:Settings, call:app.dependency_overrides.get, call:override_fn, call:gen.asend, call:User, call:uuid.UUID, call:session.add, call:session.commit, call:gen.aclose, call:asyncio.run, call:create_admin_user, call:create_session_cookie, call:test_client.cookies.set, func:create_admin_user(), call:app.dependency_overrides.get, call:override_fn, call:gen.asend, call:User, call:uuid.UUID, call:session.add, call:session.commit, call:gen.aclose | dep: asyncio, os, typing, unittest.mock, pytest, pytest_asyncio, fastapi.testclient, sqlalchemy.ext.asyncio, src.config, src.models.base, src.main, src.auth.dependencies, uuid, src.auth.session, src.models.user.user, src.models.project.project, src.models.project.git_repository, fastapi, sqlalchemy, aiosqlite, src.models, src.auth
|
||||
## arch
|
||||
Pytest fixture-based architecture using dependency injection, async SQLite test database with transaction isolation, and authenticated client factories for integration testing.
|
||||
Pytest plugin architecture with async fixture dependency injection, using factory patterns for database sessions and authenticated HTTP clients, and transactional test isolation with async SQLite.
|
||||
## tags
|
||||
call:app.dependency, call:create, overrides.get, call:override, fn, call:gen.asend, call:gen.aclose, user
|
||||
## symbols
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
dir: apps/api/tests/unit
|
||||
|
||||
## role
|
||||
Unit test suite for the API application covering core services, utilities, and infrastructure components.
|
||||
Comprehensive unit test suite for the API backend, covering core services, utilities, configuration, and infrastructure components.
|
||||
## parent
|
||||
index: apps/api/tests/.pi-map.index.md
|
||||
map: apps/api/tests/.pi-map.md
|
||||
|
||||
@@ -4,7 +4,7 @@ dir: apps/api/tests/unit
|
||||
index: apps/api/tests/unit/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Unit test suite for the API application covering core services, utilities, and infrastructure components.
|
||||
Comprehensive unit test suite for the API backend, covering core services, utilities, configuration, and infrastructure components.
|
||||
## files
|
||||
- __init__.py | Empty file with no functionality
|
||||
- test_alembic_migrations.py | Unit tests that verify Alembic database migrations are importable, have correct revision identifiers, and declare expected dependencies without requiring a live database. | exp: func:test_home_directory_migration_imports_and_rewrites() → None, call:Path, call:migration_path.exists, call:importlib.util.spec_from_file_location, call:importlib.util.module_from_spec, call:spec.loader.exec_module, call:callable, func:test_merge_migration_resolves_heads() → None, call:Path, call:migration_path.exists, call:importlib.util.spec_from_file_location, call:importlib.util.module_from_spec, call:spec.loader.exec_module, call:callable | dep: importlib.util, pathlib, pytest, importlib
|
||||
@@ -21,7 +21,7 @@ Unit test suite for the API application covering core services, utilities, and i
|
||||
- test_home_path_expansion.py | Unit tests for home directory path expansion (~ and $HOME) in container paths and manifest home directory resolution. | exp: class:TestExpandContainerPath, method:test_tilde_slash_expands(self) → None, call:expand_container_path, method:test_tilde_alone_expands(self) → None, call:expand_container_path, method:test_dollar_home_slash_expands(self) → None, call:expand_container_path, method:test_dollar_home_alone_expands(self) → None, call:expand_container_path, method:test_absolute_path_unchanged(self) → None, call:expand_container_path, method:test_relative_path_unchanged(self) → None, call:expand_container_path, method:test_tilde_in_middle_unchanged(self) → None, call:expand_container_path, method:test_dollar_home_in_middle_unchanged(self) → None, call:expand_container_path, method:test_root_home(self) → None, call:expand_container_path, class:TestGetManifestHomeDir, method:test_with_user_block(self) → None, call:get_manifest_home_dir, method:test_without_user_block(self) → None, call:get_manifest_home_dir, method:test_with_empty_user_name(self) → None, call:get_manifest_home_dir, method:test_with_none_user_name(self) → None, call:get_manifest_home_dir | dep: pytest, src.services.config.config_profile_resolver, src.services.build.manifest_compiler
|
||||
- test_instance_service.py | Unit tests for home directory expansion in docker-compose file modification via instance_service | exp: class:TestModifyComposeFile, method:test_extra_volumes_expand_home_dir(self, tmp_path), call:compose_path.write_text, call:modify_compose_file, call:str, call:compose_path.read_text, method:test_working_directory_expands_home_dir(self, tmp_path), call:compose_path.write_text, call:modify_compose_file, call:str, call:compose_path.read_text | dep: pytest, src.services.tool.instance_service
|
||||
- test_lifecycle_hooks.py | Unit tests for lifecycle hook helper functions that derive notification titles and determine whether events should trigger notifications. | exp: class:TestDeriveTitle, method:test_known_event_types(self) → None, call:_derive_title, method:test_unknown_event_type(self) → None, call:_derive_title, class:TestShouldNotify, method:test_error_events_are_notified(self) → None, call:_should_notify, method:test_health_changed_running_is_notified(self) → None, call:_should_notify, method:test_created_started_stopped_restarted_deleted_filtered(self) → None, call:_should_notify, method:test_health_changed_non_running_filtered(self) → None, call:_should_notify | dep: pytest, src.services.instance.lifecycle_hooks
|
||||
- test_manifest_compiler.py | Unit tests for a manifest compiler that generates Dockerfiles, docker-compose files, and entrypoint scripts based on manifest configurations. | exp: class:TestGetManifestHomeDir, method:test_home_directory_in_manifest_wins(self) → None, call:get_manifest_home_dir, method:test_user_name_derives_home(self) → None, call:get_manifest_home_dir, method:test_root_fallback(self) → None, call:get_manifest_home_dir, method:test_empty_home_directory_falls_back(self) → None, call:get_manifest_home_dir, class:TestCompileDockerfileHomeDirectory, method:test_env_home_and_workdir_use_home_directory(self) → None, call:compile_dockerfile, method:test_workspace_symlink_created(self) → None, call:compile_dockerfile, method:test_runtime_working_dir_overrides_home_workdir(self) → None, call:compile_dockerfile, method:test_working_dir_expands_tilde(self) → None, call:compile_dockerfile, class:TestCompileComposeHomeDirectory, method:test_default_repo_mount_synthesized(self) → None, call:compile_compose, method:test_explicit_repo_mount_preserved(self) → None, call:compile_compose, method:test_workspace_name_substituted_in_mount_target(self) → None, call:compile_compose, method:test_working_dir_expands_home(self) → None, call:compile_compose, class:TestCompileEntrypoint, method:test_entrypoint_creates_home_and_workspace(self) → None, call:compile_entrypoint, method:test_entrypoint_uses_root_then_sudo_for_workspace_symlink(self) → None, call:compile_entrypoint, call:entrypoint.find, method:test_entrypoint_fixes_mount_owners(self) → None, call:compile_entrypoint, func:test_compile_dockerfile_creates_config_dirs_for_user() → None, call:compile_dockerfile, func:test_compile_dockerfile_no_user_does_not_create_home() → None, call:compile_dockerfile, func:test_compile_dockerfile_uses_user_npm_prefix() → None, call:compile_dockerfile, func:test_compile_dockerfile_starts_as_root_and_drops_privileges() → None, call:compile_dockerfile, call:compile_entrypoint, func:test_compile_compose_runs_as_root() → None, call:compile_compose | dep: pytest, src.services.build.manifest_compiler
|
||||
- test_manifest_compiler.py | Unit tests for a manifest compiler that generates Dockerfiles, docker-compose files, and entrypoint scripts for containerized development environments. | exp: class:TestGetManifestHomeDir, method:test_home_directory_in_manifest_wins(self) → None, call:get_manifest_home_dir, method:test_user_name_derives_home(self) → None, call:get_manifest_home_dir, method:test_root_fallback(self) → None, call:get_manifest_home_dir, method:test_empty_home_directory_falls_back(self) → None, call:get_manifest_home_dir, class:TestCompileDockerfileHomeDirectory, method:test_env_home_and_workdir_use_home_directory(self) → None, call:compile_dockerfile, method:test_workspace_symlink_created(self) → None, call:compile_dockerfile, method:test_runtime_workspace_not_baked_into_image(self) → None, call:compile_dockerfile, method:test_runtime_working_dir_overrides_home_workdir(self) → None, call:compile_dockerfile, method:test_working_dir_expands_tilde(self) → None, call:compile_dockerfile, class:TestCompileComposeHomeDirectory, method:test_default_repo_mount_synthesized(self) → None, call:compile_compose, method:test_explicit_repo_mount_preserved(self) → None, call:compile_compose, method:test_workspace_name_substituted_in_mount_target(self) → None, call:compile_compose, method:test_working_dir_expands_home(self) → None, call:compile_compose, class:TestCompileEntrypoint, method:test_entrypoint_creates_home_and_workspace(self) → None, call:compile_entrypoint, method:test_entrypoint_uses_root_then_sudo_for_workspace_symlink(self) → None, call:compile_entrypoint, call:entrypoint.find, method:test_entrypoint_fixes_mount_owners(self) → None, call:compile_entrypoint, func:test_compile_dockerfile_creates_config_dirs_for_user() → None, call:compile_dockerfile, func:test_compile_dockerfile_no_user_does_not_create_home() → None, call:compile_dockerfile, func:test_compile_dockerfile_uses_user_npm_prefix() → None, call:compile_dockerfile, func:test_compile_dockerfile_starts_as_root_and_drops_privileges() → None, call:compile_dockerfile, call:compile_entrypoint, func:test_compile_compose_runs_as_root() → None, call:compile_compose | dep: pytest, src.services.build.manifest_compiler
|
||||
- test_migration_metadata.py | Tests Alembic database migration files for correct table definitions and revision chain metadata | exp: func:test_initial_migration_defines_all_core_tables() → None, call:Path(__file__).resolve, call:spec_from_file_location, call:module_from_spec, call:spec.loader.exec_module, func:test_refresh_tokens_migration_has_expected_revision_chain() → None, call:Path(__file__).resolve, call:spec_from_file_location, call:module_from_spec, call:spec.loader.exec_module | dep: pytest, importlib.util, pathlib, pathlib.Path
|
||||
- test_monitoring_models.py | Unit tests verifying creation, persistence, and querying of monitoring models (InstanceEvent and HealthCheck) with database migration compatibility. | exp: func:test_instance_event_creation(db_session) → None, call:User, call:uuid.uuid4, call:db_session.add, call:db_session.commit, call:ToolInstance, call:InstanceEvent, call:db_session.refresh, call:isinstance, func:test_health_check_creation(db_session) → None, call:User, call:uuid.uuid4, call:db_session.add, call:db_session.commit, call:ToolInstance, call:HealthCheck, call:db_session.refresh, call:isinstance, func:test_instance_event_query_by_instance(db_session) → None, call:User, call:uuid.uuid4, call:db_session.add, call:db_session.commit, call:ToolInstance, call:InstanceEvent, call:db_session.execute, call:select(InstanceEvent).where, call:result.scalar_one | dep: uuid, datetime, pytest, sqlalchemy, src.models.system.health_check, src.models.system.instance_event, src.models.tool.tool_instance, src.models.user.user
|
||||
- test_notification_service.py | Unit tests for NotificationService covering CRUD operations, filtering, sorting, and ownership isolation. | exp: func:notification_service() → NotificationService, call:NotificationService, func:user_a(db_session: AsyncSession) → User, call:User, call:uuid.uuid4, call:db_session.add, call:db_session.commit, func:user_b(db_session: AsyncSession) → User, call:User, call:uuid.uuid4, call:db_session.add, call:db_session.commit, func:test_create_notification(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:uuid.uuid4, func:test_list_notifications_orders_by_created_at_desc(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:datetime.now, call:timedelta, call:db_session.commit, call:db_session.refresh, call:notification_service.list_notifications, func:test_list_notifications_excludes_dismissed(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:notification_service.dismiss, call:notification_service.list_notifications, func:test_list_notifications_unread_only(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:notification_service.mark_read, call:notification_service.list_notifications, func:test_get_unread_count(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:range, call:notification_service.create_notification, call:notification_service.mark_read, call:notification_service.get_unread_count, func:test_mark_read_sets_read_at(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:notification_service.mark_read, func:test_mark_all_read_affects_all_unread(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:range, call:notification_service.create_notification, call:notification_service.mark_all_read, call:notification_service.get_unread_count, func:test_dismiss_sets_dismissed_at(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:notification_service.dismiss, call:db_session.execute, call:select(Notification).where, call:result.scalar_one, func:test_mark_read_wrong_owner_raises(db_session: AsyncSession, notification_service: NotificationService, user_a: User, user_b: User) → None, call:notification_service.create_notification, call:pytest.raises, call:notification_service.mark_read, func:test_dismiss_wrong_owner_raises(db_session: AsyncSession, notification_service: NotificationService, user_a: User, user_b: User) → None, call:notification_service.create_notification, call:pytest.raises, call:notification_service.dismiss, func:test_list_notifications_mute_categories(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:notification_service.list_notifications, func:test_get_unread_count_excludes_dismissed(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:notification_service.create_notification, call:notification_service.dismiss, call:notification_service.get_unread_count, func:test_dismiss_all_affects_all_non_dismissed(db_session: AsyncSession, notification_service: NotificationService, user_a: User) → None, call:range, call:notification_service.create_notification, call:notification_service.dismiss_all, call:notification_service.list_notifications, func:test_dismiss_all_affects_only_caller(db_session: AsyncSession, notification_service: NotificationService, user_a: User, user_b: User) → None, call:range, call:notification_service.create_notification, call:notification_service.dismiss_all, call:notification_service.list_notifications, func:test_mark_all_read_affects_only_caller(db_session: AsyncSession, notification_service: NotificationService, user_a: User, user_b: User) → None, call:range, call:notification_service.create_notification, call:notification_service.mark_all_read, call:notification_service.get_unread_count | dep: uuid, datetime, pytest, sqlalchemy, sqlalchemy.ext.asyncio, src.models.system.notification, src.models.user.user, src.services.shared.notification_service, NotificationService, Notification, User, AsyncSession
|
||||
@@ -30,7 +30,7 @@ Unit test suite for the API application covering core services, utilities, and i
|
||||
- test_readiness_probe.py | Unit tests for a Docker container readiness probe service that executes commands via docker exec with retry logic. | exp: class:TestExecuteProbe, class:TestIntegrationScenarios | dep: unittest.mock, src.services.shared.readiness_probe, subprocess
|
||||
- test_ssh_keys.py | Unit tests for SSH key preparation functionality including file creation, permissions, ownership, and error handling | exp: class:TestPrepareSshKeyFiles | dep: os, pathlib, unittest.mock, pytest, src.services.shared.ssh_keys
|
||||
## arch
|
||||
pytest-based unit testing with heavy use of mocking (subprocess, filesystem, database) to test components in isolation without external dependencies.
|
||||
Standard Python unittest/pytest patterns with heavy mocking of external dependencies (Docker, Git, subprocess, database), isolated per-module test files, and no shared fixtures or test infrastructure.
|
||||
## tags
|
||||
test, url, call:notification, git, home, call:, merge, call:db
|
||||
## symbols
|
||||
|
||||
@@ -91,16 +91,35 @@ class TestCompileDockerfileHomeDirectory:
|
||||
assert "WORKDIR /home/custom" in dockerfile
|
||||
|
||||
def test_workspace_symlink_created(self) -> None:
|
||||
"""When workspace name is known at build time, create /workspace symlink."""
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
"home_directory": "/home/custom",
|
||||
"workspace_name": "my-app",
|
||||
"user": {"name": "dev", "uid": 1000, "gid": 1000},
|
||||
}
|
||||
dockerfile = compile_dockerfile(manifest)
|
||||
|
||||
assert "mkdir -p /home/custom" in dockerfile
|
||||
assert "ln -sfn" in dockerfile
|
||||
assert "ln -sfn /home/custom/my-app /workspace" in dockerfile
|
||||
|
||||
def test_runtime_workspace_not_baked_into_image(self) -> None:
|
||||
"""When workspace name is a runtime placeholder, do not create literal
|
||||
{{WORKSPACE_NAME}} directories or symlinks in the image."""
|
||||
manifest = {
|
||||
"base_image": "ubuntu:24.04",
|
||||
"interface_type": "terminal",
|
||||
"home_directory": "/home/custom",
|
||||
"user": {"name": "dev", "uid": 1000, "gid": 1000},
|
||||
"mounts": [
|
||||
{"source_type": "repo", "target": "~/{{WORKSPACE_NAME}}"}
|
||||
],
|
||||
}
|
||||
dockerfile = compile_dockerfile(manifest)
|
||||
|
||||
assert "{{WORKSPACE_NAME}}" not in dockerfile
|
||||
assert "ln -sfn" not in dockerfile
|
||||
|
||||
def test_runtime_working_dir_overrides_home_workdir(self) -> None:
|
||||
manifest = {
|
||||
|
||||
Reference in New Issue
Block a user