5fc8e035e6
- Add design doc / ADR for configurable /home/user home directory - Add implementation plan with phased rollout - Add test plan / QA checklist - Update OpenSpec task for home-path-expansion
115 lines
7.1 KiB
Markdown
115 lines
7.1 KiB
Markdown
# Test Plan / QA Checklist: Tool Container Home Directory
|
|
|
|
## Scope
|
|
|
|
This plan covers validation of the configurable `home_directory` feature for tool containers, including:
|
|
|
|
- `home_directory` field on `ToolType` and manifest schema.
|
|
- Repository/workspace mount target using `{home_directory}/{repo_name}` or `{home_directory}/{workspace_name}`.
|
|
- Manifest compiler behavior (`HOME`, `WORKDIR`, `/workspace` symlink).
|
|
- Legacy tool type migration via Alembic.
|
|
- Config-profile and git-mount `~`/`$HOME` expansion.
|
|
- Entrypoint permission fixer.
|
|
|
|
## Unit Tests
|
|
|
|
### `apps/api/tests/unit/test_home_path_expansion.py`
|
|
|
|
| ID | Test | Expected Result |
|
|
|----|------|-----------------|
|
|
| U1 | `expand_container_path("~/config", "/home/user")` | returns `/home/user/config` |
|
|
| U2 | `expand_container_path("~", "/home/user")` | returns `/home/user` |
|
|
| U3 | `expand_container_path("$HOME/config", "/home/user")` | returns `/home/user/config` |
|
|
| U4 | `expand_container_path("$HOME", "/home/user")` | returns `/home/user` |
|
|
| U5 | `expand_container_path("/opt/data", "/home/user")` | returns `/opt/data` unchanged |
|
|
| U6 | `expand_container_path("relative/path", "/home/user")` | returns `relative/path` unchanged |
|
|
| U7 | `expand_container_path("path/$HOME/other", "/home/user")` | returns `path/$HOME/other` unchanged (mid-string not expanded) |
|
|
|
|
### `apps/api/tests/unit/test_manifest_compiler.py`
|
|
|
|
| ID | Test | Expected Result |
|
|
|----|------|-----------------|
|
|
| U10 | `get_manifest_home_dir({"home_directory": "/home/dev"})` | returns `/home/dev` |
|
|
| U11 | `get_manifest_home_dir({"user": {"name": "user"}})` | returns `/home/user` |
|
|
| U12 | `get_manifest_home_dir({"user": {"name": "dev"}})` | returns `/home/dev` |
|
|
| U13 | `get_manifest_home_dir({})` | returns `/root` |
|
|
| U14 | `compile_dockerfile()` for manifest with `home_directory: /home/user` and `user.name: user` | Dockerfile contains `ENV HOME=/home/user`, `ENV USER=user`, `WORKDIR /home/user`, and creates `/workspace` symlink |
|
|
| U15 | `compile_dockerfile()` for manifest without user block | Dockerfile does not create user and defaults `HOME=/root` |
|
|
| U16 | `compile_compose()` with no explicit repo mount and `home_directory: /home/user` | Compose mounts `{repo_path}:/home/user/{repo_name}` |
|
|
| U17 | `compile_compose()` with explicit repo mount target `/custom/path` | Compose preserves `/custom/path`; `/home/user` default is not injected |
|
|
|
|
### `apps/api/tests/unit/test_instance_service.py` (new or expanded)
|
|
|
|
| ID | Test | Expected Result |
|
|
|----|------|-----------------|
|
|
| U20 | `create_tool_instance()` for `definition_type == "dockerfile"` | Compose mounts `{repo_path}:/home/user/{repo_name}` |
|
|
| U21 | `create_tool_instance()` for `definition_type == "compose"` with migrated template | Template renders `/home/user/{{WORKSPACE_NAME}}` correctly |
|
|
| U22 | `start_tool_instance()` passes `home_dir` to `apply_resolved_profile()` | Config-profile `~/config` expands to `/home/user/config` |
|
|
| U23 | `start_tool_instance()` passes `home_dir` to `resolve_git_mounts()` | Git mount `~/repo` expands to `/home/user/repo` |
|
|
|
|
### `apps/api/tests/unit/test_alembic_migrations.py` (new or expanded)
|
|
|
|
| ID | Test | Expected Result |
|
|
|----|------|-----------------|
|
|
| U30 | Upgrade adds `home_directory` column with default `/home/user` | Column exists and legacy rows have value `/home/user` |
|
|
| U31 | Upgrade rewrites `compose_template` containing `/workspace` | Template now uses `/home/user/{{WORKSPACE_NAME}}` |
|
|
| U32 | Downgrade restores `/workspace` strings | Original templates are restored |
|
|
| U33 | Downgrade drops `home_directory` column | Column no longer exists |
|
|
|
|
## Integration Tests
|
|
|
|
### `apps/api/tests/integration/test_tool_instance_lifecycle.py` (new or expanded)
|
|
|
|
| ID | Test | Steps | Expected Result |
|
|
|----|------|-------|-----------------|
|
|
| I1 | Manifest tool starts with `/home/user` home | Create manifest tool type with default `home_directory`; create and start instance | Container has `HOME=/home/user`, repo at `/home/user/{repo_name}`, `/workspace` symlink resolves correctly |
|
|
| I2 | Manifest tool with overridden `home_directory` | Set `home_directory: /home/dev` in manifest; create and start instance | Container has `HOME=/home/dev`, repo at `/home/dev/{repo_name}` |
|
|
| I3 | Container user can write under `/home/user` | Start instance; exec `touch /home/user/test-file` as container user | Command succeeds and file is owned by container user |
|
|
| I4 | Config-profile mount under `~` is writable | Create profile with mount `{"target": "~/config", "files": {"settings.json": "{}"}}`; start instance with profile | File appears at `/home/user/config/settings.json` and is writable by container user |
|
|
| I5 | Git mount under `~` is writable | Create profile with git mount mapping `{"target_path": "~/dotfiles"}`; start instance | Repo files appear at `/home/user/dotfiles` and are writable by container user |
|
|
| I6 | Legacy dockerfile tool uses new mount target after migration | Run Alembic migration; create legacy dockerfile tool instance | Repo mounts at `/home/user/{repo_name}` and `/workspace` symlink works |
|
|
| I7 | Explicit manifest repo mount target is preserved | Create manifest with explicit repo mount target `/opt/repo`; start instance | Repo mounts at `/opt/repo`; no `/home/user/{repo_name}` mount injected |
|
|
|
|
## Manual QA Checklist
|
|
|
|
- [ ] Create a new manifest tool type without specifying `home_directory`; verify default `/home/user` is used.
|
|
- [ ] Create a manifest tool type with `home_directory: /home/custom`; verify container reflects the override.
|
|
- [ ] Verify repository root directory name appears in the mount target (e.g., `/home/user/my-app`).
|
|
- [ ] Verify workspace-based instance uses workspace name in the mount target.
|
|
- [ ] Verify `/workspace` symlink points to the actual repo/workspace directory and `cd /workspace && pwd` works.
|
|
- [ ] Verify legacy dockerfile tool, after migration, mounts repo under `/home/user/{repo_name}`.
|
|
- [ ] Verify config-profile file mount under `~/config` lands at `/home/user/config`.
|
|
- [ ] Verify git mount under `~/dotfiles` lands at `/home/user/dotfiles`.
|
|
- [ ] Verify container user can create files under `/home/user` and mounted config directories.
|
|
- [ ] Verify `alembic downgrade -1` restores `/workspace` strings and removes the column.
|
|
|
|
## Regression Coverage
|
|
|
|
- Existing manifest tools without `home_directory` continue to work (default `/home/user`).
|
|
- Existing config profiles without `~`/`$HOME` continue to work.
|
|
- Existing legacy tools that did not use `/workspace` are unaffected by the migration rewrite.
|
|
- Existing `home-path-expansion` tests still pass.
|
|
|
|
## Test Commands
|
|
|
|
```bash
|
|
cd apps/api
|
|
pytest tests/unit/test_home_path_expansion.py tests/unit/test_manifest_compiler.py tests/unit/test_instance_service.py tests/unit/test_alembic_migrations.py -xvs
|
|
pytest tests/integration/test_tool_instance_lifecycle.py -xvs
|
|
|
|
# Alembic round-trip
|
|
alembic upgrade head
|
|
alembic downgrade -1
|
|
alembic upgrade head
|
|
|
|
cd apps/web
|
|
npm run typecheck
|
|
```
|
|
|
|
## Exit Criteria
|
|
|
|
- All listed unit and integration tests pass.
|
|
- Manual QA checklist is completed or equivalent automated coverage is added.
|
|
- Alembic migration round-trip passes without errors.
|
|
- No regressions in existing `home-path-expansion` or tool instance tests.
|