File size: 12,397 Bytes
5dd1bb4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | # Verification Specification
**Feature:** F007
**Generated from:** specs/F007-VERIFICATION_INPUT.json
**Generated:** 2026-03-27
---
## 1. Unit Tests
### Dockerfile Validation
| Test | Description | Input | Expected | Category |
|------|-------------|-------|----------|----------|
| test_dockerfile_exists | Dockerfile exists at server/Dockerfile | N/A | File exists | happy |
| test_dockerfile_has_base_image_arg | BASE_IMAGE ARG is declared | Parse Dockerfile | `ARG BASE_IMAGE` present | happy |
| test_dockerfile_port_env_variable | PORT env var with fallback to 8000 | Parse Dockerfile | `ENV PORT` or CMD reads `$PORT` | happy |
| test_dockerfile_cmd_uses_port_env | CMD respects PORT env override | Set `PORT=7860` | Server binds to 7860 | happy |
| test_dockerfile_non_root_user | Container runs as non-root user | Parse Dockerfile | `USER appuser` or equivalent non-root USER directive | security |
| test_dockerfile_copies_databases | Spider databases are bundled | Parse Dockerfile | COPY instruction includes `data/databases/` | happy |
| test_dockerfile_healthcheck | Health check endpoint configured | Parse Dockerfile | HEALTHCHECK directive present | happy |
| test_dockerfile_no_dev_dependencies | No test/dev packages in final image | Inspect final stage | No pytest, ruff, etc. | edge |
**Run:** `uv run pytest tests/unit/test_dockerfile.py -v`
### openenv.yaml Manifest
| Test | Description | Input | Expected | Category |
|------|-------------|-------|----------|----------|
| test_manifest_exists | openenv.yaml exists at project root | N/A | File exists | happy |
| test_manifest_spec_version | spec_version field equals 1 | Parse YAML | `spec_version: 1` | happy |
| test_manifest_name | name field is sql_env | Parse YAML | `name: sql_env` | happy |
| test_manifest_type_space | type field is 'space' | Parse YAML | `type: space` | happy |
| test_manifest_runtime_fastapi | runtime field is 'fastapi' | Parse YAML | `runtime: fastapi` | happy |
| test_manifest_app_entrypoint | app field points to valid module | Parse YAML | `app: server.app:app` | happy |
| test_manifest_port | port field is 8000 | Parse YAML | `port: 8000` | happy |
| test_manifest_no_extra_fields | No unrecognized fields | Parse YAML | Only spec_version, name, type, runtime, app, port | edge |
| test_manifest_missing_required_field | Missing field produces validation error | Remove `name` | Validation error | error |
**Run:** `uv run pytest tests/unit/test_manifest.py -v`
### Blog Outline (docs/blog-outline.md)
| Test | Description | Input | Expected | Category |
|------|-------------|-------|----------|----------|
| test_blog_outline_exists | Blog outline file exists | N/A | File at `docs/blog-outline.md` | happy |
| test_blog_has_hook_section | Hook section present | Parse markdown | Section heading for hook/intro | happy |
| test_blog_has_problem_section | Problem section present | Parse markdown | Section about static benchmarks | happy |
| test_blog_has_solution_section | Solution/architecture section present | Parse markdown | Section about SQLEnv architecture | happy |
| test_blog_has_results_placeholder | Results placeholder for F006 | Parse markdown | Placeholder text for training results | happy |
| test_blog_has_try_it_section | Try-it-yourself section with links | Parse markdown | Links to HF Space, notebook, GitHub | happy |
| test_blog_links_not_broken | All links in blog are valid or marked placeholder | Parse markdown | No dead internal links | edge |
| test_blog_minimum_length | Blog outline has substantive content | Parse markdown | At least 200 words | edge |
**Run:** `uv run pytest tests/unit/test_blog_outline.py -v`
### Training Notebook (notebooks/train_grpo.ipynb)
| Test | Description | Input | Expected | Category |
|------|-------------|-------|----------|----------|
| test_notebook_exists | Notebook file exists | N/A | File at `notebooks/train_grpo.ipynb` | happy |
| test_notebook_valid_json | Notebook is valid JSON / ipynb format | Parse file | Valid nbformat structure | happy |
| test_notebook_has_setup_cell | Setup cell with pip install | Inspect cells | Cell containing `pip install` | happy |
| test_notebook_has_connect_cell | Connect cell using SQLEnvClient | Inspect cells | Cell importing/using SQLEnvClient | happy |
| test_notebook_has_train_cell | Training cell with GRPO loop | Inspect cells | Cell with training logic | happy |
| test_notebook_has_eval_cell | Evaluation cell for held-out questions | Inspect cells | Cell with evaluation logic | happy |
| test_notebook_has_plot_cell | Plotting cell with matplotlib | Inspect cells | Cell importing matplotlib and plotting | happy |
| test_notebook_colab_compatible | Colab badge or runtime metadata | Inspect metadata | `colab` in metadata or Colab badge in first cell | happy |
| test_notebook_no_hardcoded_paths | No absolute local paths | Inspect all cells | No `/Users/`, `/home/`, `C:\\` paths | edge |
| test_notebook_cells_ordered | Setup before connect before train | Inspect cell order | Correct logical ordering | edge |
| test_notebook_empty_outputs | Notebook shipped with cleared outputs | Inspect cells | All `outputs` arrays empty | edge |
**Run:** `uv run pytest tests/unit/test_notebook.py -v`
---
## 2. Integration Tests
### Flow: Local Docker Build and Run
| Step | Action | Expected | Verification |
|------|--------|----------|--------------|
| 1 | `docker build -t sql-env:test -f server/Dockerfile .` | Build succeeds with exit code 0 | Check exit code |
| 2 | `docker run -d -p 8000:8000 --name sql-env-test sql-env:test` | Container starts | Container running (`docker ps`) |
| 3 | Wait for health check (up to 30s) | `/health` returns 200 | `curl -f http://localhost:8000/health` |
| 4 | Connect WebSocket client, call reset | Episode starts, observation returned | Valid SQLObservation JSON |
| 5 | Send DESCRIBE action via WebSocket | Column info returned | Non-empty result field |
| 6 | Send ANSWER action via WebSocket | Episode ends, reward returned | `done: true`, reward is numeric |
| 7 | Stop container | Container stops cleanly | `docker stop sql-env-test` exits 0 |
**Run:** `uv run pytest tests/integration/test_docker_local.py -v`
### Flow: PORT Override for HF Spaces
| Step | Action | Expected | Verification |
|------|--------|----------|--------------|
| 1 | `docker run -d -p 7860:7860 -e PORT=7860 --name sql-env-port sql-env:test` | Container starts on port 7860 | Container running |
| 2 | `curl -f http://localhost:7860/health` | Health check passes | HTTP 200 |
| 3 | Port 8000 is NOT listening | No response on 8000 | `curl` fails on port 8000 |
**Run:** `uv run pytest tests/integration/test_port_override.py -v`
### Flow: Database Bundling Verification
| Step | Action | Expected | Verification |
|------|--------|----------|--------------|
| 1 | Build Docker image | Build succeeds | Exit code 0 |
| 2 | `docker run --rm sql-env:test ls /app/env/data/databases/` | Spider databases present | At least one database directory listed |
| 3 | `docker run --rm sql-env:test find /app/env/data/databases/ -name "*.sqlite"` | SQLite files present | At least one .sqlite file found |
| 4 | Start container and reset episode | Episode loads a bundled database | No "database not found" error |
**Run:** `uv run pytest tests/integration/test_db_bundling.py -v`
---
## 3. API Tests
No new API endpoints are introduced by F007. The existing `/health`, WebSocket, and REST endpoints from prior features are tested via integration tests above.
---
## 4. E2E Tests
### Scenario: Judge Experience -- Visit HF Space and Play Episode
**Setup:** Docker container running (locally simulating HF Space)
**Actions:**
1. Open health endpoint URL -- confirm service is up
2. Connect via WebSocket
3. Call `reset` -- receive initial observation with question and schema
4. Call `step` with DESCRIBE action -- receive column details
5. Call `step` with QUERY action -- receive query results
6. Call `step` with ANSWER action -- receive terminal observation with reward
**Expected:** Full episode completes without errors; reward is 0.0 or 1.0
**Run:** `uv run pytest tests/e2e/test_judge_experience.py -v`
### Scenario: Notebook Cell Sequence Validation
**Setup:** Notebook file at `notebooks/train_grpo.ipynb`
**Actions:**
1. Parse notebook JSON
2. Validate each cell type and content markers in order:
- Cell with `pip install` (setup)
- Cell with `SQLEnvClient` (connect)
- Cell with training loop keywords: `grpo`, `train`, `optimizer` (train)
- Cell with `eval` or `accuracy` or `held-out` (evaluate)
- Cell with `matplotlib` or `plt.` (plot)
3. Validate no syntax errors in code cells (compile check)
**Expected:** All five cell categories present in correct order; no syntax errors
**Run:** `uv run pytest tests/e2e/test_notebook_validation.py -v`
### Scenario: README Has Competition-Ready Content
**Setup:** README.md at project root
**Actions:**
1. Verify README contains project description
2. Verify README contains quickstart / getting started section
3. Verify README contains link to HF Space (or placeholder)
4. Verify README contains link to training notebook
5. Verify README contains architecture or how-it-works section
**Expected:** All five content sections present
**Run:** `uv run pytest tests/e2e/test_readme_completeness.py -v`
---
## 5. Edge Cases Checklist
- [ ] Dockerfile builds on CPU-only machine (no CUDA dependencies in final image)
- [ ] Container memory stays under HF Spaces free tier limit (~16GB)
- [ ] PORT env variable with non-numeric value handled gracefully
- [ ] PORT env variable with value 0 or negative handled gracefully
- [ ] Missing data/databases/ directory causes clear error at startup, not silent failure
- [ ] openenv.yaml with wrong spec_version is rejected by openenv validate
- [ ] Blog outline contains no TODO/FIXME/placeholder markers except the results section
- [ ] Notebook code cells have no import errors when dependencies are installed
- [ ] Notebook does not require GPU (runs on Colab free tier CPU)
- [ ] Container starts within 60 seconds (reasonable cold start)
- [ ] Docker image size is under 2GB (reasonable for free tier)
- [ ] .dockerignore excludes test files, .git, __pycache__, .env
- [ ] Non-root user can read database files (file permissions correct)
- [ ] Container handles SIGTERM gracefully (clean shutdown)
---
## 6. Evidence Requirements
| Category | Evidence Type | Example |
|----------|---------------|---------|
| Unit tests | pytest output | `X passed` |
| Integration | pytest + docker logs | `Container healthy, episode complete` |
| Dockerfile | docker build output | `Successfully built <hash>` |
| Port override | curl output | `HTTP 200 on port 7860` |
| Database bundling | docker exec output | `ls` shows .sqlite files |
| Blog outline | File exists + content check | `5 sections present` |
| Notebook | nbformat validation | `Valid ipynb, 5+ cells in order` |
| README | Content grep | `All required sections present` |
| E2E | Full episode log | `reset -> steps -> answer, reward=1.0` |
| Image size | docker images output | `< 2GB` |
---
## 7. External Deployment Prerequisites and Remediation
Use this checklist when deployment verification fails with external auth/access errors.
### GHCR Base Image Access (`403 Forbidden`)
1. Authenticate Docker to GHCR:
- `echo "$GITHUB_TOKEN" | docker login ghcr.io -u <github-username> --password-stdin`
2. Ensure `GITHUB_TOKEN` has package read scope for `ghcr.io/meta-pytorch/openenv-base`.
3. Retry build using explicit lowercase tag:
- `uv run openenv build -t openenv-sql-env-f007-hf-submission`
### Hugging Face Push Readiness
1. Authenticate Hugging Face CLI:
- `huggingface-cli login`
2. Confirm target Space repo exists and token has write access.
3. Run push:
- `uv run openenv push`
### Verification Outcome Rules for External Failures
- If local tests pass but GHCR/HF auth fails, record status as **partial verification** (external blocker) and include exact remediation commands above.
- Do not mark verifier result as `approved` until at least one authenticated build+push attempt is documented.
- Record authenticated evidence in `specs/F007-DEMO.md` under `## Live Local Proof` with separate `Authenticated Build Evidence` and `Hugging Face Push Evidence` subsections containing raw command output.
|