mirror of
https://github.com/makeplane/plane.git
synced 2026-07-10 04:25:24 +02:00
* chore(api): add docker compose test runner
Adds docker-compose-test.yml at the repo root that boots an isolated
postgres / valkey / rabbitmq / minio stack with health checks and tmpfs
data dirs, then runs pytest against it and exits. Includes a usage doc
under apps/api/tests/RUNNING_TESTS.md and a pointer in AGENTS.md.
Prereq: ./setup.sh (generates apps/api/.env).
Usage:
docker compose -f docker-compose-test.yml up --build \
--abort-on-container-exit --exit-code-from api-tests
docker compose -f docker-compose-test.yml down -v
* fix(api): correct bugs surfaced by the pytest suite
Five small bugs caught by enabling the pytest contract suite end-to-end.
Each is independently justifiable:
- api/serializers/cycle.py + api/views/cycle.py: CycleCreateSerializer.validate
required project_id in the request body, but the view only ever passes
it through the URL kwarg. Cycle create/update via the public API was
returning 400 "Project ID is required". Read project_id from
serializer context (passed by the view) in addition to body/instance.
- app/views/api.py: ApiTokenEndpoint.get(pk) and patch(pk) did not filter
out is_service=True tokens, so a user could read and modify service
tokens through the user token endpoint. The list mode and delete
already filter is_service=False; aligned the other two.
- bgtasks/work_item_link_task.py: validate_url_ip checked hostname before
scheme, so file:///etc/passwd raised "No hostname found" instead of
the documented "Only HTTP and HTTPS" error. Swapped the order so the
scheme guard matches the docstring intent.
- utils/path_validator.py: get_allowed_hosts used `WEB_URL or APP_BASE_URL`
so when both are configured to different hosts (the standard local
setup: WEB_URL=:8000, APP_BASE_URL=:3000), only one was added to the
allow-list. Redirects to APP_BASE_URL then had their next_path stripped
because the host wasn't allowed. Include every configured base URL.
* chore(api): align pytest tests with current behavior, clear warnings
Test-side fixes paired with the product fixes in the previous commit, plus
deprecation cleanup that drops the test run from 104 warnings to 0.
Tests:
- tests/contract/api/test_cycles.py: project fixture sets cycle_view=True;
the Project model defaults the flag to False, so cycle create/update
always tripped "Cycles are not enabled for this project".
- tests/contract/app/test_authentication.py: next_path uses "/workspaces"
(validate_next_path rejects values without a leading slash and returns
empty, which dropped the path from the redirect URL).
- tests/unit/bg_tasks/test_copy_s3_objects.py: mocked sync_with_external_service
now returns description_json; the task unconditionally writes the value
back to the Issue, and Issue.description_json is NOT NULL on UPDATE.
- tests/unit/utils/test_url.py: three length-limit tests placed the URL at
char 970+ on a single line, which contains_url truncates away as ReDoS
defense (500-char per-line cap). Restructured to keep test intent intact
while staying inside the per-line window.
Warning cleanup (104 → 0):
- settings/common.py: removed USE_L10N=True (deprecated in Django 4.0,
removed in 5.0; default is True).
- celery.py, settings/local.py, settings/production.py: pythonjsonlogger
moved jsonlogger → json; update the import / formatter path.
83 lines
4.3 KiB
Markdown
83 lines
4.3 KiB
Markdown
# Running the API Test Suite
|
|
|
|
This guide covers running the Django/pytest suite for `apps/api` inside Docker via `docker-compose-test.yml` at the repo root. The compose file boots an isolated stack — Postgres, Valkey (Redis), RabbitMQ, MinIO — with tmpfs-backed data dirs, so every run begins from a clean slate and a single teardown command removes everything.
|
|
|
|
For background on the test layout, markers, and fixtures, see [`TESTING_GUIDE.md`](./TESTING_GUIDE.md) and [`README.md`](./README.md).
|
|
|
|
## Prerequisites
|
|
|
|
- Docker and Docker Compose v2 (`docker compose ...`)
|
|
- Env files generated via the setup script:
|
|
|
|
```bash
|
|
./setup.sh
|
|
```
|
|
|
|
This copies `apps/api/.env.example` → `apps/api/.env` (along with the other app env files). The compose file reads `apps/api/.env`, so this step must run **before** the first `docker compose` invocation.
|
|
|
|
## Running the suite
|
|
|
|
All commands are run from the repo root.
|
|
|
|
### Full suite
|
|
|
|
```bash
|
|
docker compose -f docker-compose-test.yml up \
|
|
--build \
|
|
--abort-on-container-exit \
|
|
--exit-code-from api-tests
|
|
```
|
|
|
|
- `--build` rebuilds the `api-tests` image when `Dockerfile.dev` or `requirements/*.txt` change.
|
|
- `--abort-on-container-exit` stops the dependency services as soon as `api-tests` exits.
|
|
- `--exit-code-from api-tests` propagates pytest's exit code so this works in CI.
|
|
|
|
### Filtered runs
|
|
|
|
Use `docker compose run` to override the default `pytest` command. Anything you pass after the service name is forwarded to pytest.
|
|
|
|
```bash
|
|
# Only unit tests (marker defined in pytest.ini)
|
|
docker compose -f docker-compose-test.yml run --rm --build api-tests pytest -m unit
|
|
|
|
# A single directory, filtered by name
|
|
docker compose -f docker-compose-test.yml run --rm api-tests \
|
|
pytest plane/tests/unit -k "test_workspace"
|
|
|
|
# Single file with verbose output
|
|
docker compose -f docker-compose-test.yml run --rm api-tests \
|
|
pytest plane/tests/unit/models/test_workspace.py -vv
|
|
```
|
|
|
|
The available markers (`unit`, `contract`, `smoke`, `slow`) are declared in `apps/api/pytest.ini`.
|
|
|
|
### Teardown
|
|
|
|
```bash
|
|
docker compose -f docker-compose-test.yml down -v
|
|
```
|
|
|
|
`-v` removes the ephemeral volumes and the `test_env` network. Because the data directories are tmpfs, no host state survives a teardown — every run starts clean. Run this between unrelated test sessions to free Docker resources.
|
|
|
|
## How it works
|
|
|
|
| Service | Image | Purpose |
|
|
| ------------ | ------------------------------------ | --------------------------------------------- |
|
|
| `test-db` | `postgres:15.7-alpine` | Application database |
|
|
| `test-redis` | `valkey/valkey:7.2.11-alpine` | Cache / Celery broker |
|
|
| `test-mq` | `rabbitmq:3.13.6-management-alpine` | Task queue |
|
|
| `test-minio` | `minio/minio` | S3-compatible object storage |
|
|
| `api-tests` | built from `apps/api/Dockerfile.dev` | Installs `requirements/test.txt`, runs pytest |
|
|
|
|
All four dependencies expose health checks; `api-tests` waits for `service_healthy` on each via `depends_on`, so pytest only starts once the stack is ready.
|
|
|
|
Test-time env overrides live in the compose file itself (`POSTGRES_HOST=test-db`, `REDIS_URL=redis://test-redis:6379/`, `AWS_S3_ENDPOINT_URL=http://test-minio:9000`, `DJANGO_SETTINGS_MODULE=plane.settings.test`). Everything else is inherited from `apps/api/.env`.
|
|
|
|
## Troubleshooting
|
|
|
|
- **`./apps/api/.env: no such file or directory`** — run `./setup.sh` from the repo root.
|
|
- **Port already in use** — none of the test services publish host ports; if you see this it's coming from a different compose stack. Stop the local stack (`docker compose -f docker-compose-local.yml down`).
|
|
- **Stale image after dependency changes** — rebuild explicitly: `docker compose -f docker-compose-test.yml build --no-cache api-tests`.
|
|
- **MinIO bucket missing** — the `test-minio` entrypoint creates the bucket named by `AWS_S3_BUCKET_NAME` (default `uploads`). Change the value in `apps/api/.env` and re-run.
|
|
- **Database state leaking between runs** — confirm you ran `down -v` (not just `down`). The tmpfs mounts are torn down with the container, but the network and any externally created volumes need `-v` to clear.
|