12 KiB
Batch 10 Final QA Report
Date: 2026-05-24 Scope: Final QA pass, README update, build validation, end-to-end regression checklist
1. README Update Summary
The following changes were made to README.md to reflect all refinement-pass improvements:
| Section | Change |
|---|---|
| Key Features → Reporting | Updated to mention direct web reporting via POST /api/content/:cxid/report in addition to Telegram bot reports. |
| Key Features → Native Media Batching | Added new row documenting native Telegram photo/video/audio/document batching with automatic 1,024-character caption truncation. |
| Tech Stack | Added reqwest 0.12 entry for server-side report forwarding. |
| API Endpoints | Added /api/content/:cxid/file/:file_idx/raw (raw text), /api/content/:cxid/report (direct web report), and noted the homepage includes dynamic bot username link + direct web reporting. |
| Admin Commands | Greatly expanded the table from 3 commands to 16 commands, adding /help, /get_id (with search variants), /create_submit_forward, /show_c_forward, /add_blacklist, /rm_blacklist, /sban, /smute, /mute, /pban, /kick, /rmute, /rban. Updated /blacklist_uid and /whitelist_uid to document the configured admin-group restriction. |
| Review Groups | Rewrote to document web report flow and inline keyboard actions ([ Rmv + Ban ], [ Delete Only ], [ Blacklist Only ], [ Ignore ]). |
| Submission Forward System | New subsection documenting the full forward workflow: creation, user submission, review with action buttons, approval media batching, caption truncation, and DM confirmation. |
| Auto-Destruct & Password Protection Fixes | New subsection documenting: HEAD requests no longer consume views; serve_raw_file now mirrors serve_file increment behavior; frontend password UX improvements (field appears on 401, "Incorrect password." error). |
2. Build Results
cargo check --workspace
Result: ✅ PASS
Checking cgcx-content-typing v0.1.0
Checking cgcx-file-pipeline v0.1.0
Checking cgcx-server v0.1.0
Checking cgcx-bot v0.1.0
Finished `dev` profile [unoptimized + debuginfo] target(s) in 5.36s
cargo test --workspace
Result: ✅ PASS (0 tests; all suites compile and run cleanly)
Finished `test` profile [unoptimized + debuginfo] target(s) in 21.37s
All test suites: 0 passed; 0 failed; 0 ignored
All doc-tests: 0 passed; 0 failed; 0 ignored
Note: Zero unit/integration tests exist across the workspace. All crates compile under test profile.
cd frontend && npm run build
Result: ✅ PASS
vite v8.0.14 building client environment for production...
✓ 621 modules transformed.
✓ built in 5.44s
Non-blocking warning: JS chunk >500 kB (pre-existing, not a regression).
3. Final End-to-End Regression Checklist (Batches 1–10)
Batch 1 — Command Fixes & View Count Safety
| # | Item | Status | Evidence |
|---|---|---|---|
| 1.1 | /get_id works in groups, supergroups, and channels (admin-only) |
✅ | is_admin() uses get_chat_member() + checks Administrator | Owner. Works for all chat types. handle_get_id_search() uses get_chat_administrators() for search. |
| 1.2 | /help renders without Telegram parse errors |
✅ | Uses .parse_mode(ParseMode::Html). Special chars escaped via escape_html(). No unescaped &, <, or >. |
| 1.3 | /blacklist_uid and /whitelist_uid show usage when args missing |
✅ | Both handlers check split_whitespace().nth(1).and_then(parse) and return usage text on failure. |
| 1.4 | /blacklist_uid and /whitelist_uid restricted to admin groups |
✅ | Gated by ctx.config.groups.admin_group_ids.contains(&chat_id.0) && is_admin(...). Requires both configured admin group AND admin status. |
| 1.5 | Password+auto-destroy content no longer 410s on first GET | ✅ | serve_file: views incremented only when !is_range && !is_conditional && !is_head. Early 410 happens before password check, so first GET passes. |
| 1.6 | serve_raw_file view increment fix |
✅ | Now accepts method: Method parameter. View increment guarded by if !is_head. Mirrors serve_file cleanup logic. |
Batch 2 — Direct Web Reporting & Frontend Dynamic Links
| # | Item | Status | Evidence |
|---|---|---|---|
| 2.1 | POST /api/content/:cxid/report endpoint exists |
✅ | cgcx-server/src/main.rs:349 — .route("/api/content/:cxid/report", post(report_content)). |
| 2.2 | reqwest in cgcx-server/Cargo.toml |
✅ | reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }. |
| 2.3 | Report handler inserts into DB and forwards to Telegram review groups | ✅ | report_content() inserts via ReportRepo, then iterates review_group_ids and sends sendMessage via Bot API. |
| 2.4 | Frontend uses dynamic BOT_USERNAME for bot link |
✅ | frontend/src/lib/api.js:4 exports BOT_USERNAME. Home.svelte uses {BOT_USERNAME} in main link and report links. |
| 2.5 | Frontend direct report form calls API instead of Telegram deep link | ✅ | Home.svelte:22 — fetch(${API_BASE}/api/content/.../report, {method: 'POST', ...}). |
Batch 3 — Homepage Password UX
| # | Item | Status | Evidence |
|---|---|---|---|
| 3.1 | Password field appears when accessing password-protected content | ✅ | Home.svelte:submit() — on 401 with empty password, sets needsPassword = true. |
| 3.2 | Incorrect password shows "Incorrect password." error | ✅ | Home.svelte:29 — on 401 with non-empty password, sets error = 'Incorrect password.'. |
| 3.3 | Correct password navigates to content view | ✅ | On success, fetchMetadata resolves and popstate event triggers router navigation. |
Batch 4 — Forward System, Media Batching, Caption Safety
| # | Item | Status | Evidence |
|---|---|---|---|
| 4.1 | Review message sent with correct inline keyboard | ✅ | finalize_upload sends keyboard with [ Approve ], [ Ignore ], [ Blackl. ], [ Ban ], [ Ban/BL u. ]. |
| 4.2 | handle_forward_callback handles all five actions |
✅ | Match arms for approve, ignore, blk, ban, banblk at lines 1899–2114. |
| 4.3 | Permission check on review group before action | ✅ | is_admin_in_chat(bot, ChatId(forward_def.review_group_id), ...) at line 1898. |
| 4.4 | Media batching handles >10 files by splitting | ✅ | decrypted.chunks(10) in both finalize_upload and handle_forward_callback. |
| 4.5 | Video/audio sent as native Telegram types | ✅ | InputMediaVideo, InputMediaAudio used when mime_type.starts_with("video/") / audio/. |
| 4.6 | Caption truncated to 1,024 characters | ✅ | if caption.chars().count() > 1024 { caption = caption.chars().take(1024).collect(); } at line 1953. |
| 4.7 | Approve action sends DM confirmation to submitter | ✅ | bot.send_message(ChatId(submission.user_id), ...) with posted link and direct access link. |
Batch 5 — Review Action Buttons
| # | Item | Status | Evidence |
|---|---|---|---|
| 5.1 | [ Approve ] callback sets password, forwards media, DMs user |
✅ | Lines 1899–2053. Sets direct password hash, forwards media, edits review message to [ APPROVED ]. |
| 5.2 | [ Ignore ] callback DMs rejection, edits message |
✅ | Lines 2054–2065. DMs rejection, edits review message to [ IGNORED ]. |
| 5.3 | [ Blackl. ] callback adds to forward blacklist |
✅ | Lines 2066–2077. Adds user to forward blacklist, edits to [ BLACKLISTED ]. |
| 5.4 | [ Ban ] callback bans in destination + review groups |
✅ | Lines 2078–2094. Bans, inserts punishments, edits to [ BANNED ]. |
| 5.5 | [ Ban/BL u. ] callback bans + blacklists |
✅ | Lines 2095–2114. Combines ban and blacklist, edits to [ BAN/BL ]. |
Batch 6 — GLOBAL_BAN Propagation
| # | Item | Status | Evidence |
|---|---|---|---|
| 6.1 | GroupsConfig.global_ban: bool with default false |
✅ | cgcx-config/src/lib.rs:66–73. |
| 6.2 | propagate_punishment() early returns if !global_ban |
✅ | cgcx-bot/src/main.rs:2270–2272. |
| 6.3 | Target chats: admin groups + review groups + active forward destinations | ✅ | Lines 2274–2290. |
| 6.4 | Bot admin check skips chats where bot is not admin | ✅ | is_admin(bot, ChatId(chat_id), bot_id).await before applying. |
| 6.5 | Punishment commands call propagate_punishment |
✅ | /sban, /smute, /mute, /pban, /kick all call it after local insertion. |
Batch 7 — Author Visibility & Metadata
| # | Item | Status | Evidence |
|---|---|---|---|
| 7.1 | Upload options include show_author toggle |
✅ | UploadOptions.show_author: bool, default true. "v1:opt:toggle_author" callback flips it. |
| 7.2 | show_author stored in DB |
✅ | Migration 005_show_author.sql adds show_author INTEGER NOT NULL DEFAULT 1. |
| 7.3 | Author hidden when show_author=false |
✅ | Server returns author: null when show_author=false (main.rs:~534). |
| 7.4 | Metadata displays date, size, author on view page | ✅ | ViewContent.svelte shows created_at, total_size, conditional author with Telegram link. |
Batch 8 — Deduplication & Hash Blacklist
| # | Item | Status | Evidence |
|---|---|---|---|
| 8.1 | plaintext_hash computed during encryption |
✅ | blake3::Hasher updated in encryption loop in cgcx-file-pipeline/src/lib.rs. |
| 8.2 | Deduplication lookup before storing | ✅ | find_active_by_plaintext_hash checked at line ~103. |
| 8.3 | Ref count increment on reuse | ✅ | increment_ref_count(&existing.content_id, existing.file_index) called. |
| 8.4 | Hash blacklist blocks re-uploads | ✅ | HashBlacklistRepo::contains(hash_bytes) called before dedup (line ~99). Returns CgcxError::BlockedHash. |
| 8.5 | hash_blacklist table exists |
✅ | Migration 007_hash_blacklist.sql. |
Batch 9 — Username Tracking
| # | Item | Status | Evidence |
|---|---|---|---|
| 9.1 | Username changes logged to configurable JSON file | ✅ | UserRepo::ensure_exists logs when old_username != new_username to uname_changes_path. |
| 9.2 | Config path default set | ✅ | Config.uname_changes_path, default "data/uname_changes.json". |
| 9.3 | Called on every interaction | ✅ | Bot calls ensure_exists on messages and callbacks with Some(&ctx.config.uname_changes_path). |
Batch 10 — Final QA & Documentation
| # | Item | Status | Evidence |
|---|---|---|---|
| 10.1 | README updated with all refinement-pass changes | ✅ | This document. |
| 10.2 | cargo check --workspace passes |
✅ | 5.36s, zero errors. |
| 10.3 | cargo test --workspace passes |
✅ | All suites compile, zero failures. |
| 10.4 | npm run build passes |
✅ | 5.44s, zero errors. |
| 10.5 | Final regression checklist prepared | ✅ | This checklist. |
4. Blockers / Risks
-
Zero Test Coverage
- No unit or integration tests exist in any crate. All verification above is static code review.
- Recommendation: Add integration tests for
FilePipeline::ingest_file, password flows, and report submission.
-
Memory Usage During Forward/Review
- Large files are decrypted entirely into memory (
decrypt_bytes+InputFile::memory) during forward approval and review group posting. - Risk: OOM on constrained hosts with large uploads (e.g., 100 MB+ videos).
- Recommendation: Consider streaming decryption to temp files and using
InputFile::file(path).
- Large files are decrypted entirely into memory (
-
Chunk Size Warning (Non-blocking)
- Frontend build warns about >500 KB JS chunk. Pre-existing, not a regression.
5. Summary
All requested features across batches 1–10 are implemented and appear correct based on static analysis. The workspace compiles cleanly, tests pass (trivially), and the frontend builds successfully. The README now accurately reflects the full feature set. The primary remaining risk is the complete absence of automated tests and potential memory pressure during large-file forward operations.