# Batch 1 QA Report ## Workspace Checks ### cargo check --workspace **Result:** ✅ PASSED ``` Checking cgcx-content-typing v0.1.0 Checking cgcx-file-pipeline v0.1.0 Checking cgcx-bot v0.1.0 Checking cgcx-server v0.1.0 Finished `dev` profile [unoptimized + debuginfo] target(s) in 2m 34s ``` ### cargo test --workspace **Result:** ✅ PASSED (0 tests) ``` Running unittests for cgcx_bot, cgcx_config, cgcx_content_typing, cgcx_core, cgcx_crypto, cgcx_db, cgcx_file_pipeline, cgcx_moderation, cgcx_server, cgcx_storage All test suites: 0 passed; 0 failed; 0 ignored All doc-tests: 0 passed; 0 failed; 0 ignored ``` **Note:** The workspace currently has no unit tests. All crates compile under test profile successfully. ### Frontend Dist Verification **Result:** ✅ PRESENT - `frontend/dist/index.html` exists - `frontend/dist/assets/` contains bundled JS/CSS - `frontend/dist/fonts/` contains web fonts --- ## Regression Checklist — Batch 1 | # | Item | Status | Code Evidence | |---|------|--------|---------------| | 1 | `/get_id` works in groups, supergroups, and channels (admin-only) | ✅ | `is_admin()` uses `get_chat_member()` + checks `Administrator \| Owner` (works for groups, supergroups, channels). `handle_get_id_search()` uses `get_chat_administrators()` for admin search. Command gated by `is_admin()` at line 462. | | 2 | `/help` renders without Telegram parse errors | ✅ | Uses `.parse_mode(ParseMode::Html)`. All special chars escaped: `<` and `>` for angle brackets, `` and `` tags properly closed. No unescaped `&`, `<`, or `>` in the help text. | | 3 | `/blacklist_uid` and `/whitelist_uid` show usage when args missing | ✅ | `handle_admin_blacklist_uid` (line 2165) and `handle_admin_whitelist_uid` (line 2184) both check `text.split_whitespace().nth(1).and_then(\|s\| s.parse::().ok())` and return `"Usage: /blacklist_uid "` / `"Usage: /whitelist_uid "` when parsing fails. | | 4 | `/blacklist_uid` and `/whitelist_uid` are rejected in non-admin groups | ✅ | Both commands gated by `ctx.config.groups.admin_group_ids.contains(&chat_id.0) && is_admin(...)` (lines 421, 428). Requires chat ID to be in configured admin groups AND user to be an admin. | | 5 | Password+auto-destroy content no longer 410s on first GET (HEAD requests don't consume views) | ✅ | `serve_file` (line 736): `is_head = method == Method::HEAD`. Views incremented only when `!is_range && !is_conditional && !is_head`. Early 410 (`content.view_count >= max`) happens before password check, so first GET passes, returns content, then increments. HEAD never consumes a view. | --- ## Notes & Observations - **`serve_raw_file`** (`/api/content/:cxid/file/:file_idx/raw`) does not increment view counts and does not accept a `Method` parameter. It is registered via `.get(serve_raw_file)` so Axum will route HEAD to it. It has the early `content.view_count >= max` 410 check but never increments views. This is likely intentional (raw endpoint for text preview), but worth noting that view-consumption semantics differ from the main `serve_file` endpoint. - **No tests** exist in the workspace. Future batches may benefit from adding unit/integration tests for the regression items above. - **No blockers** for Batch 1.