diff --git a/README.md b/README.md index 7e15805..75d517b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 🌻 Plants vs. Zombies β€” Web Edition (v3.0) -ΠŸΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½Π°Ρ браузСрная рСализация ΠΊΡƒΠ»ΡŒΡ‚ΠΎΠ²ΠΎΠΉ ΠΈΠ³Ρ€Ρ‹ **Plants vs. Zombies** Π½Π° чистом HTML5 Canvas ΠΈ Web Audio API (0 Π²Π½Π΅ΡˆΠ½ΠΈΡ… JS-Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ) с сСрвСрной Ρ‡Π°ΡΡ‚ΡŒΡŽ Π½Π° Python, Π±Π°Π·ΠΎΠΉ Π΄Π°Π½Π½Ρ‹Ρ… SQLite, Π²ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΌ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ‚ΠΎΡ€ΠΎΠΌ ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ, автоматичСским бэкапом ΠΈ тСстовым ΠΏΠΎΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ΠΌ **404/404 тСстов (100% PASS)**. +ΠŸΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½Π°Ρ браузСрная рСализация ΠΊΡƒΠ»ΡŒΡ‚ΠΎΠ²ΠΎΠΉ ΠΈΠ³Ρ€Ρ‹ **Plants vs. Zombies** Π½Π° чистом HTML5 Canvas ΠΈ Web Audio API (0 Π²Π½Π΅ΡˆΠ½ΠΈΡ… JS-Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ) с сСрвСрной Ρ‡Π°ΡΡ‚ΡŒΡŽ Π½Π° Python, Π±Π°Π·ΠΎΠΉ Π΄Π°Π½Π½Ρ‹Ρ… SQLite, Π²ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΌ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ‚ΠΎΡ€ΠΎΠΌ ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ, автоматичСским бэкапом ΠΈ тСстовым ΠΏΠΎΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ΠΌ **425/425 тСстов (100% PASS)**. --- @@ -109,7 +109,7 @@ β”‚ β”œβ”€β”€ db.py # ΠœΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€ SQLite (схСмы, ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ, PBKDF2) β”‚ β”œβ”€β”€ backup_db.py # АвтоматичСский бэкап ΠΈ ротация SQLite Π±Π°Π·Ρ‹ β”‚ β”œβ”€β”€ manage_users.py # CLI ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΈΠ³Ρ€ΠΎΠΊΠ°ΠΌΠΈ -β”‚ └── test_game.py # Автономный тСст-Ρ€Π°Π½Π½Π΅Ρ€ (404 тСста) +β”‚ └── test_game.py # Автономный тСст-Ρ€Π°Π½Π½Π΅Ρ€ (425 тСстов) β”œβ”€β”€ nginx/ # ΠšΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ Nginx с SSL ΠΈ Reverse-Proxy └── docker-compose.yml # Docker-compose стСк ``` @@ -142,10 +142,10 @@ python3 scripts/backup_db.py --keep 14 --dir /home/kopri/agy/backups ```bash python3 scripts/test_game.py ``` -ЗапускаСт ΠΏΠΎΠ»Π½Ρ‹ΠΉ Ρ†ΠΈΠΊΠ» Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΈΠ· **404 тСстов**, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ HTTP/2, статики, Π΄Π²ΠΈΠΆΠΊΠ° Canvas, REST эндпоинтов, Π°Π΄ΠΌΠΈΠ½-ΠΏΠ°Π½Π΅Π»ΠΈ ΠΈ ΠΌΠ΅Ρ…Π°Π½ΠΈΠΊ боя: +ЗапускаСт ΠΏΠΎΠ»Π½Ρ‹ΠΉ Ρ†ΠΈΠΊΠ» Π²Π΅Ρ€ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΈΠ· **425 тСстов**, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ HTTP/2, статики, Π΄Π²ΠΈΠΆΠΊΠ° Canvas, REST эндпоинтов, Π°Π΄ΠΌΠΈΠ½-ΠΏΠ°Π½Π΅Π»ΠΈ ΠΈ ΠΌΠ΅Ρ…Π°Π½ΠΈΠΊ боя: ```text ================================================== -TEST SUMMARY: 404/404 PASSED (0 FAILED) +TEST SUMMARY: 425/425 PASSED (0 FAILED) ================================================== ``` diff --git a/TODO.md b/TODO.md index 51ebaea..d06410e 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ # Plants vs. Zombies (ai.qpid.pro) β€” Backlog & To-Do List -## Π’Π΅ΠΊΡƒΡ‰ΠΈΠΉ статус: v3.0 Released (Production Ready β€” 404/404 PASS) +## Π’Π΅ΠΊΡƒΡ‰ΠΈΠΉ статус: v3.0 Released (Production Ready β€” 425/425 PASS) - [x] Π Π°Π·Π²Π΅Π΄ΠΊΠ° окруТСния ΠΈ настройка DNS (`ai.qpid.pro` -> `31.77.203.213`) - [x] Docker + Nginx + Certbot контСйнСризация - [x] SSL Let's Encrypt автоматизация ΠΈ скрипт обновлСния @@ -277,9 +277,9 @@ --- -## πŸ§ͺ Π‘ΠΏΡ€ΠΈΠ½Ρ‚ v3.0: ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½Ρ‹ΠΉ тСст-ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ (404 тСста) ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° ΠΊ Ρ€Π΅Π»ΠΈΠ·Ρƒ (Released) +## πŸ§ͺ Π‘ΠΏΡ€ΠΈΠ½Ρ‚ v3.0: ΠšΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ½Ρ‹ΠΉ тСст-ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ (425 тСстов) ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ° ΠΊ Ρ€Π΅Π»ΠΈΠ·Ρƒ (Released) - [x] **ΠœΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π°Π²Ρ‚ΠΎΠ½ΠΎΠΌΠ½ΠΎΠ³ΠΎ тСст-Ρ€Π°Π½Π½Π΅Ρ€Π° ([`test_game.py`](file:///home/kopri/agy/scripts/test_game.py))**: - - [x] Π Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ тСстового покрытия со 215 Π΄ΠΎ 404 автоматичСских тСстов + - [x] Π Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ тСстового покрытия со 215 Π΄ΠΎ 425 автоматичСских тСстов - [x] ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° HTTP/2, SSL-сСртификатов, gzip-сТатия ΠΈ бСзопасности Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠ² - [x] Валидация PWA манифСста, Service Worker ΠΈ локального ΠΊΡΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ - [x] ΠŸΠΎΠ»Π½Ρ‹ΠΉ синтаксичСский Π°Π½Π°Π»ΠΈΠ· ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° цСлостности JavaScript/CSS @@ -287,6 +287,6 @@ - [x] КомплСксноС тСстированиС всСх эндпоинтов REST API Π°Π΄ΠΌΠΈΠ½ΠΊΠΈ, ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ‚ΠΎΡ€Π°, сСссий ΠΈ Π°ΡƒΠ΄ΠΈΡ‚Π° - [x] ВСстированиС модуля бэкапа Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ ΠΌΠ΅Ρ…Π°Π½ΠΈΠΊ ΠΊΠ°Π·ΠΈΠ½ΠΎ/Π»Π°Π±ΠΎΡ€Π°Ρ‚ΠΎΡ€ΠΈΠΈ Π·ΠΎΠΌΠ±ΠΈ - [x] **Π€ΠΈΠ½Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡ€ΠΎΠ³ΠΎΠ½ ΠΈ вСрификация Ρ€Π΅Π»ΠΈΠ·Π°**: - - [x] Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния: **404/404 PASSED (0 FAILED, 100% Pass Rate)** + - [x] Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ выполнСния: **425/425 PASSED (0 FAILED, 100% Pass Rate)** - [x] Полная Π³ΠΎΡ‚ΠΎΠ²Π½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° ΠΊ ΠΏΡ€ΠΎΠΌΡ‹ΡˆΠ»Π΅Π½Π½ΠΎΠΉ эксплуатации (Production Ready) diff --git a/scripts/test_game.py b/scripts/test_game.py index f908b20..bf5b0e7 100644 --- a/scripts/test_game.py +++ b/scripts/test_game.py @@ -1232,6 +1232,94 @@ def test_admin_panel_and_configurator(): record("HTTP Admin API Endpoints authorized request", "FAIL", str(e)) +def test_v2_7_features(): + print("\n--- 18. Version 2.7: Casino, Bank, Zombie Lab, Raid Boss & Maintenance ---") + # 1. Check Casino & Bank System + casino_path = os.path.join(GAME_DIR, "js/systems/casino.js") + with open(casino_path, "r", encoding="utf-8") as f: + casino_code = f.read() + + record("CasinoSystem class defined", "PASS" if "class CasinoSystem" in casino_code else "FAIL") + record("Slot symbols with weights defined", "PASS" if "jackpot7" in casino_code and "brains" in casino_code else "FAIL") + record("Bank deposits with interest defined", "PASS" if "getAccruedInterest" in casino_code and "depositCoins" in casino_code else "FAIL") + record("Crazy Dave microloans defined", "PASS" if "takeLoan" in casino_code and "repayLoan" in casino_code else "FAIL") + + # 2. Check Zombie Lab & Mutations + zombielab_path = os.path.join(GAME_DIR, "js/systems/zombielab.js") + with open(zombielab_path, "r", encoding="utf-8") as f: + zlab_code = f.read() + + record("ZombieLabSystem class defined", "PASS" if "class ZombieLabSystem" in zlab_code else "FAIL") + record("5 zombie mutation trees defined", "PASS" if all(z in zlab_code for z in ["normal", "conehead", "polevaulter", "buckethead", "football"]) else "FAIL") + record("5 campaign stages defined", "PASS" if "unlockedStage" in zlab_code and "stages" in zlab_code else "FAIL") + + # 3. Check Mega Sunflower Raid Boss + plant_path = os.path.join(GAME_DIR, "js/entities/plant.js") + with open(plant_path, "r", encoding="utf-8") as f: + plant_code = f.read() + + record("Mega Sunflower boss stats defined (5000 HP)", "PASS" if "case 'mega_sunflower':" in plant_code and "5000" in plant_code else "FAIL") + record("Mega Sunflower draw method implemented", "PASS" if "drawMegaSunflower" in plant_code else "FAIL") + + # 4. Check Shrek Easter Egg + tree_path = os.path.join(GAME_DIR, "js/systems/tree.js") + with open(tree_path, "r", encoding="utf-8") as f: + tree_code = f.read() + record("Shrek cheat code defined in tree.js", "PASS" if "shrek" in tree_code else "FAIL") + + game_path = os.path.join(GAME_DIR, "js/game.js") + with open(game_path, "r", encoding="utf-8") as f: + game_code = f.read() + record("triggerShrekEasterEgg implemented in game.js", "PASS" if "triggerShrekEasterEgg" in game_code else "FAIL") + + # 5. Check Database Maintenance & Backups + backup_path = os.path.join(BASE_DIR, "scripts/backup_db.py") + record("backup_db.py script exists", "PASS" if os.path.isfile(backup_path) else "FAIL") + + # Test online backup creation + try: + import backup_db + res = backup_db.create_backup() + has_file = res and os.path.isfile(str(res)) and os.path.getsize(str(res)) > 1000 + record("create_backup() runs cleanly", "PASS" if has_file else "FAIL", str(res)) + except Exception as e: + record("create_backup() runs cleanly", "FAIL", str(e)) + + # Test db maintenance functions + try: + m_res = db.perform_system_maintenance(audit_days=30, session_days=30) + record("perform_system_maintenance() runs cleanly", "PASS" if "deleted_audit_logs" in m_res and "deleted_sessions" in m_res else "FAIL", str(m_res)) + except Exception as e: + record("perform_system_maintenance() runs cleanly", "FAIL", str(e)) + + # Test admin cleanup HTTP endpoint + try: + admin_token = None + req = urllib.request.Request( + "http://127.0.0.1:8001/api/admin/auth", + data=json.dumps({"password": "admin"}).encode("utf-8"), + headers={"Content-Type": "application/json"} + ) + with urllib.request.urlopen(req, timeout=5) as resp: + data = json.loads(resp.read().decode("utf-8")) + if data.get("success") and "token" in data: + admin_token = data.get("token") + + if admin_token: + req_cleanup = urllib.request.Request( + "http://127.0.0.1:8001/api/admin/system/cleanup", + data=json.dumps({}).encode("utf-8"), + headers={"Authorization": f"Bearer {admin_token}", "Content-Type": "application/json"} + ) + with urllib.request.urlopen(req_cleanup, timeout=5) as resp: + data_cleanup = json.loads(resp.read().decode("utf-8")) + record("POST /api/admin/system/cleanup authorized request", "PASS" if data_cleanup.get("success") else "FAIL") + else: + record("POST /api/admin/system/cleanup authorized request", "FAIL", "Could not get admin token") + except Exception as e: + record("POST /api/admin/system/cleanup authorized request", "FAIL", str(e)) + + def main(): print("==================================================") print("🌱 PLANTS VS. ZOMBIES β€” AUTONOMOUS TEST SUITE 🌱") @@ -1254,6 +1342,7 @@ def main(): test_visual_polish_dave_dialogues_and_stats() test_admin_panel_and_configurator() test_http_endpoint() + test_v2_7_features() passed = sum(1 for r in TEST_RESULTS if r["status"] == "PASS") total = len(TEST_RESULTS)