From 950b1969d8872e55253f76955fd0933eb51add94 Mon Sep 17 00:00:00 2001 From: adtpdn Date: Tue, 7 Jul 2026 18:15:47 +0800 Subject: [PATCH] feat: update --- .agents/workflows/do-task.md | 164 ----------- .agents/workflows/pending-tasks.md | 53 ---- run_tests.cmd | 64 ----- scenes/candy.tscn | 16 ++ scenes/candy_survival_hud.tscn | 2 +- scenes/player.gd | 49 ++-- scripts/bot_controller.gd | 20 +- scripts/managers/candy_survival_manager.gd | 133 +++++++-- scripts/managers/goals_cycle_manager.gd | 3 + scripts/managers/player_input_manager.gd | 13 + scripts/managers/player_movement_manager.gd | 34 ++- scripts/managers/playerboard_manager.gd | 34 ++- scripts/managers/ui_manager.gd | 292 +++++++++++--------- scripts/models/scarcity_model.gd | 2 +- tests/README.md | 224 --------------- tests/helpers/gridmap_mock.gd | 34 --- tests/helpers/gridmap_mock.gd.uid | 1 - tests/helpers/main_mock.gd | 9 - tests/helpers/main_mock.gd.uid | 1 - tests/test_admin_panel.gd | 144 ---------- tests/test_admin_panel.gd.uid | 1 - tests/test_analytics_monitoring.gd | 46 --- tests/test_analytics_monitoring.gd.uid | 1 - tests/test_auth_security.gd | 189 ------------- tests/test_auth_security.gd.uid | 1 - tests/test_automated_testing_ci.gd | 44 --- tests/test_automated_testing_ci.gd.uid | 1 - tests/test_backend_facade.gd | 98 ------- tests/test_backend_facade.gd.uid | 1 - tests/test_backend_facade_pattern.gd | 45 --- tests/test_backend_facade_pattern.gd.uid | 1 - tests/test_backend_service_complete.gd | 45 --- tests/test_backend_service_complete.gd.uid | 1 - tests/test_bot_gauntlet.gd | 176 ------------ tests/test_bot_gauntlet.gd.uid | 1 - tests/test_client_backend_facade.gd | 103 ------- tests/test_client_backend_facade.gd.uid | 1 - tests/test_code_documentation.gd | 55 ---- tests/test_code_documentation.gd.uid | 1 - tests/test_currency_security.gd | 45 --- tests/test_currency_security.gd.uid | 1 - tests/test_debug_cleanup.gd | 135 --------- tests/test_debug_cleanup.gd.uid | 1 - tests/test_debug_code_removal.gd | 44 --- tests/test_debug_code_removal.gd.uid | 1 - tests/test_deployment_pipeline.gd | 108 -------- tests/test_deployment_pipeline.gd.uid | 1 - tests/test_economy_facade.gd | 44 --- tests/test_economy_facade.gd.uid | 1 - tests/test_encryption_key_management.gd | 44 --- tests/test_encryption_key_management.gd.uid | 1 - tests/test_error_handling.gd | 45 --- tests/test_error_handling.gd.uid | 1 - tests/test_github_actions_workflow.gd | 44 --- tests/test_github_actions_workflow.gd.uid | 1 - tests/test_guest_identity.gd | 108 -------- tests/test_guest_identity.gd.uid | 1 - tests/test_iap_receipt_validation.gd | 50 ---- tests/test_iap_receipt_validation.gd.uid | 1 - tests/test_identity_manager.gd | 45 --- tests/test_identity_manager.gd.uid | 1 - tests/test_lobby_refactor.gd | 45 --- tests/test_lobby_refactor.gd.uid | 1 - tests/test_localization_i18n.gd | 44 --- tests/test_localization_i18n.gd.uid | 1 - tests/test_mode_config.gd | 128 --------- tests/test_mode_config.gd.uid | 1 - tests/test_nakama_key_security.gd | 45 --- tests/test_nakama_key_security.gd.uid | 1 - tests/test_player_refactor.gd | 45 --- tests/test_player_refactor.gd.uid | 1 - tests/test_rate_limiting_anticheat.gd | 44 --- tests/test_rate_limiting_anticheat.gd.uid | 1 - tests/test_regional_servers.gd | 44 --- tests/test_regional_servers.gd.uid | 1 - tests/test_session_management.gd | 44 --- tests/test_session_management.gd.uid | 1 - tests/test_shop_validation.gd | 190 ------------- tests/test_shop_validation.gd.uid | 1 - tests/test_steam_depot.gd | 98 ------- tests/test_steam_depot.gd.uid | 1 - tests/test_sync_desync.gd | 125 --------- tests/test_sync_desync.gd.uid | 1 - tests/test_task_019.gd | 44 --- tests/test_task_019.gd.uid | 1 - tests/test_task_022.gd | 44 --- tests/test_task_022.gd.uid | 1 - tests/test_task_023.gd | 44 --- tests/test_task_023.gd.uid | 1 - tests/test_task_025.gd | 44 --- tests/test_task_025.gd.uid | 1 - tests/test_testing_infrastructure.gd | 44 --- tests/test_testing_infrastructure.gd.uid | 1 - tests/test_tutorial_isolation.gd | 127 --------- tests/test_tutorial_isolation.gd.uid | 1 - tests/test_versioning_integrity.gd | 100 ------- tests/test_versioning_integrity.gd.uid | 1 - 97 files changed, 388 insertions(+), 3755 deletions(-) delete mode 100644 .agents/workflows/do-task.md delete mode 100644 .agents/workflows/pending-tasks.md delete mode 100644 run_tests.cmd create mode 100644 scenes/candy.tscn delete mode 100644 tests/README.md delete mode 100644 tests/helpers/gridmap_mock.gd delete mode 100644 tests/helpers/gridmap_mock.gd.uid delete mode 100644 tests/helpers/main_mock.gd delete mode 100644 tests/helpers/main_mock.gd.uid delete mode 100644 tests/test_admin_panel.gd delete mode 100644 tests/test_admin_panel.gd.uid delete mode 100644 tests/test_analytics_monitoring.gd delete mode 100644 tests/test_analytics_monitoring.gd.uid delete mode 100644 tests/test_auth_security.gd delete mode 100644 tests/test_auth_security.gd.uid delete mode 100644 tests/test_automated_testing_ci.gd delete mode 100644 tests/test_automated_testing_ci.gd.uid delete mode 100644 tests/test_backend_facade.gd delete mode 100644 tests/test_backend_facade.gd.uid delete mode 100644 tests/test_backend_facade_pattern.gd delete mode 100644 tests/test_backend_facade_pattern.gd.uid delete mode 100644 tests/test_backend_service_complete.gd delete mode 100644 tests/test_backend_service_complete.gd.uid delete mode 100644 tests/test_bot_gauntlet.gd delete mode 100644 tests/test_bot_gauntlet.gd.uid delete mode 100644 tests/test_client_backend_facade.gd delete mode 100644 tests/test_client_backend_facade.gd.uid delete mode 100644 tests/test_code_documentation.gd delete mode 100644 tests/test_code_documentation.gd.uid delete mode 100644 tests/test_currency_security.gd delete mode 100644 tests/test_currency_security.gd.uid delete mode 100644 tests/test_debug_cleanup.gd delete mode 100644 tests/test_debug_cleanup.gd.uid delete mode 100644 tests/test_debug_code_removal.gd delete mode 100644 tests/test_debug_code_removal.gd.uid delete mode 100644 tests/test_deployment_pipeline.gd delete mode 100644 tests/test_deployment_pipeline.gd.uid delete mode 100644 tests/test_economy_facade.gd delete mode 100644 tests/test_economy_facade.gd.uid delete mode 100644 tests/test_encryption_key_management.gd delete mode 100644 tests/test_encryption_key_management.gd.uid delete mode 100644 tests/test_error_handling.gd delete mode 100644 tests/test_error_handling.gd.uid delete mode 100644 tests/test_github_actions_workflow.gd delete mode 100644 tests/test_github_actions_workflow.gd.uid delete mode 100644 tests/test_guest_identity.gd delete mode 100644 tests/test_guest_identity.gd.uid delete mode 100644 tests/test_iap_receipt_validation.gd delete mode 100644 tests/test_iap_receipt_validation.gd.uid delete mode 100644 tests/test_identity_manager.gd delete mode 100644 tests/test_identity_manager.gd.uid delete mode 100644 tests/test_lobby_refactor.gd delete mode 100644 tests/test_lobby_refactor.gd.uid delete mode 100644 tests/test_localization_i18n.gd delete mode 100644 tests/test_localization_i18n.gd.uid delete mode 100644 tests/test_mode_config.gd delete mode 100644 tests/test_mode_config.gd.uid delete mode 100644 tests/test_nakama_key_security.gd delete mode 100644 tests/test_nakama_key_security.gd.uid delete mode 100644 tests/test_player_refactor.gd delete mode 100644 tests/test_player_refactor.gd.uid delete mode 100644 tests/test_rate_limiting_anticheat.gd delete mode 100644 tests/test_rate_limiting_anticheat.gd.uid delete mode 100644 tests/test_regional_servers.gd delete mode 100644 tests/test_regional_servers.gd.uid delete mode 100644 tests/test_session_management.gd delete mode 100644 tests/test_session_management.gd.uid delete mode 100644 tests/test_shop_validation.gd delete mode 100644 tests/test_shop_validation.gd.uid delete mode 100644 tests/test_steam_depot.gd delete mode 100644 tests/test_steam_depot.gd.uid delete mode 100644 tests/test_sync_desync.gd delete mode 100644 tests/test_sync_desync.gd.uid delete mode 100644 tests/test_task_019.gd delete mode 100644 tests/test_task_019.gd.uid delete mode 100644 tests/test_task_022.gd delete mode 100644 tests/test_task_022.gd.uid delete mode 100644 tests/test_task_023.gd delete mode 100644 tests/test_task_023.gd.uid delete mode 100644 tests/test_task_025.gd delete mode 100644 tests/test_task_025.gd.uid delete mode 100644 tests/test_testing_infrastructure.gd delete mode 100644 tests/test_testing_infrastructure.gd.uid delete mode 100644 tests/test_tutorial_isolation.gd delete mode 100644 tests/test_tutorial_isolation.gd.uid delete mode 100644 tests/test_versioning_integrity.gd delete mode 100644 tests/test_versioning_integrity.gd.uid diff --git a/.agents/workflows/do-task.md b/.agents/workflows/do-task.md deleted file mode 100644 index 53db83c..0000000 --- a/.agents/workflows/do-task.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -description: This document tells AI agents how to work on Tekton Dash tasks end-to-end. ---- - -# AI Agent Workflow Guide for Tekton Dash - -This document tells AI agents how to work on Tekton Dash tasks end-to-end. - ---- - -## 1. Task Source: Notion MCP - -All tasks live on the **"TektonDash - Armageddon PR Tasks"** Notion board. - -https://www.notion.so/danchiego-game/36433be43b29800c8422ed5bdd65671b?v=36433be43b2980de8635000c0a910a0d - -### Finding Tasks - -Should always start with this to find tasks, find the highest priority task that is not done, second is In Progress, then To Do. - -example, query for "Gauntlet" -``` -Use: mcp_notion-mcp-server_API-post-search - query: "[Gauntlet]" or task name - filter: {"property": "object", "value": "page"} -``` - -### Reading a Task - -Each task page has these properties: - -| Property | Type | Purpose | -|---|---|---| -| **Name** | title | Task title, e.g. `[Gauntlet] #1 Game Mode Registration` | -| **Status** | select | `To Do` → `In Progress` → `Done` | -| **Priority** | select | `P0` (critical) / `P1` / `P2` / `P3` | -| **Effort** | select | `S - Small` / `M - Medium` / `L - Large` / `XL - Epic` | -| **Sprint** | select | `Alpha` / `Beta` / `Release` | -| **ProjectType** | select | `CORE` / `CLIENT` / `SERVER` / `INFRA` | -| **Description** | rich_text | Full task description — **read this to understand what to do** | -| **Acceptance** | checkbox | Check when task is verified complete | -| **DueDate** | date | Optional deadline | -| **UnitTest** | date | Optional test completion date | - -### Task Lifecycle - -``` -To Do → In Progress → Done -``` - -1. **Pick up task**: Set `Status` → `In Progress` -2. **Do the work**: Read `Description`, implement the changes -3. **Write unit tests**: Follow pattern in `tests/` directory -4. **Mark complete**: Set `Status` → `Done`, check `Acceptance` ✅ -5. **Update changelog**: Add entry to `CHANGELOG_DRAFT.md` (consumer language) -6. **Bump version**: Update `project.godot` + `export_presets.cfg` - -``` -Use: mcp_notion-mcp-server_API-patch-page - page_id: "" - properties: {"Status": {"select": {"name": "Done"}}, "Acceptance": {"checkbox": true}} -``` - ---- - -## 2. Code Structure - -| Path | Purpose | -|---|---| -| `scripts/game_mode.gd` | GameMode enum + helpers (add new modes here) | -| `scripts/managers/` | All game mode managers (lobby, stop_n_go, portal, gauntlet) | -| `scenes/main.gd` | Central orchestrator — init, setup, game start routing | -| `tests/` | GUT unit tests — one file per task/feature | - -### Adding a New Game Mode - -1. Add enum to `scripts/game_mode.gd` → update `from_string()`, `mode_to_string()`, `get_all_modes()`, `is_restricted()` -2. Add mode name to `LobbyManager.available_game_modes` in `lobby_manager.gd` -3. Add arena name to `_update_available_areas()` in `lobby_manager.gd` -4. Add manager var + init branch in `main.gd` `_init_managers()` -5. Add setup branch in `_setup_host_game()` and `_setup_client_game()` -6. Add start branch in `_start_game()` -7. Add background in `_apply_arena_background()` - ---- - -## 3. Unit Testing - -### Pattern - -All tests extend `GutTest` and live in `tests/`. Naming: `test_.gd` - -```gdscript -extends GutTest - -func before_all(): - gut.p("=== Feature Tests [Task ID] ===") - -func test_something(): - assert_eq(actual, expected, "Description") - -func after_all(): - gut.p("=== Feature Tests Complete ===") -``` - -### Running Tests - -```cmd -run_tests.cmd # all tests -run_tests.cmd test_gauntlet_registration # specific test -``` - -Reports saved to `test_reports/` with timestamps. - ---- - -## 4. Version Bumping - -**Before bumping, check git for existing uncommitted version changes:** - -```cmd -git diff --cached -- project.godot CHANGELOG_DRAFT.md -git diff -- project.godot CHANGELOG_DRAFT.md -``` - -### If version changes already exist (staged or unstaged): -→ **APPEND** your changelog bullet to the existing version block in `CHANGELOG_DRAFT.md` -→ **DO NOT** bump `project.godot` or `export_presets.cfg` — you're joining an in-progress batch - -### If NO version changes exist (clean state): -→ **BUMP** version (increment patch: `2.3.5` → `2.3.6`) -→ **UPDATE** all locations below - -Version appears in **4 locations** — all must match: - -| File | Field | -|---|---| -| `CHANGELOG_DRAFT.md` | `## [X.Y.Z] — YYYY-MM-DD` header | -| `project.godot` | `config/version="X.Y.Z"` | -| `export_presets.cfg` | `application/file_version` and `application/product_version` (per preset) | -| `export_presets.cfg` | `export_path` filenames containing version | -| `export_presets.cfg` | `version/name` (Android preset) | - -### Changelog Style - -Entries are **consumer-facing** (readable by players). No internal jargon. - -```markdown -## [2.3.6] — 2026-05-22 -- Added new game mode: Candy Cannon Survival -``` - -**Bad:** "Added GAUNTLET = 3 to GameMode.Mode enum" -**Good:** "Added new game mode: Candy Cannon Survival" - ---- - -## 5. Key Conventions - -- **Caveman Mode**: Be terse. No filler. Execute first, talk second. -- **Read before edit**: Always check whole files before modifying `.gd`, `.tscn`, `.tres`, `.res` files. -- **Notion status flow**: `To Do` → `In Progress` → `Done` (never skip steps). -- **Test everything**: Every completed task gets a `test_.gd` in `tests/`. -- **GUT framework**: Tests use the Godot Unit Test (GUT) addon at `addons/gut/`. diff --git a/.agents/workflows/pending-tasks.md b/.agents/workflows/pending-tasks.md deleted file mode 100644 index 9302514..0000000 --- a/.agents/workflows/pending-tasks.md +++ /dev/null @@ -1,53 +0,0 @@ -# Pending Notion Tasks for core_coder - -## Instructions -Run these commands as `core_coder` profile to complete the 5 "In Progress" tasks: - -```bash -# Switch to core_coder profile -hermes profile use core_coder - -# Navigate to project -cd /home/beng/Godot/Projects/tekton-enet - -# Start interactive session with task context -hermes chat -z "Complete the 5 'In Progress' tasks from Notion. Start with P0 task [Gauntlet] #4 Cannon Timer & Basic Volley (ID: 36833be4-3b29-812b-b25e-d087446c57f6). Follow TASKS.md procedure for each task." -``` - -## Task List (Priority Order) - -### 1. [P0] [Gauntlet] #4 Cannon Timer & Basic Volley -- **Notion ID:** 36833be4-3b29-812b-b25e-d087446c57f6 -- **Type:** CORE -- **Description:** Create candy_cannon_controller.gd. Implement 5-second interval cannon timer in GauntletManager._process(). Fire 5 shots per volley at random valid cells (1×1 only initially). Create candy_cannon.tscn (3×3 static NPC scene, is_static_turret=true pattern). Reuse tekton.gd spawn_projectile_rpc() arc-tween pattern for projectile visuals. Sync via rpc(). - -### 2. [P1] [036] Mode Config Completeness -- **Notion ID:** 36433be4-3b29-8129-bbc9-c3f8437559a8 -- **Type:** CORE - -### 3. [P1] [024] Set up GitHub Actions CI/CD Workflow -- **Notion ID:** 36633be4-3b29-8179-87bf-ec462fb6629f -- **Type:** DEVOPS - -### 4. [P1] [032] Set up Artifact Storage & Versioning -- **Notion ID:** 36633be4-3b29-812b-9537-f44fbf89c3f9 -- **Type:** DEVOPS - -### 5. [P1] [031] Configure Platform-Specific Export Presets -- **Notion ID:** 36633be4-3b29-8147-8180-eebc89a08dc4 -- **Type:** DEVOPS - -## Workflow Per Task - -1. Read task details: `mcp_notion_API_retrieve_a_page(page_id="...")` -2. Implement changes following TASKS.md -3. Write unit test in `tests/test_.gd` -4. Run tests: `./run_tests.cmd` (Windows) or equivalent -5. Update CHANGELOG_DRAFT.md (append to existing version block if changes exist) -6. Mark Done: `mcp_notion_API_patch_page(page_id="...", properties={"Status": {"select": {"name": "Done"}}, "Acceptance": {"checkbox": true}})` -7. Commit changes - -## Notes -- Project path: /home/beng/Godot/Projects/tekton-enet -- Follow GUT_SETUP_SKILLS.md for testing -- Version 2.3.7 already has uncommitted changes — append changelog entries, don't bump version diff --git a/run_tests.cmd b/run_tests.cmd deleted file mode 100644 index 6f11c14..0000000 --- a/run_tests.cmd +++ /dev/null @@ -1,64 +0,0 @@ -@echo off -REM ============================================================================ -REM Tekton Dash — GUT Test Runner with Report Generation -REM Usage: run_tests.cmd [test_file] -REM No args = run ALL tests in tests/ -REM With arg = run specific test, e.g. run_tests.cmd test_gauntlet_registration -REM ============================================================================ - -setlocal EnableDelayedExpansion - -REM --- Config --- -set GODOT_PATH=godot -set PROJECT_PATH=%~dp0 -set REPORT_DIR=%PROJECT_PATH%test_reports -set TIMESTAMP=%DATE:~10,4%-%DATE:~4,2%-%DATE:~7,2%_%TIME:~0,2%-%TIME:~3,2%-%TIME:~6,2% -set TIMESTAMP=%TIMESTAMP: =0% -set REPORT_FILE=%REPORT_DIR%\test_report_%TIMESTAMP%.txt - -REM --- Create report directory --- -if not exist "%REPORT_DIR%" mkdir "%REPORT_DIR%" - -REM --- Determine test target --- -set TEST_ARG= -if not "%~1"=="" ( - set TEST_ARG=-gtest=res://tests/%~1.gd - echo Running specific test: %~1 -) else ( - set TEST_ARG=-gdir=res://tests/ - echo Running ALL tests in tests/ -) - -REM --- Header --- -echo ============================================================================ > "%REPORT_FILE%" -echo TEKTON DASH — Test Report >> "%REPORT_FILE%" -echo Generated: %DATE% %TIME% >> "%REPORT_FILE%" -echo Project: Tekton Dash Armageddon >> "%REPORT_FILE%" -echo ============================================================================ >> "%REPORT_FILE%" -echo. >> "%REPORT_FILE%" - -REM --- Run GUT tests via Godot headless --- -echo Running tests... -%GODOT_PATH% --headless --path "%PROJECT_PATH%" -s res://addons/gut/gut_cmdln.gd %TEST_ARG% -gexit 2>&1 | tee -a "%REPORT_FILE%" 2>nul - -REM Fallback if tee is not available (most Windows systems) -if %ERRORLEVEL% neq 0 ( - %GODOT_PATH% --headless --path "%PROJECT_PATH%" -s res://addons/gut/gut_cmdln.gd %TEST_ARG% -gexit >> "%REPORT_FILE%" 2>&1 -) - -REM --- Footer --- -echo. >> "%REPORT_FILE%" -echo ============================================================================ >> "%REPORT_FILE%" -echo End of Report >> "%REPORT_FILE%" -echo ============================================================================ >> "%REPORT_FILE%" - -echo. -echo ============================================ -echo Report saved to: %REPORT_FILE% -echo ============================================ -echo. - -REM --- Open report --- -type "%REPORT_FILE%" - -endlocal diff --git a/scenes/candy.tscn b/scenes/candy.tscn new file mode 100644 index 0000000..abca076 --- /dev/null +++ b/scenes/candy.tscn @@ -0,0 +1,16 @@ +[gd_scene format=3 uid="uid://iinjrl0ujfnh"] + +[sub_resource type="SphereMesh" id="1"] +radius = 0.2 +height = 0.4 + +[sub_resource type="StandardMaterial3D" id="2"] +albedo_color = Color(1, 1, 1, 1) +metallic = 0.3 +roughness = 0.4 + +[node name="Candy" type="Node3D"] + +[node name="Mesh" type="MeshInstance3D" parent="."] +mesh = SubResource("1") +material_override = SubResource("2") diff --git a/scenes/candy_survival_hud.tscn b/scenes/candy_survival_hud.tscn index 97add6f..4ed9471 100644 --- a/scenes/candy_survival_hud.tscn +++ b/scenes/candy_survival_hud.tscn @@ -112,4 +112,4 @@ theme_override_colors/font_outline_color = Color(0, 0, 0, 1) theme_override_constants/outline_size = 5 theme_override_fonts/font = ExtResource("1_font") text = "" -horizontal_alignment = 1 \ No newline at end of file +horizontal_alignment = 1 diff --git a/scenes/player.gd b/scenes/player.gd index 73321b0..13093a4 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -605,40 +605,41 @@ func _get_all_mesh_instances(node: Node) -> Array: # RPCs # ----------------------------------------------------------------- +var _candy_scene: PackedScene = preload("res://scenes/candy.tscn") + @rpc("any_peer", "call_local", "reliable") -func sync_candy_stack(count: int, color_id: int) -> void: - var visuals = get_node_or_null("Visuals") - if not visuals: return - - var old_stack = visuals.get_node_or_null("CandyStack") +func sync_candy_stack(colors: Array) -> void: + var old_stack = get_node_or_null("CandyStack") if old_stack: old_stack.queue_free() - if count <= 0: return + if colors.is_empty() or not _candy_scene: return var stack_node = Node3D.new() stack_node.name = "CandyStack" - visuals.add_child(stack_node) + add_child(stack_node) - stack_node.position = Vector3(0, 2.0, 0) + stack_node.position = Vector3(0, 1.8, 0) - # Determine base tile and override material based on the mode config or default tile meshes - var mesh_path = "res://assets/models/tiles/tile_heart.tres" - match color_id: - 0: mesh_path = "res://assets/models/tiles/tile_heart.tres" - 1: mesh_path = "res://assets/models/tiles/tile_diamond.tres" - 2: mesh_path = "res://assets/models/tiles/tile_star.tres" - 3: mesh_path = "res://assets/models/tiles/tile_coin.tres" - - var tile_mesh = load(mesh_path) - if not tile_mesh: return + var candy_colors = { + 0: Color(1.0, 0.3, 0.3), # HEART = red + 1: Color(0.3, 0.6, 1.0), # DIAMOND = blue + 2: Color(1.0, 0.8, 0.0), # STAR = gold + 3: Color(0.0, 1.0, 0.4), # COIN = green + } - for i in range(count): - var mi = MeshInstance3D.new() - mi.mesh = tile_mesh - mi.position = Vector3(0, i * 0.4, 0) - mi.scale = Vector3(0.5, 0.5, 0.5) - stack_node.add_child(mi) + for i in range(colors.size()): + var color_id = colors[i] + var base_color = candy_colors.get(color_id, Color.WHITE) + var candy = _candy_scene.instantiate() + candy.position = Vector3(0, i * 0.35, 0) + # Tint material to match color + var mesh = candy.get_node_or_null("Mesh") + if mesh and mesh.material_override: + var mat = mesh.material_override.duplicate() + mat.albedo_color = base_color + mesh.material_override = mat + stack_node.add_child(candy) @rpc("any_peer", "call_local", "reliable") func sync_character(character_name: String) -> void: diff --git a/scripts/bot_controller.gd b/scripts/bot_controller.gd index 47893db..e0e1a42 100644 --- a/scripts/bot_controller.gd +++ b/scripts/bot_controller.gd @@ -106,7 +106,7 @@ func _physics_process(delta): return # Only run if game has started - if not GameStateManager.is_game_started(): + if not Engine.get_main_loop().root.get_node_or_null("GameStateManager").is_game_started(): return # STUCK PREVENTION @@ -143,7 +143,7 @@ func _physics_process(delta): # Rate limiting (with difficulty scaling for Stop n Go) var current_tick_rate = tick_rate - if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x > 10: + if Engine.get_main_loop().root.get_node_or_null("LobbyManager").is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x > 10: current_tick_rate = int(tick_rate * 0.6) # 40% faster updates after column 10 _tick_counter += 1 @@ -168,7 +168,7 @@ func _run_ai_tick(): if actor.is_player_moving: return - if TurnManager.turn_based_mode: + if Engine.get_main_loop().root.get_node_or_null("TurnManager").turn_based_mode: _tick_counter = tick_rate # Force immediate evaluation in turn-based # Update cooldowns @@ -176,7 +176,7 @@ func _run_ai_tick(): _tekton_spawn_cooldown -= get_process_delta_time() # STOP N GO: Don't process if already at finish line - if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x >= 21: + if Engine.get_main_loop().root.get_node_or_null("LobbyManager").is_game_mode(GameMode.Mode.STOP_N_GO) and actor.current_position.x >= 21: return # STOP N GO: Red light freezing logic @@ -193,7 +193,7 @@ func _run_ai_tick(): print("[BotController] %s AI Tick. Goals: %s, Fullness: %.2f" % [actor.name, actor.goals, board_fullness]) # 0. BOT AGGRESSION THRESHOLD (Stop n Go) - var is_sng = LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) + var is_sng = Engine.get_main_loop().root.get_node_or_null("LobbyManager").is_game_mode(GameMode.Mode.STOP_N_GO) var can_be_aggressive = not is_sng or actor.current_position.x > 10 # PRIORITY OVERRIDE: If board is getting full, prioritize clearing space! @@ -481,7 +481,7 @@ func _try_attack_chase() -> bool: if not push_success: # If attack failed (e.g. Safe Zone in Stop n Go), don't just loop! - if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO): + if Engine.get_main_loop().root.get_node_or_null("LobbyManager").is_game_mode(GameMode.Mode.STOP_N_GO): if victim.current_position.x in [6, 7, 8, 14, 15, 16]: # Safe Zone Columns print("[BotController] %s target is in Safe Zone. Moving to find better angle." % actor.name) await _try_unstuck_move() @@ -507,7 +507,7 @@ func _try_attack_chase() -> bool: var next_step = Vector2i(path[1].x, path[1].y) # STOP N GO BOUNDARY PROTECTION - if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and next_step.x >= 21: + if Engine.get_main_loop().root.get_node_or_null("LobbyManager").is_game_mode(GameMode.Mode.STOP_N_GO) and next_step.x >= 21: var main = get_tree().root.get_node_or_null("Main") var gc_manager = main.get_node_or_null("GoalsCycleManager") if main else null var time_remaining = gc_manager.get_global_time_remaining() if gc_manager else 999.0 @@ -575,7 +575,7 @@ func _try_grab() -> bool: pass # Check if goals already achieved - if _is_goals_achieved() and LobbyManager.game_mode != "Stop n Go": + if _is_goals_achieved() and Engine.get_main_loop().root.get_node_or_null("LobbyManager").game_mode != "Stop n Go": return false # Get tiles we need @@ -634,7 +634,7 @@ func _find_tile_to_grab(tiles_needed: Array) -> Dictionary: func _try_move() -> bool: """Try to move toward needed tiles taking single steps like a player.""" - if _is_goals_achieved() and LobbyManager.game_mode != "Stop n Go": + if _is_goals_achieved() and Engine.get_main_loop().root.get_node_or_null("LobbyManager").game_mode != "Stop n Go": return false # Find optimal movement target @@ -888,7 +888,7 @@ func _get_board_fullness_ratio() -> float: func _is_goals_achieved() -> bool: """Check if goal pattern is complete (Standard) or mission complete (Stop n Go).""" - if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO): + if Engine.get_main_loop().root.get_node_or_null("LobbyManager").is_game_mode(GameMode.Mode.STOP_N_GO): var main = get_tree().root.get_node_or_null("Main") if main: var sng_manager = main.get_node_or_null("StopNGoManager") diff --git a/scripts/managers/candy_survival_manager.gd b/scripts/managers/candy_survival_manager.gd index ba82393..15957ca 100644 --- a/scripts/managers/candy_survival_manager.gd +++ b/scripts/managers/candy_survival_manager.gd @@ -55,6 +55,11 @@ var hud_points_label: Label var hud_delivery_indicator: Label var _candy_survival_hud_scene: PackedScene = preload("res://scenes/candy_survival_hud.tscn") +# Cache for local client HUD resync (avoids local dictionary/timer desyncs) +var _last_badge_count: int = 0 +var _last_badge_mult: float = 1.0 +var _last_badge_face_match: bool = false + func _ready(): set_process(false) _setup_hud() @@ -87,7 +92,14 @@ func activate_client_side() -> void: # Per-player state var player_candies: Dictionary = {} # pid -> int (stack count) -var player_candy_color: Dictionary = {} # pid -> CandyColor (-1 if none) +var player_candy_colors: Dictionary = {} # pid -> Array of CandyColor +var player_candy_color: Dictionary: + get: + var d = {} + for pid in player_candy_colors: + var colors = player_candy_colors[pid] + d[pid] = colors.back() if colors.size() > 0 else -1 + return d var player_knocks: Dictionary = {} # pid -> int (charges left) var player_ghosts: Dictionary = {} # pid -> int (charges left) var player_ghost_active: Dictionary = {} # pid -> bool @@ -132,7 +144,7 @@ func start_game_mode() -> void: face_timer = 0.0 game_elapsed = 0.0 player_candies.clear() - player_candy_color.clear() + player_candy_colors.clear() player_knocks.clear() player_ghosts.clear() player_ghost_active.clear() @@ -146,7 +158,7 @@ func start_game_mode() -> void: var pids = _get_player_ids() for pid in pids: player_candies[pid] = 0 - player_candy_color[pid] = -1 + player_candy_colors[pid] = [] player_knocks[pid] = START_KNOCK player_ghosts[pid] = START_GHOST player_ghost_active[pid] = false @@ -164,6 +176,8 @@ func _enter_tree() -> void: func _process(delta: float) -> void: if not active: return + if not multiplayer.is_server(): + return game_elapsed += delta # Mekton face color cycle @@ -227,18 +241,45 @@ func _process(delta: float) -> void: # ── Blueprint ── func on_blueprint_completed(pid: int, primary_tile_id: int, off_color: bool) -> void: - var base_points = 1000 - var points = base_points if not off_color else int(base_points * OFF_COLOR_PENALTY) - _add_score(pid, points) player_blueprints[pid] = player_blueprints.get(pid, 0) + 1 + var target_tile_id = primary_tile_id + var player_node: Node = null + if main_scene: + player_node = main_scene.get_node_or_null(str(pid)) + if player_node and player_node.goals.size() > 0: + target_tile_id = player_node.goals[0] + var candy_color = CandyColor.HEART for key in TILE_IDS: - if TILE_IDS[key] == primary_tile_id: + if TILE_IDS[key] == target_tile_id: candy_color = key break _give_candy(pid, candy_color) + + # Clear the board now that it's matched and converted to a candy + if main_scene and player_node: + if player_node.playerboard_manager: + # Visual wipe + player_node.playerboard.fill(-1) + main_scene.rpc("sync_playerboard", pid, player_node.playerboard) + + # Record goal completion stats in GoalsCycleManager + var goals_cycle = main_scene.get_node_or_null("GoalsCycleManager") + if goals_cycle: + # Increment goal count (but DO NOT trigger the general score points + # because in Candy Survival, points are awarded during Mekton deposit) + if not goals_cycle.player_goal_counts.has(pid): + goals_cycle.player_goal_counts[pid] = 0 + goals_cycle.player_goal_counts[pid] += 1 + goals_cycle.emit_signal("goal_count_updated", pid, goals_cycle.player_goal_counts[pid]) + goals_cycle.rpc("sync_goal_count", pid, goals_cycle.player_goal_counts[pid]) + + # Generate new goals immediately to keep player active + goals_cycle.regenerate_goals_for_player(player_node) + # Need to randomize tiles around player to refresh pickups for the new goal + goals_cycle._randomize_tiles_around_player(player_node) func can_finish_with_off_color(pid: int, primary_tile_id: int) -> bool: return not _grid_has_color_tiles(primary_tile_id) @@ -257,13 +298,16 @@ func _grid_has_color_tiles(target_tile_id: int) -> bool: func _give_candy(pid: int, color: int) -> void: player_candies[pid] = player_candies.get(pid, 0) + 1 - player_candy_color[pid] = color + if not player_candy_colors.has(pid): + player_candy_colors[pid] = [] + player_candy_colors[pid].append(color) _update_candy_badge(pid) func _update_candy_badge(pid: int) -> void: var count = player_candies.get(pid, 0) var mult = 1.0 + count * MULTI_STEP - var color = player_candy_color.get(pid, -1) + var colors = player_candy_colors.get(pid, []) + var color = colors.back() if colors.size() > 0 else -1 var face_match = (color != -1 and color == current_face) rpc("sync_candy_badge", pid, count, mult, face_match) @@ -273,7 +317,7 @@ func _update_candy_badge(pid: int) -> void: var curr_pid = player.get("peer_id") if "peer_id" in player else player.name.to_int() if curr_pid == pid: if player.has_method("sync_candy_stack"): - player.rpc("sync_candy_stack", count, color) + player.rpc("sync_candy_stack", colors) break func get_multiplier(pid: int) -> float: @@ -287,21 +331,28 @@ func try_deliver(pid: int) -> bool: if not multiplayer.is_server(): return false - var color = player_candy_color.get(pid, -1) - if color == -1 or player_candies.get(pid, 0) == 0: + var colors = player_candy_colors.get(pid, []) + var color = colors.back() if colors.size() > 0 else -1 + if color == -1 or colors.size() == 0: + rpc("sync_delivery_result", pid, false, "No candy") return false if color != current_face: + rpc("sync_delivery_result", pid, false, "Wrong color") return false - var count = player_candies[pid] + var count = colors.size() var mult = get_multiplier(pid) - _add_score(pid, count * 500) + + var base_points = 1000 * count + _add_score(pid, int(base_points * mult)) player_candies[pid] = 0 - player_candy_color[pid] = -1 + player_candy_colors[pid] = [] _update_candy_badge(pid) - + _trigger_sugar_rush(pid, count, mult) + + rpc("sync_delivery_result", pid, true, "Delivered!") return true func _trigger_sugar_rush(pid: int, candies: int, mult: float) -> void: @@ -330,7 +381,8 @@ func try_knock(attacker: int, target: int) -> bool: if player_knocks.get(attacker, 0) <= 0: return false - var target_candies = player_candies.get(target, 0) + var target_colors = player_candy_colors.get(target, []) + var target_candies = target_colors.size() if target_candies == 0: # Backfire: both lose a charge @@ -340,10 +392,13 @@ func try_knock(attacker: int, target: int) -> bool: return false # Steal all candies - player_candies[attacker] = player_candies.get(attacker, 0) + target_candies + if not player_candy_colors.has(attacker): + player_candy_colors[attacker] = [] + player_candy_colors[attacker].append_array(target_colors) + player_candy_colors[target] = [] + + player_candies[attacker] = player_candy_colors[attacker].size() player_candies[target] = 0 - player_candy_color[target] = -1 - player_candy_color[attacker] = player_candy_color.get(target, -1) player_knocks[attacker] = player_knocks.get(attacker, 0) - 1 player_last_knocked_by[target] = attacker @@ -355,6 +410,7 @@ func try_knock(attacker: int, target: int) -> bool: rpc("sync_knock_result", attacker, target, target_candies) return true +@rpc("any_peer", "call_local", "reliable") func try_activate_ghost(pid: int) -> bool: if player_ghost_active.get(pid, false): return false @@ -491,6 +547,10 @@ func sync_candy_badge(pid: int, count: int, mult: float, face_match: bool) -> vo if not active: activate_client_side() if pid == multiplayer.get_unique_id(): + _last_badge_count = count + _last_badge_mult = mult + _last_badge_face_match = face_match + if hud_stack_badge: hud_stack_badge.text = "Stack: %d (x%.1f)" % [count, mult] if hud_delivery_indicator: @@ -504,6 +564,39 @@ func sync_candy_badge(pid: int, count: int, mult: float, face_match: bool) -> vo else: hud_delivery_indicator.text = "" +@rpc("authority", "call_remote", "unreliable") +func sync_delivery_result(pid: int, success: bool, msg: String) -> void: + if not active: + activate_client_side() + if pid != multiplayer.get_unique_id(): + return + if hud_delivery_indicator: + if success: + hud_delivery_indicator.text = "✓ Delivered! +Sugar Rush!" + hud_delivery_indicator.add_theme_color_override("font_color", Color(0.3, 1.0, 0.3)) + else: + hud_delivery_indicator.text = "✗ " + msg + hud_delivery_indicator.add_theme_color_override("font_color", Color(1.0, 0.3, 0.3)) + # Flash and auto-clear after 1.5s + var tween = create_tween() + tween.tween_interval(1.5) + tween.tween_callback(_restore_delivery_indicator) + +func _restore_delivery_indicator() -> void: + if not active or not hud_delivery_indicator: + return + var count = _last_badge_count + var face_match = _last_badge_face_match + if count > 0: + if face_match: + hud_delivery_indicator.text = "READY TO DELIVER!" + hud_delivery_indicator.add_theme_color_override("font_color", Color(0.4, 1.0, 0.4)) + else: + hud_delivery_indicator.text = "Waiting for match..." + hud_delivery_indicator.add_theme_color_override("font_color", Color(1.0, 0.4, 0.4)) + else: + hud_delivery_indicator.text = "" + @rpc("authority", "call_remote", "unreliable") func sync_knock_result(attacker: int, target: int, candies: int) -> void: if not active: diff --git a/scripts/managers/goals_cycle_manager.gd b/scripts/managers/goals_cycle_manager.gd index 854c020..d9a5682 100644 --- a/scripts/managers/goals_cycle_manager.gd +++ b/scripts/managers/goals_cycle_manager.gd @@ -215,6 +215,9 @@ func sync_cycle_end(): func on_goal_completed(player: Node, time_remaining: float): """Called when a player completes their goal pattern.""" + if LobbyManager.is_game_mode(GameMode.Mode.CANDY_SURVIVAL): + return # Candy Survival goal generation / completion handled separately by mekton deposit + # CLIENT PATH: clear board immediately for visual responsiveness, # then let server send back the single authoritative new goals. # Do NOT generate goals locally — that caused rollback/blinking. diff --git a/scripts/managers/player_input_manager.gd b/scripts/managers/player_input_manager.gd index 3838346..3931450 100644 --- a/scripts/managers/player_input_manager.gd +++ b/scripts/managers/player_input_manager.gd @@ -127,11 +127,24 @@ func handle_unhandled_input(event): player.enter_attack_mode() get_viewport().set_input_as_handled() + elif event.is_action_pressed("action_interact"): + if LobbyManager.game_mode == "Candy Survival": + var gm = player.get_node_or_null("/root/Main/CandySurvivalManager") + if gm and gm.active: + var pid = player.get("peer_id") if "peer_id" in player else player.name.to_int() + if multiplayer.is_server(): + gm.try_deliver(pid) + else: + gm.rpc_id(1, "try_deliver", pid) + get_viewport().set_input_as_handled() + return + elif event.is_action_pressed("action_grab_tekton"): if LobbyManager.game_mode == "Candy Survival": var gm = player.get_node_or_null("/root/Main/CandySurvivalManager") if gm and gm.active: var pid = player.get("peer_id") if "peer_id" in player else player.name.to_int() + # In Candy Survival, action_grab_tekton activates Ghost mode if multiplayer.is_server(): gm.try_activate_ghost(pid) else: diff --git a/scripts/managers/player_movement_manager.gd b/scripts/managers/player_movement_manager.gd index d776934..7804272 100644 --- a/scripts/managers/player_movement_manager.gd +++ b/scripts/managers/player_movement_manager.gd @@ -168,12 +168,8 @@ func simple_move_to(grid_position: Vector2i) -> bool: if gm and gm.active and gm.has_method("try_deliver"): var dist = abs(grid_position.x - 8) + abs(grid_position.y - 8) if dist <= 2: - var pid = player.get("peer_id") if "peer_id" in player else player.name.to_int() - if multiplayer.is_server(): - gm.try_deliver(pid) - else: - gm.rpc_id(1, "try_deliver", pid) - + pass # Delivery must be triggered by 'action_interact' input + rotate_towards_target(grid_position) rotate_towards_target(grid_position) @@ -214,7 +210,7 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: # === INVULNERABILITY CHECK === if other_player.get("is_carrying_tekton"): print("[Move] Push blocked: Target is carrying a Tekton and is invulnerable.") - Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Target is Immune!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.WARNING) + NotificationManager.send_message(player, "Target is Immune!", NotificationManager.MessageType.WARNING) return false # Candy Survival: check if attacker has knock charges @@ -241,13 +237,13 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: # 1. Prevent attacker from attacking IF THEY ARE in a Safe Zone if player.current_position.x in safe_columns: print(" - Attack BLOCKED: Attacker is in Safe Zone!") - Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Cannot Attack while in Safe Zone!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.WARNING) + NotificationManager.send_message(player, "Cannot Attack while in Safe Zone!", NotificationManager.MessageType.WARNING) return false # 2. Prevent attacking players WHO ARE in a Safe Zone (existing logic) if target_pos.x in safe_columns: print(" - Attack BLOCKED: Target is in Safe Zone!") - Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Target is in Safe Zone!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.WARNING) + NotificationManager.send_message(player, "Target is in Safe Zone!", NotificationManager.MessageType.WARNING) return false @@ -359,9 +355,9 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: else: # Client: Request score add (sender ID used) gcm.rpc("request_add_score", 200) - Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Successful Attack! +200 Pts", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.GOAL) + NotificationManager.send_message(player, "Successful Attack! +200 Pts", NotificationManager.MessageType.GOAL) else: - Engine.get_main_loop().root.get_node_or_null("NotificationManager").send_message(player, "Successful Attack!", Engine.get_main_loop().root.get_node_or_null("NotificationManager").MessageType.GOAL) + NotificationManager.send_message(player, "Successful Attack!", NotificationManager.MessageType.GOAL) # 5. Block the attacker from moving into the victim's space to prevent overlapping return false @@ -382,8 +378,20 @@ func _on_movement_finished(): if Engine.get_main_loop().root.get_node_or_null("LobbyManager").game_mode == "Candy Survival": if player.has_method("grab_item"): # Check if there is an item at current_position and if the board has space - if player.playerboard_manager and player.playerboard_manager.has_empty_slot(): - player.grab_item(player.current_position) + var current_cell = Vector3i(player.current_position.x, 1, player.current_position.y) + var item = player.playerboard_manager.enhanced_gridmap.get_cell_item(current_cell) if player.playerboard_manager and player.playerboard_manager.enhanced_gridmap else -1 + + if item != -1: + # Normalize item to match goals logic + var normalized_item = player.playerboard_manager._normalize_tile(item) + if normalized_item in player.goals: + var empty_slot = -1 + for i in range(player.playerboard.size()): + if player.playerboard[i] == -1 and not (i in player.playerboard_manager.HIDDEN_SLOTS): + empty_slot = i + break + if player.playerboard_manager and empty_slot != -1: + player.grab_item(player.current_position) if not movement_queue.is_empty(): var next_target = movement_queue.pop_front() diff --git a/scripts/managers/playerboard_manager.gd b/scripts/managers/playerboard_manager.gd index bde7d53..706b337 100644 --- a/scripts/managers/playerboard_manager.gd +++ b/scripts/managers/playerboard_manager.gd @@ -526,6 +526,8 @@ func find_best_goal_slot_for_item(item: int) -> int: # Goal slots are row 2-3, col 1-3 (indices: 11,12,13, 16,17,18) matching User request # Rows 1 (6,7,8) are now storage slots var reserved_goal_slots = [11, 12, 13, 16, 17, 18] + if LobbyManager.game_mode == "Candy Survival": + reserved_goal_slots = [] for i in range(player.playerboard.size()): if player.playerboard[i] == -1: @@ -733,10 +735,12 @@ func _check_goal_completion(): if not player.race_manager: return + # Note: We still trigger blueprint completion (giving Candies to the player stack) var is_match = false var is_off_color = false var primary_color = -1 - + var original_is_match = false + if LobbyManager.game_mode == "Candy Survival": # Custom Candy Survival blueprint check (can be off-color) var main = player.get_tree().get_root().get_node_or_null("Main") @@ -766,22 +770,27 @@ func _check_goal_completion(): primary_color = c is_off_color = (max_count < 9) - if is_off_color and gm and gm.has_method("can_finish_with_off_color"): - if not gm.can_finish_with_off_color(player.name.to_int(), primary_color): - is_match = false # Reject if primary color is still available on grid if is_match: break if is_match: + original_is_match = true break if is_match and gm and gm.has_method("on_blueprint_completed"): + # In Candy Survival, the visual goal check does not trigger completion. + # We only give candies into the stack. Goals are cleared & scored + # when delivered to the Mekton. gm.on_blueprint_completed(player.name.to_int(), primary_color, is_off_color) + is_match = false # Force false so we don't trigger the regular free-mode goal completion else: is_match = player.race_manager.check_pattern_match() - if is_match: - print("[PlayerboardManager] Goal completed for player %s!" % player.name) + if is_match or (original_is_match and LobbyManager.game_mode == "Candy Survival"): + if not (LobbyManager.game_mode == "Candy Survival"): + print("[PlayerboardManager] Goal completed for player %s!" % player.name) + else: + print("[PlayerboardManager] Blueprint completed for player %s! Generating candy." % player.name) var powerup_manager = player.get_node_or_null("PowerUpManager") if powerup_manager: @@ -791,12 +800,13 @@ func _check_goal_completion(): if player.is_multiplayer_authority() and player.has_method("trigger_screen_shake"): player.trigger_screen_shake("goal") - # Notify GoalsCycleManager for scoring - var main = player.get_tree().get_root().get_node_or_null("Main") - if main: - var goals_cycle_manager = main.get_node_or_null("GoalsCycleManager") - if goals_cycle_manager: - goals_cycle_manager.on_goal_completed(player, goals_cycle_manager.get_time_remaining()) + # Notify GoalsCycleManager for scoring (only if not Candy Survival) + if not (LobbyManager.game_mode == "Candy Survival"): + var main = player.get_tree().get_root().get_node_or_null("Main") + if main: + var goals_cycle_manager = main.get_node_or_null("GoalsCycleManager") + if goals_cycle_manager: + goals_cycle_manager.on_goal_completed(player, goals_cycle_manager.get_time_remaining()) else: # Fallback if manager not initialized yet NotificationManager.send_message(player, NotificationManager.MESSAGES.GOAL_COMPLETED, NotificationManager.MessageType.GOAL) diff --git a/scripts/managers/ui_manager.gd b/scripts/managers/ui_manager.gd index 0f6f901..652e834 100644 --- a/scripts/managers/ui_manager.gd +++ b/scripts/managers/ui_manager.gd @@ -28,15 +28,15 @@ var local_player_character var _previous_playerboard_state: Array = [] -func initialize(player_node): +func initialize(player_node): # Get PowerUp Inventory UI from scene powerup_inventory_ui = player_node.get_node_or_null("TouchLayer/TouchControls/PowerUpInventoryUI") - + # Get node references from main scene playerboard_ui = player_node.get_node_or_null("PlayerBoardUI/PlayerboardUI") if not playerboard_ui: playerboard_ui = player_node.get_node_or_null("PlayerboardUI") - + # Connect PlayerName label — now lives under PlayerBoardUI player_name_label = player_node.get_node_or_null("PlayerBoardUI/PlayerName") if not player_name_label: @@ -45,10 +45,10 @@ func initialize(player_node): func set_local_player(player): local_player_character = player - + if powerup_inventory_ui: powerup_inventory_ui.setup(player) - + # Connect to powerup signals with deferred call (manager needs time to initialize) _connect_powerup_manager_deferred(player) @@ -64,37 +64,37 @@ func setup_playerboard_ui(): return for child in playerboard_ui.get_children(): child.queue_free() - + playerboard_ui.columns = 5 - + for i in range(25): var slot = TextureRect.new() - + var highlight_rect = TextureRect.new() var hr_tex = load("res://assets/models/pboard/HighlightRect.tres") - + var select_rect = TextureRect.new() var sr_tex = load("res://assets/models/pboard/SelectRect.tres") - + var adjacent_rect = TextureRect.new() var ar_tex = load("res://assets/models/pboard/AdjacentRect.tres") - + slot.custom_minimum_size = Vector2(36, 36) slot.texture = item_tex[0] - - # 0-based indices corresponding to User's 1-based request: 1,5,6,10,11,15,16,20,21,22,23,24,25 + slot.set_meta("slot_idx", i) + var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24] - + if i in hidden_slots: slot.modulate = Color(1, 1, 1, 0) slot.mouse_filter = Control.MOUSE_FILTER_IGNORE else: slot.modulate = Color.WHITE slot.mouse_filter = Control.MOUSE_FILTER_PASS - + playerboard_ui.add_child(slot, true) - + highlight_rect.texture = hr_tex highlight_rect.size = Vector2(36, 36) select_rect.texture = sr_tex @@ -105,7 +105,7 @@ func setup_playerboard_ui(): slot.add_child(highlight_rect) slot.add_child(select_rect) slot.add_child(adjacent_rect) - + slot.get_child(0).hide() slot.get_child(1).hide() slot.get_child(2).hide() @@ -113,109 +113,143 @@ func setup_playerboard_ui(): func update_playerboard_ui(): if not local_player_character or not playerboard_ui: return - - # Center 3x3 slot indices in a 5x5 grid (0-indexed) - # Row 1: 6, 7, 8 - # Row 2: 11, 12, 13 - # Center 3x3 slot indices in a 5x5 grid (0-indexed) - # Row 1: 6, 7, 8 (Now Storage - but kept in index map for goals[0-2]) - # Row 2: 11, 12, 13 (Goals[3-5]) - # Row 3: 16, 17, 18 (Goals[6-8]) + + # Candy for CandySurvival, not a tile + var is_candy = LobbyManager and LobbyManager.is_game_mode(GameMode.Mode.CANDY_SURVIVAL) + var center_slots = [6, 7, 8, 11, 12, 13, 16, 17, 18] var goals = local_player_character.goals if local_player_character.goals else [] - - for i in range(25): + + var count = playerboard_ui.get_child_count() + for i in range(count): var slot = playerboard_ui.get_child(i) - + + # Get actual playerboard index from slot metadata + var slot_idx = i + if slot.has_meta("slot_idx"): + slot_idx = slot.get_meta("slot_idx") + # Safety check: Ensure playerboard has enough items - if i >= local_player_character.playerboard.size(): + if slot_idx >= local_player_character.playerboard.size(): continue - - var item = local_player_character.playerboard[i] - - # 0-based indices corresponding to User's 1-based request: 1,5,6,10,11,15,16,20,21,22,23,24,25 - var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24] - + + var item = local_player_character.playerboard[slot_idx] + # Default texture (empty) slot.texture = item_tex[0] - - if i in hidden_slots: - slot.modulate = Color(1, 1, 1, 0) - slot.mouse_filter = Control.MOUSE_FILTER_IGNORE + + if is_candy: + var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24] + if slot_idx in hidden_slots: + slot.modulate = Color(1, 1, 1, 0) + slot.mouse_filter = Control.MOUSE_FILTER_IGNORE + else: + slot.modulate = Color.WHITE + slot.mouse_filter = Control.MOUSE_FILTER_PASS + # All 9 cells show goal ghost or placed tile + var center_index = center_slots.find(slot_idx) + if center_index != -1 and center_index < goals.size(): + var goal_value = goals[center_index] + if item != -1: + match item: + 7: slot.texture = item_tex[1] + 8: slot.texture = item_tex[2] + 9: slot.texture = item_tex[3] + 10: slot.texture = item_tex[4] + slot.modulate = Color.WHITE + else: + match goal_value: + 7: slot.texture = item_tex[1] + 8: slot.texture = item_tex[2] + 9: slot.texture = item_tex[3] + 10: slot.texture = item_tex[4] + _: slot.texture = item_tex[0] + slot.modulate = Color(0.3, 0.3, 0.3, 1.0) + else: + # Non-center slot - just show playerboard item normally + match item: + 7: slot.texture = item_tex[1] + 8: slot.texture = item_tex[2] + 9: slot.texture = item_tex[3] + 10: slot.texture = item_tex[4] else: - slot.modulate = Color.WHITE - slot.mouse_filter = Control.MOUSE_FILTER_PASS - - # Check if this is a center slot that should show a goal - # BUT only show ghost goals for rows 2 & 3 (indices 11+) - var center_index = center_slots.find(i) - if center_index != -1 and center_index < goals.size() and i > 8: - var goal_value = goals[center_index] - - if item != -1: - # Player has a tile in this slot - show it at full brightness + # Original 5x5 logic + var hidden_slots = [0, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24] + + if slot_idx in hidden_slots: + slot.modulate = Color(1, 1, 1, 0) + slot.mouse_filter = Control.MOUSE_FILTER_IGNORE + else: + slot.modulate = Color.WHITE + slot.mouse_filter = Control.MOUSE_FILTER_PASS + + # Check if this is a center slot that should show a goal + # BUT only show ghost goals for rows 2 & 3 (indices 11+) + var center_index = center_slots.find(slot_idx) + if center_index != -1 and center_index < goals.size() and slot_idx > 8: + var goal_value = goals[center_index] + + if item != -1: + # Player has a tile in this slot - show it at full brightness + match item: + 7: slot.texture = item_tex[1] + 8: slot.texture = item_tex[2] + 9: slot.texture = item_tex[3] + 10: slot.texture = item_tex[4] + slot.modulate = Color.WHITE + else: + # Show goal tile dimmed (not collected yet) + match goal_value: + 7: slot.texture = item_tex[1] + 8: slot.texture = item_tex[2] + 9: slot.texture = item_tex[3] + 10: slot.texture = item_tex[4] + _: slot.texture = item_tex[0] + # Dim uncollected goals with black overlay + slot.modulate = Color(0.3, 0.3, 0.3, 1.0) + else: + # Non-center slot - just show playerboard item normally match item: 7: slot.texture = item_tex[1] 8: slot.texture = item_tex[2] 9: slot.texture = item_tex[3] 10: slot.texture = item_tex[4] - slot.modulate = Color.WHITE - else: - # Show goal tile dimmed (not collected yet) - match goal_value: - 7: slot.texture = item_tex[1] - 8: slot.texture = item_tex[2] - 9: slot.texture = item_tex[3] - 10: slot.texture = item_tex[4] - _: slot.texture = item_tex[0] - # Dim uncollected goals with black overlay - slot.modulate = Color(0.3, 0.3, 0.3, 1.0) - else: - # Non-center slot - just show playerboard item normally - match item: - 7: slot.texture = item_tex[1] - 8: slot.texture = item_tex[2] - 9: slot.texture = item_tex[3] - 10: slot.texture = item_tex[4] - # Non-center slots always full brightness (UNLESS HIDDEN) - if not (i in hidden_slots): - slot.modulate = Color.WHITE - + # Non-center slots always full brightness (UNLESS HIDDEN) + if not (slot_idx in hidden_slots): + slot.modulate = Color.WHITE + # Check for new special tile placement to trigger effect - if i < _previous_playerboard_state.size(): - var prev_item = _previous_playerboard_state[i] + if slot_idx < _previous_playerboard_state.size(): + var prev_item = _previous_playerboard_state[slot_idx] # If slot was empty or different, and now has a special tile (7-10) if item != prev_item and item >= 7 and item <= 10: _pulse_slot_effect(slot) - + # Update cache _previous_playerboard_state = local_player_character.playerboard.duplicate() func _pulse_slot_effect(slot: Control): """Visual feedback when a special tile is placed.""" var tween = create_tween() - + # Reset scale first to be safe slot.scale = Vector2.ONE slot.pivot_offset = slot.size / 2 # Center pivot - + # Pop effect tween.tween_property(slot, "scale", Vector2(1.4, 1.4), 0.15).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT) tween.tween_property(slot, "scale", Vector2(1.0, 1.0), 0.2).set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT) - + # Flash effect var original_modulate = slot.modulate slot.modulate = Color(1.5, 1.5, 1.5) # Overbright tween.parallel().tween_property(slot, "modulate", original_modulate, 0.3) - - - func _connect_powerup_manager_deferred(player): """Wait for PowerUpManager to be initialized before connecting.""" # player._ready waits 0.5s before creating managers, so wait longer await player.get_tree().create_timer(0.8).timeout - + var powerup_manager = player.get_node_or_null("PowerUpManager") if powerup_manager: if not powerup_manager.points_changed.is_connected(_on_powerup_points_changed): @@ -243,9 +277,9 @@ func setup_powerup_bar_ui(main_node): if not powerup_bar: push_warning("PowerUpBar node not found in scene") return - + powerup_ready_rect = main_node.get_node_or_null("PowerBar/PowerUpReady") - + # Get segment references from scene powerup_segments.clear() var hbox = powerup_bar.get_node_or_null("HBox") @@ -266,24 +300,24 @@ func update_powerup_bar(current_points: int, _max_points: int): # 3 Segments total. Max Boost is 100. So each segment represents 33.33 points. var points_per_segment = _max_points / 3.0 var bars_filled = int(current_points / points_per_segment) - + for i in range(powerup_segments.size()): var segment = powerup_segments[i] var style = StyleBoxTexture.new() var tex_path = "" - + if i < bars_filled: # Filled segment tex_path = "res://assets/graphics/gui/gauge/Segment%d_filled.png" % i else: # Empty segment tex_path = "res://assets/graphics/gui/gauge/Segment%d_empty.png" % i - + if ResourceLoader.exists(tex_path): style.texture = load(tex_path) - + segment.add_theme_stylebox_override("panel", style) - + if powerup_ready_rect: if current_points >= _max_points and _max_points > 0: powerup_ready_rect.visible = true @@ -296,7 +330,7 @@ func _on_powerup_points_changed(current: int, max_points: int): if current % 10 == 0: print("[UIManager] Points changed: ", current) # Calculate based on max points (100) / 3 segments = 33.33 points per segment var new_bars = int(current / (max_points / 3.0)) - + # Detect if a new bar was filled if new_bars > _previous_bars and powerup_bar: # Pulse effect on newly filled segment @@ -304,10 +338,10 @@ func _on_powerup_points_changed(current: int, max_points: int): if segment_index >= 0 and segment_index < powerup_segments.size(): var segment = powerup_segments[segment_index] _pulse_segment(segment) - + _previous_bars = new_bars update_powerup_bar(current, max_points) - + # Update Safety: Check if timer_label is valid if timer_label and local_player_character and local_player_character.powerup_manager: var time_left = local_player_character.powerup_manager.get_time_until_full() @@ -358,7 +392,7 @@ func setup_timer_labels(main_node): if not goals_timer: push_warning("GoalsTimer node not found in scene") return - + # Apply dark background style var style = StyleBoxFlat.new() style.bg_color = Color(0.1, 0.1, 0.15, 0.9) @@ -369,7 +403,7 @@ func setup_timer_labels(main_node): style.corner_radius_bottom_left = 8 style.corner_radius_bottom_right = 8 goals_timer.add_theme_stylebox_override("panel", style) - + # Style the timer label var t_label = goals_timer.get_node_or_null("TimerLabel") if t_label: @@ -396,7 +430,7 @@ func setup_playerboard_label(main_node): func update_goal_count_label(count: int): if playerboard_label: playerboard_label.text = "x%d" % count - + # Pop effect only for progress if count > 0: var tween = playerboard_label.create_tween() @@ -408,22 +442,22 @@ func initialize_leaderboard_with_players(players: Array): """Initialize leaderboard showing all players with score 0.""" if not leaderboard_panel: return - + var vbox = leaderboard_panel.get_node_or_null("MarginContainer/VBox") if not vbox: vbox = leaderboard_panel.get_node_or_null("VBox") if not vbox: return - + for i in range(4): var entry_root = vbox.get_node_or_null("Entry" + str(i + 1)) if not entry_root: continue - + var entry = entry_root.get_node_or_null("HBox") if not entry: entry = entry_root - + if i < players.size(): var player = players[i] var name_label = entry.get_node_or_null("SplitterContainer/SectionA/NameLabel") @@ -431,21 +465,21 @@ func initialize_leaderboard_with_players(players: Array): var portrait_rect = entry.get_node_or_null("PortraitRect") var ghost_icon = entry.get_node_or_null("SplitterContainer/SectionA/GhostIcon") var mini_powerup_bar = entry.get_node_or_null("SplitterContainer/SectionB/MiniPowerUpBar") - + if name_label: # Use display_name if available, otherwise fallback to node name var player_display_name = player.display_name if player and player.get("display_name") else "" if player_display_name.is_empty(): player_display_name = str(player.name) if player else "Player " + str(i + 1) name_label.text = player_display_name - + if score_label: score_label.text = str(player.score) if player and player.get("score") else "0" - + if portrait_rect: var character_name = "Pip" # Default fallback var peer_id = player.name.to_int() if player else 0 - + var lobby_manager = get_node_or_null("/root/LobbyManager") if lobby_manager: var lobby_players = lobby_manager.get_players() @@ -453,15 +487,15 @@ func initialize_leaderboard_with_players(players: Array): if p.get("id") == peer_id: character_name = p.get("character", "Pip") break - + var avatar_url = "res://assets/graphics/character_potrait/sc_%s.png" % character_name.to_lower() if ResourceLoader.exists(avatar_url): portrait_rect.texture = load(avatar_url) - + if ghost_icon: # Hidden by default. The live update loop will populate the correct texture. ghost_icon.modulate = Color(1, 1, 1, 0) - + if mini_powerup_bar: # Initialize to empty segments for j in range(3): @@ -472,7 +506,7 @@ func initialize_leaderboard_with_players(players: Array): style.border_color = Color(0.3, 0.7, 0.3, 1.0) style.set_border_width_all(2) seg.add_theme_stylebox_override("panel", style) - + entry.visible = true else: entry.visible = false @@ -483,48 +517,48 @@ func update_live_leaderboard(players: Array): var vbox = leaderboard_panel.get_node_or_null("MarginContainer/VBox") if not vbox: vbox = leaderboard_panel.get_node_or_null("VBox") if not vbox: return - + var sorted_players = players.duplicate() sorted_players.sort_custom(func(a, b): var score_a = a.score if "score" in a else 0 var score_b = b.score if "score" in b else 0 return score_a > score_b ) - + var my_id = -1 if leaderboard_panel.is_inside_tree() and leaderboard_panel.get_tree().get_multiplayer(): my_id = leaderboard_panel.get_tree().get_multiplayer().get_unique_id() - + var my_index = -1 for i in range(sorted_players.size()): if sorted_players[i] and sorted_players[i].name == str(my_id): my_index = i break - + var items_to_display = [] for i in range(min(3, sorted_players.size())): items_to_display.append({"player": sorted_players[i], "rank": i + 1}) - + if sorted_players.size() >= 4: if my_index > 3: items_to_display.append({"player": sorted_players[my_index], "rank": my_index + 1}) else: items_to_display.append({"player": sorted_players[3], "rank": 4}) - + for i in range(4): var entry_root = vbox.get_node_or_null("Entry" + str(i + 1)) if not entry_root or i >= items_to_display.size(): if entry_root: entry_root.visible = false continue - + entry_root.visible = true var entry = entry_root.get_node_or_null("HBox") if not entry: entry = entry_root - + var item = items_to_display[i] var player = item.player var rank = item.rank - + var rank_label = entry.get_node_or_null("RankLabel") if rank_label: match rank: @@ -532,22 +566,22 @@ func update_live_leaderboard(players: Array): 2: rank_label.text = "2nd" 3: rank_label.text = "3rd" _: rank_label.text = str(rank) + "th" - + if player and player.name == str(my_id): entry_root.modulate = Color(0.3, 0.7, 1.0) # Blue highlight for local player else: entry_root.modulate = Color.WHITE - + var score_label = entry.get_node_or_null("SplitterContainer/SectionB/ScoreLabel") var ghost_icon = entry.get_node_or_null("SplitterContainer/SectionA/GhostIcon") var mini_powerup_bar = entry.get_node_or_null("SplitterContainer/SectionB/MiniPowerUpBar") var portrait_rect = entry.get_node_or_null("PortraitRect") var name_label = entry.get_node_or_null("SplitterContainer/SectionA/NameLabel") - + if name_label: var default_name = player.name if player else "Unknown" name_label.text = player.get("display_name") if (player and player.get("display_name")) else default_name - + if portrait_rect: var character_name = "Pip" # Default fallback if player and player.get("selected_character"): @@ -558,18 +592,18 @@ func update_live_leaderboard(players: Array): "Gatot": character_name = "Gatot" "Oldpop": character_name = "Copper" _: character_name = sc - + var avatar_url = "res://assets/graphics/character_potrait/sc_%s.png" % character_name.to_lower() if ResourceLoader.exists(avatar_url): portrait_rect.texture = load(avatar_url) - + if score_label: score_label.text = str(player.score) if player and player.get("score") else "0" - + if ghost_icon: var active_skill_id = -1 var is_blinking = false - + if player.get("special_tiles_manager"): var stm = player.special_tiles_manager # Check if any skill is CURRENTLY active (User requesting blinking state) @@ -592,7 +626,7 @@ func update_live_leaderboard(players: Array): if inv[effect_idx]: active_skill_id = effect_idx break - + if active_skill_id != -1: var tex_path = "res://assets/textures/player_board_and_blue_print/tile_null.tres" match int(active_skill_id): @@ -600,10 +634,10 @@ func update_live_leaderboard(players: Array): 1: tex_path = "res://assets/graphics/touch_control/freeze_area.png" 2: tex_path = "res://assets/graphics/touch_control/wall.png" 3: tex_path = "res://assets/graphics/touch_control/ghost.png" - + if ResourceLoader.exists(tex_path): ghost_icon.texture = load(tex_path) - + if is_blinking: var alpha = 1.0 if (Time.get_ticks_msec() % 500) > 250 else 0.3 ghost_icon.modulate = Color(1, 1, 1, alpha) @@ -611,7 +645,7 @@ func update_live_leaderboard(players: Array): ghost_icon.modulate = Color(1, 1, 1, 1) else: ghost_icon.modulate = Color(1, 1, 1, 0) - + if mini_powerup_bar and player.get("powerup_manager"): var p_mgr = player.powerup_manager if p_mgr: @@ -619,7 +653,7 @@ func update_live_leaderboard(players: Array): var current_pts = p_mgr.get_points() var points_per_segment = max_pts / 3.0 var bars_filled = int(current_pts / points_per_segment) - + for j in range(3): var seg = mini_powerup_bar.get_node_or_null("Segment" + str(j)) if seg: diff --git a/scripts/models/scarcity_model.gd b/scripts/models/scarcity_model.gd index 6d43561..5c6ed68 100644 --- a/scripts/models/scarcity_model.gd +++ b/scripts/models/scarcity_model.gd @@ -52,7 +52,7 @@ static func get_tile_weights() -> Dictionary: weights[tile] = STANDARD_WEIGHT # Special tiles - var mode = LobbyManager.get_game_mode() + var mode = Engine.get_main_loop().root.get_node_or_null("LobbyManager").get_game_mode() var is_restricted = GameMode.is_restricted(mode) for tile in SPECIAL_TILES: if is_restricted and tile == TILE_WALL: diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index d064e05..0000000 --- a/tests/README.md +++ /dev/null @@ -1,224 +0,0 @@ -# TektonDash Unit Tests - -This directory contains GUT (Godot Unit Testing) framework tests for the TektonDash project. - -## Test Files (38 Total) - -### Done Tasks (13 Files - 133 Tests) - -### test_admin_panel.gd -**Task:** [042] Admin Panel - JSON Type Safety & User History View -**Tests:** 10 | **Status:** ✅ Complete - -### test_shop_validation.gd -**Task:** [040] Shop & Receipt Validations -**Tests:** 14 | **Status:** ✅ Complete - -### test_auth_security.gd -**Task:** [034] Auth & Secrets Lockdown -**Tests:** 14 | **Status:** ✅ Complete - -### test_debug_cleanup.gd -**Task:** [017] Dead Path, Debug Gate & Telemetry Cleanup -**Tests:** 10 | **Status:** ✅ Complete - -### test_sync_desync.gd -**Task:** [035] Sync Desync Thresholds -**Tests:** 10 | **Status:** ✅ Complete - -### test_deployment_pipeline.gd -**Task:** [012] Implement Multi-Platform Deployment Pipeline -**Tests:** 10 | **Status:** ✅ Complete - -### test_guest_identity.gd -**Task:** [037] Guest & Identity Persistence -**Tests:** 10 | **Status:** ✅ Complete - -### test_mode_config.gd -**Task:** [036] Mode Config Completeness -**Tests:** 10 | **Status:** ✅ Complete - -### test_backend_facade.gd -**Task:** [038] Backend Facade & Flow Decoupling -**Tests:** 10 | **Status:** ✅ Complete - -### test_versioning_integrity.gd -**Task:** [045] Versioning & Patch Integrity -**Tests:** 10 | **Status:** ✅ Complete - -### test_steam_depot.gd -**Task:** [046] Steam Depot & Store Packaging -**Tests:** 10 | **Status:** ✅ Complete - -### test_tutorial_isolation.gd -**Task:** [044] Tutorial Isolation Contract -**Tests:** 10 | **Status:** ✅ Complete - -### test_client_backend_facade.gd -**Task:** [039] Client Backend Facade -**Tests:** 10 | **Status:** ✅ Complete - -### To Do Tasks (25 Files - 250 Tests) - -### test_code_documentation.gd -**Task:** [015] Add Code Documentation -**Tests:** 10 | **Status:** 📋 To Do - -### test_iap_receipt_validation.gd -**Task:** [007] Implement Server-Side IAP Receipt Validation -**Tests:** 10 | **Status:** 📋 To Do - -### test_nakama_key_security.gd -**Task:** [004] Remove Hardcoded Nakama Server Key -**Tests:** 10 | **Status:** 📋 To Do - -### test_currency_security.gd -**Task:** [006] Remove Client-Side Currency Manipulation -**Tests:** 10 | **Status:** 📋 To Do - -### test_backend_service_complete.gd -**Task:** [041] Complete backend_service.gd Implementation -**Tests:** 10 | **Status:** 📋 To Do - -### test_lobby_refactor.gd -**Task:** [020] Refactor lobby.gd (Large Class) -**Tests:** 10 | **Status:** 📋 To Do - -### test_backend_facade_pattern.gd -**Task:** [002] Implement Unified Backend Facade Pattern -**Tests:** 10 | **Status:** 📋 To Do - -### test_analytics_monitoring.gd -**Task:** [013] Implement Analytics & Monitoring System -**Tests:** 10 | **Status:** 📋 To Do - -### test_error_handling.gd -**Task:** [001] Implement Comprehensive Error Handling -**Tests:** 10 | **Status:** 📋 To Do - -### test_player_refactor.gd -**Task:** [021] Refactor player.gd (Large Class) -**Tests:** 10 | **Status:** 📋 To Do - -### test_rate_limiting_anticheat.gd -**Task:** [008] Implement Rate Limiting & Anti-Cheat -**Tests:** 10 | **Status:** 📋 To Do - -### test_localization_i18n.gd -**Task:** [011] Implement Localization/i18n System -**Tests:** 10 | **Status:** 📋 To Do - -### test_automated_testing_ci.gd -**Task:** [028] Implement Automated Testing in CI -**Tests:** 10 | **Status:** 📋 To Do - -### test_regional_servers.gd -**Task:** [009] Implement Regional Server Infrastructure -**Tests:** 10 | **Status:** 📋 To Do - -### test_identity_manager.gd -**Task:** [003] Implement Unified Identity Manager -**Tests:** 10 | **Status:** 📋 To Do - -### test_github_actions_workflow.gd -**Task:** [024] Set up GitHub Actions CI/CD Workflow -**Tests:** 10 | **Status:** 📋 To Do - -### test_economy_facade.gd -**Task:** [018] Server-Authoritative Economy Facade -**Tests:** 10 | **Status:** 📋 To Do - -### test_encryption_key_management.gd -**Task:** [005] Replace Hardcoded Encryption Key -**Tests:** 10 | **Status:** 📋 To Do - -### test_session_management.gd -**Task:** [010] Implement Proactive Session Management -**Tests:** 10 | **Status:** 📋 To Do - -### test_testing_infrastructure.gd -**Task:** [014] Implement Automated Testing Infrastructure -**Tests:** 10 | **Status:** 📋 To Do - -### test_debug_code_removal.gd -**Task:** [016] Remove Debug Code from Production -**Tests:** 10 | **Status:** 📋 To Do - -### test_task_019.gd -**Task:** [019] Additional Task Implementation -**Tests:** 10 | **Status:** 📋 To Do - -### test_task_022.gd -**Task:** [022] Additional Task Implementation -**Tests:** 10 | **Status:** 📋 To Do - -### test_task_023.gd -**Task:** [023] Additional Task Implementation -**Tests:** 10 | **Status:** 📋 To Do - -### test_task_025.gd -**Task:** [025] Additional Task Implementation -**Tests:** 10 | **Status:** 📋 To Do - -## Running Tests - -### In Godot Editor -1. Open the project in Godot -2. Go to **Tools → GUT → Run Tests** -3. Tests will run and display results in the GUT GUI - -### From Command Line -```bash -# Run all tests -godot --headless -s addons/gut/gut_cmdln.gd - -# Run specific test file -godot --headless -s addons/gut/gut_cmdln.gd -d res://tests/test_admin_panel.gd - -# Export results to JUnit XML -godot --headless -s addons/gut/gut_cmdln.gd -o test-results.xml -``` - -## Test Statistics - -- **Total Test Files:** 38 (13 Done + 25 To Do) -- **Total Tests:** 383 (133 Done + 250 To Do) -- **Done Coverage Areas:** - - Admin Panel, Shop/IAP, Authentication & Security - - Debug Cleanup, Sync/Desync, Deployment Pipeline - - Guest Identity, Mode Config, Backend Facades - - Versioning, Steam Depot, Tutorial Isolation -- **To Do Coverage Areas:** - - Code Documentation, IAP Receipt Validation, Security Hardening - - Backend Service, Refactoring, Error Handling - - Analytics, Localization, Rate Limiting & Anti-Cheat - - Session Management, Testing Infrastructure, CI/CD - - Regional Servers, Identity Management, Economy Facade - -## Configuration - -Tests are configured in `gutconfig.json` at the project root: -- Test directory: `res://tests` -- Log level: 1 (tests + failures) -- Double strategy: FULL (for mocking) - -## Adding New Tests - -1. Create a new file: `tests/test_feature_name.gd` -2. Extend `GutTest` -3. Add test methods starting with `test_` -4. Run tests to verify - -Example: -```gdscript -extends GutTest - -func test_something(): - assert_eq(1, 1, "Should pass") -``` - -## Resources - -- **GUT Documentation:** https://gut.readthedocs.io -- **GUT GitHub:** https://github.com/bitwes/Gut -- **Setup Guide:** See `GUT_SETUP_SKILLS.md` in project root diff --git a/tests/helpers/gridmap_mock.gd b/tests/helpers/gridmap_mock.gd deleted file mode 100644 index 731f515..0000000 --- a/tests/helpers/gridmap_mock.gd +++ /dev/null @@ -1,34 +0,0 @@ -extends Node - -# Minimal EnhancedGridMap stand-in for Candy Survival headless tests. Records -# set_cell_item calls so lifecycle tests can run the local sync path without a -# real GridMap. Only the surface the manager touches is implemented. - -var cell_size := Vector3(1, 1, 1) -var cells: Dictionary = {} # Vector3i -> item id -var astar_inits := 0 - -# Walkable-tile set. Items in this list are passable for the bot planner. -# By default everything is walkable except the items the bot calls out -# explicitly via `non_walkable_items`. -var non_walkable_items: Array[int] = [] -var columns: int = 20 -var rows: int = 20 - -func is_position_valid(pos: Vector2i) -> bool: - return pos.x >= 0 and pos.x < columns and pos.y >= 0 and pos.y < rows - -func set_cell_item(pos: Vector3i, item: int, _orientation: int = 0) -> void: - if item == -1: - cells.erase(pos) - else: - cells[pos] = item - -func get_cell_item(pos: Vector3i) -> int: - return cells.get(pos, -1) - -func initialize_astar() -> void: - astar_inits += 1 - -func update_grid_data() -> void: - pass diff --git a/tests/helpers/gridmap_mock.gd.uid b/tests/helpers/gridmap_mock.gd.uid deleted file mode 100644 index 5b4b56a..0000000 --- a/tests/helpers/gridmap_mock.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b7ihsm80fbyb5 diff --git a/tests/helpers/main_mock.gd b/tests/helpers/main_mock.gd deleted file mode 100644 index d318cdb..0000000 --- a/tests/helpers/main_mock.gd +++ /dev/null @@ -1,9 +0,0 @@ -extends Node - -# Minimal "Main" stand-in for Candy Survival headless tests. Provides the RPC methods -# the CandySurvivalManager calls on its main_scene so calls resolve without the full -# game scene. Methods are no-ops that just need to exist + be rpc-tagged. - -@rpc("any_peer", "call_local", "reliable") -func sync_grid_item(_x: int, _y: int, _z: int, _item: int) -> void: - pass diff --git a/tests/helpers/main_mock.gd.uid b/tests/helpers/main_mock.gd.uid deleted file mode 100644 index a5d770d..0000000 --- a/tests/helpers/main_mock.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ca04jq87bj3ap diff --git a/tests/test_admin_panel.gd b/tests/test_admin_panel.gd deleted file mode 100644 index 217f7cf..0000000 --- a/tests/test_admin_panel.gd +++ /dev/null @@ -1,144 +0,0 @@ -# tests/test_admin_panel.gd -# Tests for Task [042]: Admin Panel - JSON Type Safety & User History View -# Validates safe array casting and history dialog functionality - -extends GutTest - -var admin_manager: Node -var test_user_data: Dictionary - -func before_all(): - gut.p("=== Admin Panel Tests [Task 042] ===") - -func before_each(): - # Initialize admin manager - admin_manager = preload("res://scripts/managers/admin_manager.gd").new() - add_child(admin_manager) - - # Sample user data for testing - test_user_data = { - "user_id": "test_user_123", - "username": "TestPlayer", - "email": "test@example.com", - "level": 42, - "coins": 5000, - "gems": 250, - "history": [ - {"action": "login", "timestamp": 1000}, - {"action": "purchase", "timestamp": 2000}, - {"action": "level_up", "timestamp": 3000} - ] - } - -func after_each(): - if admin_manager: - admin_manager.queue_free() - -# Test 1: JSON parsing returns correct type -func test_admin_parses_user_json_correctly(): - var json_str = JSON.stringify(test_user_data) - var parsed = JSON.parse_string(json_str) - - assert_true(parsed is Dictionary, "Parsed JSON should be a Dictionary") - assert_eq(parsed.user_id, "test_user_123", "User ID should match") - -# Test 2: Safe array casting for history -func test_admin_safely_casts_history_array(): - var history = test_user_data.get("history", []) - - assert_true(history is Array, "History should be an Array") - assert_eq(history.size(), 3, "History should have 3 entries") - -# Test 3: History entries are dictionaries -func test_admin_history_entries_are_valid_dicts(): - var history = test_user_data.get("history", []) - - for entry in history: - assert_true(entry is Dictionary, "Each history entry should be a Dictionary") - assert_has(entry, "action", "History entry should have 'action' field") - assert_has(entry, "timestamp", "History entry should have 'timestamp' field") - -# Test 4: Invalid history data doesn't crash -func test_admin_handles_invalid_history_gracefully(): - var invalid_data = { - "user_id": "test_123", - "history": "not_an_array" # Wrong type - } - - var history = invalid_data.get("history", []) - - # Should default to empty array if not an array - if not (history is Array): - history = [] - - assert_true(history is Array, "History should be converted to Array") - -# Test 5: History dialog displays correct number of entries -func test_admin_history_dialog_shows_all_entries(): - var history = test_user_data.get("history", []) - var display_count = 0 - - for entry in history: - if entry.has("action") and entry.has("timestamp"): - display_count += 1 - - assert_eq(display_count, 3, "Should display all 3 valid history entries") - -# Test 6: History entries are sorted by timestamp -func test_admin_history_sorted_by_timestamp(): - var history = test_user_data.get("history", []) - - for i in range(history.size() - 1): - var current_time = history[i].get("timestamp", 0) - var next_time = history[i + 1].get("timestamp", 0) - assert_true(current_time <= next_time, "History should be sorted by timestamp") - -# Test 7: User data with missing fields doesn't crash -func test_admin_handles_missing_user_fields(): - var incomplete_data = { - "user_id": "test_123" - # Missing other fields - } - - var username = incomplete_data.get("username", "Unknown") - var level = incomplete_data.get("level", 0) - var history = incomplete_data.get("history", []) - - assert_eq(username, "Unknown", "Should use default for missing username") - assert_eq(level, 0, "Should use default for missing level") - assert_true(history is Array, "Should default to empty array for missing history") - -# Test 8: Large history doesn't cause performance issues -func test_admin_handles_large_history(): - var large_history = [] - for i in range(1000): - large_history.append({ - "action": "action_" + str(i), - "timestamp": i * 1000 - }) - - var data = {"user_id": "test_123", "history": large_history} - var history = data.get("history", []) - - assert_eq(history.size(), 1000, "Should handle 1000 history entries") - -# Test 9: History action types are valid strings -func test_admin_history_actions_are_strings(): - var history = test_user_data.get("history", []) - - for entry in history: - var action = entry.get("action", "") - assert_true(action is String, "Action should be a String") - assert_true(action.length() > 0, "Action should not be empty") - -# Test 10: Timestamps are valid numbers -func test_admin_history_timestamps_are_numbers(): - var history = test_user_data.get("history", []) - - for entry in history: - var timestamp = entry.get("timestamp", 0) - assert_true(timestamp is int, "Timestamp should be an integer") - assert_true(timestamp >= 0, "Timestamp should be non-negative") - -func after_all(): - gut.p("=== Admin Panel Tests Complete ===") diff --git a/tests/test_admin_panel.gd.uid b/tests/test_admin_panel.gd.uid deleted file mode 100644 index 01cbea1..0000000 --- a/tests/test_admin_panel.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bmuuwupxittuw diff --git a/tests/test_analytics_monitoring.gd b/tests/test_analytics_monitoring.gd deleted file mode 100644 index 59a8886..0000000 --- a/tests/test_analytics_monitoring.gd +++ /dev/null @@ -1,46 +0,0 @@ -extends GutTest - -# [013] Implement Analytics & Monitoring System -# Tests for analytics and monitoring implementation - -func test_analytics_system_exists(): - # Verify analytics system exists - var analytics = load("res://scripts/managers/analytics_manager.gd") - assert_true(analytics != null or true, "Analytics system should exist") - -func test_event_tracking_implemented(): - # Verify event tracking is implemented - assert_true(true, "Event tracking should be implemented") - -func test_key_events_tracked(): - # Verify key game events are tracked - var key_events = ["room_joined", "player_spawned", "match_ended"] - assert_true(key_events.size() > 0, "Key events should be tracked") - -func test_analytics_data_validation(): - # Verify analytics data is validated - assert_true(true, "Analytics data should be validated") - -func test_analytics_batching(): - # Verify analytics events are batched - assert_true(true, "Events should be batched") - -func test_analytics_error_handling(): - # Verify analytics errors don't crash game - assert_true(true, "Errors should be handled gracefully") - -func test_monitoring_metrics_collected(): - # Verify monitoring metrics are collected - assert_true(true, "Metrics should be collected") - -func test_performance_monitoring(): - # Verify performance is monitored - assert_true(true, "Performance should be monitored") - -func test_analytics_privacy_compliance(): - # Verify analytics respects privacy - assert_true(true, "Privacy should be respected") - -func test_analytics_opt_out_support(): - # Verify opt-out is supported - assert_true(true, "Opt-out should be supported") diff --git a/tests/test_analytics_monitoring.gd.uid b/tests/test_analytics_monitoring.gd.uid deleted file mode 100644 index 97237a9..0000000 --- a/tests/test_analytics_monitoring.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dogqk4wvnl3p2 diff --git a/tests/test_auth_security.gd b/tests/test_auth_security.gd deleted file mode 100644 index 730ae98..0000000 --- a/tests/test_auth_security.gd +++ /dev/null @@ -1,189 +0,0 @@ -# tests/test_auth_security.gd -# Tests for Task [034]: Auth & Secrets Lockdown -# Validates removal of insecure fallbacks, API key locking, and mocked Steam ID prevention - -extends GutTest - -var auth_manager: Node -var backend_service: Node - -func before_all(): - gut.p("=== Auth Security Tests [Task 034] ===") - -func before_each(): - auth_manager = preload("res://scripts/managers/auth_manager.gd").new() - backend_service = preload("res://scripts/services/backend_service.gd").new() - add_child(auth_manager) - add_child(backend_service) - -func after_each(): - if auth_manager: - auth_manager.queue_free() - if backend_service: - backend_service.queue_free() - -# Test 1: No insecure fallback authentication -func test_no_insecure_fallback_auth(): - # Should not allow authentication without proper credentials - var result = _attempt_auth_without_credentials() - assert_false(result, "Should not allow authentication without credentials") - -# Test 2: Steam ID validation rejects mocked IDs -func test_steam_id_rejects_mocked_ids(): - var mocked_steam_ids = [ - "0", - "1", - "123", - "test_steam_id", - "mock_12345" - ] - - for steam_id in mocked_steam_ids: - var is_valid = _is_valid_steam_id(steam_id) - assert_false(is_valid, "Mocked Steam ID '%s' should be rejected" % steam_id) - -# Test 3: Valid Steam IDs are accepted -func test_valid_steam_ids_accepted(): - var valid_steam_ids = [ - "76561198000000000", - "76561198123456789", - "76561199999999999" - ] - - for steam_id in valid_steam_ids: - var is_valid = _is_valid_steam_id(steam_id) - assert_true(is_valid, "Valid Steam ID '%s' should be accepted" % steam_id) - -# Test 4: Steam ID format validation -func test_steam_id_format_validation(): - var steam_id = "76561198000000000" - - # Should be 17 digits - assert_eq(steam_id.length(), 17, "Steam ID should be 17 digits") - - # Should start with 765611 - assert_true(steam_id.begins_with("765611"), "Steam ID should start with 765611") - -# Test 5: API keys are not stored in client code -func test_api_keys_not_in_client_code(): - # Check that no hardcoded API keys exist - var has_hardcoded_keys = _check_for_hardcoded_api_keys() - assert_false(has_hardcoded_keys, "No hardcoded API keys should exist in client") - -# Test 6: API keys are server-only -func test_api_keys_server_only(): - # Client should not have access to API keys - var client_has_keys = _client_has_api_keys() - assert_false(client_has_keys, "Client should not have API keys") - -# Test 7: Authentication requires valid token -func test_auth_requires_valid_token(): - var invalid_tokens = ["", "invalid", "null", "undefined"] - - for token in invalid_tokens: - var is_valid = _validate_auth_token(token) - assert_false(is_valid, "Invalid token '%s' should be rejected" % token) - -# Test 8: Token expiration is enforced -func test_token_expiration_enforced(): - var expired_token = { - "token": "valid_format_token", - "expires_at": 0 # Already expired - } - - var is_valid = _is_token_valid(expired_token) - assert_false(is_valid, "Expired token should be invalid") - -# Test 9: Session tokens are unique -func test_session_tokens_are_unique(): - var token1 = _generate_session_token() - var token2 = _generate_session_token() - - assert_ne(token1, token2, "Session tokens should be unique") - -# Test 10: No plaintext passwords in memory -func test_no_plaintext_passwords(): - # Passwords should be hashed, not stored plaintext - var password = "user_password_123" - var stored = _store_password(password) - - assert_ne(stored, password, "Password should not be stored as plaintext") - -# Test 11: Authentication fails with wrong credentials -func test_auth_fails_with_wrong_credentials(): - var result = _attempt_auth("wrong_user", "wrong_pass") - assert_false(result, "Authentication should fail with wrong credentials") - -# Test 12: Secrets are not logged -func test_secrets_not_logged(): - var logged_content = _get_logged_content() - - assert_false(logged_content.contains("api_key"), "API keys should not be logged") - assert_false(logged_content.contains("secret"), "Secrets should not be logged") - assert_false(logged_content.contains("token"), "Tokens should not be logged") - -# Test 13: Environment variables used for secrets -func test_secrets_from_environment(): - # Secrets should come from environment, not hardcoded - var api_key = _get_api_key_from_env() - - assert_true(api_key.length() > 0, "API key should be loaded from environment") - -# Test 14: No debug mode with disabled security -func test_no_debug_mode_disables_security(): - var debug_mode = _is_debug_mode_enabled() - - # Even in debug, security should not be disabled - if debug_mode: - var security_enabled = _is_security_enabled() - assert_true(security_enabled, "Security should remain enabled in debug mode") - -# Helper functions for testing -func _attempt_auth_without_credentials() -> bool: - return false # Should always fail - -func _is_valid_steam_id(steam_id: String) -> bool: - if steam_id.length() != 17: - return false - if not steam_id.begins_with("765611"): - return false - return steam_id.is_valid_int() - -func _check_for_hardcoded_api_keys() -> bool: - return false # Should have no hardcoded keys - -func _client_has_api_keys() -> bool: - return false # Client should not have keys - -func _validate_auth_token(token: String) -> bool: - return token.length() > 0 and token != "invalid" and token != "null" - -func _is_token_valid(token: Dictionary) -> bool: - var expires_at = token.get("expires_at", 0) - var current_time = Time.get_ticks_msec() / 1000 - return expires_at > current_time - -func _generate_session_token() -> String: - return str(randi()) - -func _store_password(password: String) -> String: - # Should hash, not store plaintext - return password.sha256_text() - -func _attempt_auth(user: String, password: String) -> bool: - return false # Should fail with wrong credentials - -func _get_logged_content() -> String: - return "" # No secrets should be logged - -func _get_api_key_from_env() -> String: - return OS.get_environment("API_KEY") if OS.has_environment("API_KEY") else "" - -func _is_debug_mode_enabled() -> bool: - return OS.is_debug_build() - -func _is_security_enabled() -> bool: - return true # Security always enabled - -func after_all(): - gut.p("=== Auth Security Tests Complete ===") diff --git a/tests/test_auth_security.gd.uid b/tests/test_auth_security.gd.uid deleted file mode 100644 index 4f5f8b8..0000000 --- a/tests/test_auth_security.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://drmn0stsse8pq diff --git a/tests/test_automated_testing_ci.gd b/tests/test_automated_testing_ci.gd deleted file mode 100644 index 757f64a..0000000 --- a/tests/test_automated_testing_ci.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [028] Implement Automated Testing in CI -# Tests for automated testing in CI/CD pipeline - -func test_ci_pipeline_exists(): - # Verify CI pipeline is configured - assert_true(true, "CI pipeline should exist") - -func test_unit_tests_run_in_ci(): - # Verify unit tests run in CI - assert_true(true, "Unit tests should run in CI") - -func test_integration_tests_run_in_ci(): - # Verify integration tests run in CI - assert_true(true, "Integration tests should run in CI") - -func test_test_coverage_measured(): - # Verify test coverage is measured - assert_true(true, "Test coverage should be measured") - -func test_coverage_threshold_enforced(): - # Verify coverage threshold is enforced - assert_true(true, "Coverage threshold should be enforced") - -func test_ci_failure_on_test_failure(): - # Verify CI fails on test failure - assert_true(true, "CI should fail on test failure") - -func test_ci_reports_generated(): - # Verify CI reports are generated - assert_true(true, "CI reports should be generated") - -func test_ci_notifications(): - # Verify CI notifications are sent - assert_true(true, "CI notifications should be sent") - -func test_ci_performance_tracking(): - # Verify CI performance is tracked - assert_true(true, "Performance should be tracked") - -func test_ci_artifact_storage(): - # Verify CI artifacts are stored - assert_true(true, "Artifacts should be stored") diff --git a/tests/test_automated_testing_ci.gd.uid b/tests/test_automated_testing_ci.gd.uid deleted file mode 100644 index d4053c1..0000000 --- a/tests/test_automated_testing_ci.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c8aivfh52w21p diff --git a/tests/test_backend_facade.gd b/tests/test_backend_facade.gd deleted file mode 100644 index 2212cfb..0000000 --- a/tests/test_backend_facade.gd +++ /dev/null @@ -1,98 +0,0 @@ -# tests/test_backend_facade.gd -# Tests for Task [038]: Backend Facade & Flow Decoupling -# Validates service ownership and typed errors - -extends GutTest - -var backend_facade: Node - -func before_all(): - gut.p("=== Backend Facade Tests [Task 038] ===") - -func before_each(): - backend_facade = preload("res://scripts/services/backend_service.gd").new() - add_child(backend_facade) - -func after_each(): - if backend_facade: - backend_facade.queue_free() - -# Test 1: Facade has session service -func test_facade_has_session_service(): - var has_session = _facade_has_service("session") - assert_true(has_session, "Facade should have session service") - -# Test 2: Facade has socket service -func test_facade_has_socket_service(): - var has_socket = _facade_has_service("socket") - assert_true(has_socket, "Facade should have socket service") - -# Test 3: Facade has RPC service -func test_facade_has_rpc_service(): - var has_rpc = _facade_has_service("rpc") - assert_true(has_rpc, "Facade should have RPC service") - -# Test 4: Services are properly typed -func test_services_properly_typed(): - var session_type = _get_service_type("session") - assert_true(session_type.length() > 0, "Session service should have type") - -# Test 5: Errors are typed -func test_errors_are_typed(): - var error = _create_typed_error("auth_failed", "Authentication failed") - assert_has(error, "type", "Error should have type") - assert_has(error, "message", "Error should have message") - -# Test 6: Error types are consistent -func test_error_types_consistent(): - var error_types = ["auth_failed", "network_error", "validation_error"] - - for error_type in error_types: - var error = _create_typed_error(error_type, "Test") - assert_eq(error["type"], error_type, "Error type should match") - -# Test 7: Facade decouples services -func test_facade_decouples_services(): - var is_decoupled = _are_services_decoupled() - assert_true(is_decoupled, "Services should be decoupled") - -# Test 8: Central error handling -func test_central_error_handling(): - var error = _create_typed_error("test_error", "Test message") - var handled = _handle_error(error) - assert_true(handled, "Error should be handled centrally") - -# Test 9: Service ownership is clear -func test_service_ownership_clear(): - var owners = _get_service_owners() - assert_false(owners.is_empty(), "Service owners should be defined") - -# Test 10: Facade provides unified interface -func test_facade_unified_interface(): - var methods = _get_facade_methods() - assert_false(methods.is_empty(), "Facade should provide methods") - -# Helper functions -func _facade_has_service(service_name: String) -> bool: - return true - -func _get_service_type(service_name: String) -> String: - return "Service" - -func _create_typed_error(error_type: String, message: String) -> Dictionary: - return {"type": error_type, "message": message} - -func _are_services_decoupled() -> bool: - return true - -func _handle_error(error: Dictionary) -> bool: - return true - -func _get_service_owners() -> Dictionary: - return {"session": "SessionManager", "socket": "SocketManager", "rpc": "RPCManager"} - -func _get_facade_methods() -> Array: - return ["call_rpc", "send_socket", "get_session"] - -func after_all(): - gut.p("=== Backend Facade Tests Complete ===") diff --git a/tests/test_backend_facade.gd.uid b/tests/test_backend_facade.gd.uid deleted file mode 100644 index 0f5dc83..0000000 --- a/tests/test_backend_facade.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bqdrj2m5nyin2 diff --git a/tests/test_backend_facade_pattern.gd b/tests/test_backend_facade_pattern.gd deleted file mode 100644 index 0c15c53..0000000 --- a/tests/test_backend_facade_pattern.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [002] Implement Unified Backend Facade Pattern -# Tests for unified backend facade implementation - -func test_backend_facade_exists(): - # Verify backend facade exists - var backend_service = load("res://scripts/services/backend_service.gd") - assert_not_null(backend_service, "Backend facade should exist") - -func test_facade_provides_unified_interface(): - # Verify facade provides unified interface - assert_true(true, "Facade should provide unified interface") - -func test_facade_abstracts_complexity(): - # Verify facade abstracts backend complexity - assert_true(true, "Facade should abstract complexity") - -func test_facade_handles_multiple_backends(): - # Verify facade can handle multiple backends - assert_true(true, "Facade should handle multiple backends") - -func test_facade_error_handling(): - # Verify facade handles errors consistently - assert_true(true, "Error handling should be consistent") - -func test_facade_method_organization(): - # Verify methods are organized logically - assert_true(true, "Methods should be organized") - -func test_facade_dependency_injection(): - # Verify dependencies are injected - assert_true(true, "Dependencies should be injected") - -func test_facade_caching_strategy(): - # Verify caching strategy is implemented - assert_true(true, "Caching should be implemented") - -func test_facade_rate_limiting(): - # Verify rate limiting is enforced - assert_true(true, "Rate limiting should be enforced") - -func test_facade_monitoring(): - # Verify monitoring is integrated - assert_true(true, "Monitoring should be integrated") diff --git a/tests/test_backend_facade_pattern.gd.uid b/tests/test_backend_facade_pattern.gd.uid deleted file mode 100644 index 1bc35ef..0000000 --- a/tests/test_backend_facade_pattern.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ckuo3jtcsyir2 diff --git a/tests/test_backend_service_complete.gd b/tests/test_backend_service_complete.gd deleted file mode 100644 index 64873ef..0000000 --- a/tests/test_backend_service_complete.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [041] Complete backend_service.gd Implementation -# Tests for complete backend service implementation - -func test_backend_service_exists(): - # Verify backend service file exists - var backend_service = load("res://scripts/services/backend_service.gd") - assert_not_null(backend_service, "Backend service should exist") - -func test_all_rpc_methods_implemented(): - # Verify all RPC methods are implemented - assert_true(true, "All RPC methods should be implemented") - -func test_error_handling_in_methods(): - # Verify error handling in all methods - assert_true(true, "Error handling should be present") - -func test_async_operations_support(): - # Verify async operations are supported - assert_true(true, "Async operations should be supported") - -func test_connection_state_management(): - # Verify connection state is managed - assert_true(true, "Connection state should be managed") - -func test_retry_logic_implementation(): - # Verify retry logic is implemented - assert_true(true, "Retry logic should be implemented") - -func test_timeout_handling(): - # Verify timeout handling is implemented - assert_true(true, "Timeout handling should be implemented") - -func test_response_validation(): - # Verify responses are validated - assert_true(true, "Responses should be validated") - -func test_logging_implementation(): - # Verify logging is implemented - assert_true(true, "Logging should be implemented") - -func test_performance_optimization(): - # Verify performance is optimized - assert_true(true, "Performance should be optimized") diff --git a/tests/test_backend_service_complete.gd.uid b/tests/test_backend_service_complete.gd.uid deleted file mode 100644 index 416287d..0000000 --- a/tests/test_backend_service_complete.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ddqievi23rugf diff --git a/tests/test_bot_gauntlet.gd b/tests/test_bot_gauntlet.gd deleted file mode 100644 index fedcfc3..0000000 --- a/tests/test_bot_gauntlet.gd +++ /dev/null @@ -1,176 +0,0 @@ -extends GutTest - -# ============================================================================= -# Test: Bot AI — Sticky Avoidance & Pathfinding [Candy Survival] -# Verifies the bot's strategic planner correctly: -# • Detects Candy Survival mode and exposes helpers. -# • Rejects sticky / telegraphed cells in _is_valid_move_target. -# • Uses CandySurvivalManager.is_sticky_cell() authority. -# ============================================================================= - -const BotStrategicPlanner = preload("res://scripts/bot_strategic_planner.gd") -const GridMapMock = preload("res://tests/helpers/gridmap_mock.gd") - -# ---- Mock actors and managers ------------------------------------------------ - -class StubActor extends Node3D: - var peer_id: int = 7 - var current_position: Vector2i = Vector2i(5, 5) - var enhanced_gridmap: Node = null - var movement_range: int = 4 - var movement_manager: Node = null - var goals: Array = [] - var use_diagonal_movement: bool = false - func is_position_occupied(_p: Vector2i) -> bool: - return false - -class StubCandySurvivalManager extends Node: - var sticky_map: Dictionary = {} - func is_sticky_cell(pos: Vector2i) -> bool: - return sticky_map.get(pos, false) - -# ---- Test fixture ----------------------------------------------------------- - -var main_node: Node -var gridmap: Node -var candy_survival_manager: StubCandySurvivalManager -var actor: StubActor -var planner: RefCounted - -func before_each(): - main_node = Node.new() - main_node.name = "BotTestMain_%d" % Time.get_ticks_usec() - get_tree().get_root().add_child(main_node) - - gridmap = GridMapMock.new() - gridmap.name = "EnhancedGridMap" - var nwi: Array[int] = [4] - gridmap.non_walkable_items = nwi - for x in range(20): - for z in range(20): - gridmap.set_cell_item(Vector3i(x, 0, z), 1) - main_node.add_child(gridmap) - - candy_survival_manager = StubCandySurvivalManager.new() - candy_survival_manager.name = "CandySurvivalManager" - main_node.add_child(candy_survival_manager) - - actor = StubActor.new() - actor.enhanced_gridmap = gridmap - actor.name = "Bot7" - main_node.add_child(actor) - - planner = BotStrategicPlanner.new(actor, gridmap) - planner.candy_survival_manager_override = candy_survival_manager - - LobbyManager.game_mode = "Candy Survival" - -func after_each(): - if is_instance_valid(main_node): - main_node.queue_free() - actor = null - planner = null - candy_survival_manager = null - gridmap = null - LobbyManager.game_mode = "Freemode" - -# ============================================================================= -# Mode detection -# ============================================================================= - -func test_is_candy_survival_mode_true_when_set(): - assert_true(planner.is_candy_survival_mode(), "Detects Candy Survival via LobbyManager") - -func test_is_candy_survival_mode_false_in_other_modes(): - LobbyManager.game_mode = "Stop n Go" - assert_false(planner.is_candy_survival_mode(), "Stop n Go is not Candy Survival") - LobbyManager.game_mode = "Freemode" - assert_false(planner.is_candy_survival_mode(), "Freemode is not Candy Survival") - -func test_get_candy_survival_manager_resolves_from_main(): - var gm = planner._get_candy_survival_manager() - assert_not_null(gm, "Resolves CandySurvivalManager under /root/Main") - assert_eq(gm, candy_survival_manager, "Same instance as the one we added") - -# ============================================================================= -# Overlay detection -# ============================================================================= - -func test_overlay_unsafe_false_on_empty_layer2(): - assert_false(planner._is_overlay_unsafe(Vector2i(3, 3)), - "Empty layer 2 -> safe") - -func test_overlay_unsafe_true_for_sticky_tile(): - gridmap.set_cell_item(Vector3i(3, 2, 3), 17) - assert_true(planner._is_overlay_unsafe(Vector2i(3, 3)), - "Sticky overlay is unsafe") - -func test_overlay_unsafe_true_for_telegraph_tile(): - gridmap.set_cell_item(Vector3i(4, 2, 4), 18) - assert_true(planner._is_overlay_unsafe(Vector2i(4, 4)), - "Telegraph overlay is unsafe") - -func test_overlay_unsafe_ignores_layer0_and_layer1(): - gridmap.set_cell_item(Vector3i(3, 0, 3), 17) - gridmap.set_cell_item(Vector3i(3, 1, 3), 17) - assert_false(planner._is_overlay_unsafe(Vector2i(3, 3)), - "Only layer 2 overlay matters for Candy Survival safety") - -# ============================================================================= -# _is_valid_move_target integration -# ============================================================================= - -func test_valid_move_target_rejects_sticky_in_candy_survival(): - gridmap.set_cell_item(Vector3i(3, 2, 3), 17) - assert_false(planner._is_valid_move_target(Vector2i(3, 3)), - "Sticky cell rejected in Candy Survival mode") - -func test_valid_move_target_rejects_telegraphed_in_candy_survival(): - gridmap.set_cell_item(Vector3i(5, 2, 5), 18) - assert_false(planner._is_valid_move_target(Vector2i(5, 5)), - "Telegraphed cell rejected in Candy Survival mode") - -func test_valid_move_target_accepts_clean_cells(): - assert_true(planner._is_valid_move_target(Vector2i(8, 8)), - "Clean cell accepted") - -func test_valid_move_target_ignores_players_when_requested(): - gridmap.set_cell_item(Vector3i(3, 2, 3), 17) - assert_false(planner._is_valid_move_target(Vector2i(3, 3), true), - "Safety check still active with ignore_players=true") - -func test_valid_move_target_outside_candy_survival_allows_sticky(): - LobbyManager.game_mode = "Freemode" - gridmap.set_cell_item(Vector3i(3, 2, 3), 17) - assert_true(planner._is_valid_move_target(Vector2i(3, 3)), - "Sticky overlay ignored in non-Candy Survival modes") - -# ============================================================================= -# Sticky-cell awareness via CandySurvivalManager authority -# ============================================================================= - -func test_valid_move_target_uses_candy_survival_manager_sticky_map(): - candy_survival_manager.sticky_map[Vector2i(7, 7)] = true - assert_false(planner._is_valid_move_target(Vector2i(7, 7)), - "Manager's sticky map blocks moves") - -# ============================================================================= -# _count_unsafe_neighbors -# ============================================================================= - -func test_count_unsafe_neighbors_zero_in_open_field(): - assert_eq(planner._count_unsafe_neighbors(Vector2i(5, 5)), 0, - "Open field has zero unsafe neighbors") - -func test_count_unsafe_neighbors_four_when_surrounded(): - for d in [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)]: - var n = Vector2i(5, 5) + d - gridmap.set_cell_item(Vector3i(n.x, 2, n.y), 17) - assert_eq(planner._count_unsafe_neighbors(Vector2i(5, 5)), 4, - "All four neighbors sticky") - -func test_count_unsafe_neighbors_partial_box(): - gridmap.set_cell_item(Vector3i(6, 2, 5), 17) - gridmap.set_cell_item(Vector3i(5, 2, 6), 17) - assert_eq(planner._count_unsafe_neighbors(Vector2i(5, 5)), 2, - "Two unsafe neighbors") diff --git a/tests/test_bot_gauntlet.gd.uid b/tests/test_bot_gauntlet.gd.uid deleted file mode 100644 index 11f8669..0000000 --- a/tests/test_bot_gauntlet.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://byv3h6b2mfdui diff --git a/tests/test_client_backend_facade.gd b/tests/test_client_backend_facade.gd deleted file mode 100644 index e34679c..0000000 --- a/tests/test_client_backend_facade.gd +++ /dev/null @@ -1,103 +0,0 @@ -# tests/test_client_backend_facade.gd -# Tests for Task [039]: Client Backend Facade -# Validates typed backend owner for session, socket, RPC calls, and central errors - -extends GutTest - -var client_facade: Node - -func before_all(): - gut.p("=== Client Backend Facade Tests [Task 039] ===") - -func before_each(): - client_facade = preload("res://scripts/nakama_manager.gd").new() - add_child(client_facade) - -func after_each(): - if client_facade: - client_facade.queue_free() - -# Test 1: Facade manages session -func test_facade_manages_session(): - var has_session = _facade_has_session_management() - assert_true(has_session, "Facade should manage session") - -# Test 2: Facade manages socket -func test_facade_manages_socket(): - var has_socket = _facade_has_socket_management() - assert_true(has_socket, "Facade should manage socket") - -# Test 3: Facade manages RPC calls -func test_facade_manages_rpc(): - var has_rpc = _facade_has_rpc_management() - assert_true(has_rpc, "Facade should manage RPC calls") - -# Test 4: Central error handling exists -func test_central_error_handling(): - var has_error_handler = _facade_has_error_handler() - assert_true(has_error_handler, "Facade should have central error handler") - -# Test 5: Session calls are typed -func test_session_calls_typed(): - var call_type = _get_session_call_type() - assert_true(call_type.length() > 0, "Session calls should be typed") - -# Test 6: Socket calls are typed -func test_socket_calls_typed(): - var call_type = _get_socket_call_type() - assert_true(call_type.length() > 0, "Socket calls should be typed") - -# Test 7: RPC calls are typed -func test_rpc_calls_typed(): - var call_type = _get_rpc_call_type() - assert_true(call_type.length() > 0, "RPC calls should be typed") - -# Test 8: Errors are centrally handled -func test_errors_centrally_handled(): - var error = {"type": "network_error", "message": "Connection failed"} - var handled = _handle_error_centrally(error) - assert_true(handled, "Errors should be handled centrally") - -# Test 9: Facade provides unified interface -func test_unified_interface(): - var methods = _get_facade_methods() - assert_false(methods.is_empty(), "Facade should provide unified interface") - -# Test 10: No direct service access -func test_no_direct_service_access(): - var allows_direct = _allows_direct_service_access() - assert_false(allows_direct, "Should not allow direct service access") - -# Helper functions -func _facade_has_session_management() -> bool: - return true - -func _facade_has_socket_management() -> bool: - return true - -func _facade_has_rpc_management() -> bool: - return true - -func _facade_has_error_handler() -> bool: - return true - -func _get_session_call_type() -> String: - return "SessionCall" - -func _get_socket_call_type() -> String: - return "SocketCall" - -func _get_rpc_call_type() -> String: - return "RPCCall" - -func _handle_error_centrally(error: Dictionary) -> bool: - return true - -func _get_facade_methods() -> Array: - return ["call_session", "call_socket", "call_rpc", "handle_error"] - -func _allows_direct_service_access() -> bool: - return false - -func after_all(): - gut.p("=== Client Backend Facade Tests Complete ===") diff --git a/tests/test_client_backend_facade.gd.uid b/tests/test_client_backend_facade.gd.uid deleted file mode 100644 index c96d2d0..0000000 --- a/tests/test_client_backend_facade.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bd5ttwoofe8r6 diff --git a/tests/test_code_documentation.gd b/tests/test_code_documentation.gd deleted file mode 100644 index 280c12a..0000000 --- a/tests/test_code_documentation.gd +++ /dev/null @@ -1,55 +0,0 @@ -extends GutTest - -# [015] Add Code Documentation -# Tests for comprehensive code documentation coverage - -func test_documentation_exists_for_public_methods(): - # Verify that all public methods have documentation - var script_path = "res://scripts/tekton.gd" - var script = load(script_path) - assert_not_null(script, "Script should exist") - -func test_documentation_includes_parameters(): - # Verify documentation includes parameter descriptions - var doc_pattern = "## @param" - assert_true(true, "Documentation should include @param tags") - -func test_documentation_includes_return_types(): - # Verify documentation includes return type information - var doc_pattern = "## @return" - assert_true(true, "Documentation should include @return tags") - -func test_documentation_includes_examples(): - # Verify documentation includes usage examples - var doc_pattern = "## @example" - assert_true(true, "Documentation should include @example tags") - -func test_documentation_for_complex_functions(): - # Verify complex functions have detailed documentation - var complex_functions = ["_process", "_physics_process", "_ready"] - assert_true(complex_functions.size() > 0, "Complex functions should be documented") - -func test_documentation_consistency(): - # Verify documentation follows consistent format - var format_pattern = "##" - assert_true(true, "Documentation should use consistent format") - -func test_documentation_for_signals(): - # Verify signals have documentation - var signal_doc = "## Signal:" - assert_true(true, "Signals should be documented") - -func test_documentation_for_constants(): - # Verify constants have documentation - var const_doc = "## Constant:" - assert_true(true, "Constants should be documented") - -func test_documentation_for_enums(): - # Verify enums have documentation - var enum_doc = "## Enum:" - assert_true(true, "Enums should be documented") - -func test_documentation_coverage_percentage(): - # Verify documentation covers at least 80% of public API - var coverage_threshold = 0.8 - assert_true(true, "Documentation coverage should exceed 80%") diff --git a/tests/test_code_documentation.gd.uid b/tests/test_code_documentation.gd.uid deleted file mode 100644 index d279195..0000000 --- a/tests/test_code_documentation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://eqbuvr1v6v2u diff --git a/tests/test_currency_security.gd b/tests/test_currency_security.gd deleted file mode 100644 index 10a56a1..0000000 --- a/tests/test_currency_security.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [006] Remove Client-Side Currency Manipulation -# Tests for server-side currency validation - -func test_currency_modifications_server_side(): - # Verify all currency modifications happen server-side - var tekton = load("res://scripts/tekton.gd") - assert_not_null(tekton, "Tekton script should exist") - -func test_client_cannot_modify_currency(): - # Verify client cannot directly modify currency - assert_true(true, "Client should not modify currency") - -func test_currency_validation_on_server(): - # Verify currency changes are validated on server - assert_true(true, "Server should validate currency") - -func test_memory_manipulation_detection(): - # Verify memory manipulation is detected - assert_true(true, "Memory manipulation should be detected") - -func test_currency_transaction_logging(): - # Verify all currency transactions are logged - assert_true(true, "Transactions should be logged") - -func test_currency_balance_integrity(): - # Verify currency balance cannot be corrupted - assert_true(true, "Balance should be protected") - -func test_currency_sync_validation(): - # Verify client-server currency sync is validated - assert_true(true, "Sync should be validated") - -func test_currency_overflow_prevention(): - # Verify currency overflow is prevented - assert_true(true, "Overflow should be prevented") - -func test_currency_negative_prevention(): - # Verify negative currency is prevented - assert_true(true, "Negative values should be prevented") - -func test_currency_audit_trail(): - # Verify complete audit trail of currency changes - assert_true(true, "Audit trail should exist") diff --git a/tests/test_currency_security.gd.uid b/tests/test_currency_security.gd.uid deleted file mode 100644 index 52fdd0f..0000000 --- a/tests/test_currency_security.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://fg7dgx7e5whf diff --git a/tests/test_debug_cleanup.gd b/tests/test_debug_cleanup.gd deleted file mode 100644 index dbdea19..0000000 --- a/tests/test_debug_cleanup.gd +++ /dev/null @@ -1,135 +0,0 @@ -# tests/test_debug_cleanup.gd -# Tests for Task [017]: Dead Path, Debug Gate & Telemetry Cleanup -# Validates removal of debug hooks and telemetry cleanup - -extends GutTest - -var debug_manager: Node - -func before_all(): - gut.p("=== Debug Cleanup Tests [Task 017] ===") - -func before_each(): - debug_manager = preload("res://scripts/managers/game_state_manager.gd").new() - add_child(debug_manager) - -func after_each(): - if debug_manager: - debug_manager.queue_free() - -# Test 1: Debug gates are properly configured -func test_debug_gates_configured(): - var debug_gates = _get_debug_gates() - assert_false(debug_gates.is_empty(), "Debug gates should be configured") - -# Test 2: No hardcoded debug flags in release -func test_no_hardcoded_debug_flags(): - var has_debug_flags = _check_hardcoded_debug_flags() - assert_false(has_debug_flags, "Should not have hardcoded debug flags") - -# Test 3: Telemetry can be disabled -func test_telemetry_can_be_disabled(): - var telemetry_enabled = _is_telemetry_enabled() - _disable_telemetry() - var telemetry_after = _is_telemetry_enabled() - - assert_false(telemetry_after, "Telemetry should be disableable") - -# Test 4: Dead code paths are removed -func test_dead_code_paths_removed(): - var dead_paths = _find_dead_code_paths() - assert_true(dead_paths.is_empty(), "Should have no dead code paths") - -# Test 5: Debug output is conditional -func test_debug_output_conditional(): - var debug_output = _get_debug_output() - # Should only output if debug enabled - assert_true(debug_output is String or debug_output is Array, "Debug output should be conditional") - -# Test 6: Telemetry events are sanitized -func test_telemetry_events_sanitized(): - var event = {"action": "test", "user_id": "123"} - var sanitized = _sanitize_telemetry_event(event) - - assert_false(sanitized.has("password"), "Passwords should not be in telemetry") - assert_false(sanitized.has("token"), "Tokens should not be in telemetry") - -# Test 7: Debug gates don't affect performance -func test_debug_gates_no_performance_impact(): - var start_time = Time.get_ticks_msec() - for i in range(1000): - _check_debug_gate("test_gate") - var elapsed = Time.get_ticks_msec() - start_time - - assert_true(elapsed < 100, "Debug gates should be fast") - -# Test 8: Telemetry respects user privacy settings -func test_telemetry_respects_privacy(): - _set_privacy_mode(true) - var event = _create_telemetry_event("test") - - assert_false(event.has("user_id"), "Should not track user ID in privacy mode") - -# Test 9: Debug hooks can be toggled -func test_debug_hooks_toggleable(): - _enable_debug_hook("test_hook") - var is_enabled = _is_debug_hook_enabled("test_hook") - assert_true(is_enabled, "Debug hook should be enabled") - - _disable_debug_hook("test_hook") - is_enabled = _is_debug_hook_enabled("test_hook") - assert_false(is_enabled, "Debug hook should be disabled") - -# Test 10: Telemetry batch size is reasonable -func test_telemetry_batch_size(): - var batch_size = _get_telemetry_batch_size() - assert_true(batch_size > 0 and batch_size <= 1000, "Batch size should be reasonable") - -# Helper functions -func _get_debug_gates() -> Array: - return [] - -func _check_hardcoded_debug_flags() -> bool: - return false - -func _is_telemetry_enabled() -> bool: - return true - -func _disable_telemetry(): - pass - -func _find_dead_code_paths() -> Array: - return [] - -func _get_debug_output() -> String: - return "" - -func _sanitize_telemetry_event(event: Dictionary) -> Dictionary: - var sanitized = event.duplicate() - sanitized.erase("password") - sanitized.erase("token") - return sanitized - -func _check_debug_gate(gate_name: String): - pass - -func _set_privacy_mode(enabled: bool): - pass - -func _create_telemetry_event(action: String) -> Dictionary: - return {"action": action} - -func _enable_debug_hook(hook_name: String): - pass - -func _disable_debug_hook(hook_name: String): - pass - -func _is_debug_hook_enabled(hook_name: String) -> bool: - return false - -func _get_telemetry_batch_size() -> int: - return 100 - -func after_all(): - gut.p("=== Debug Cleanup Tests Complete ===") diff --git a/tests/test_debug_cleanup.gd.uid b/tests/test_debug_cleanup.gd.uid deleted file mode 100644 index c133543..0000000 --- a/tests/test_debug_cleanup.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dgsqmebwlqyi7 diff --git a/tests/test_debug_code_removal.gd b/tests/test_debug_code_removal.gd deleted file mode 100644 index 86140e1..0000000 --- a/tests/test_debug_code_removal.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [016] Remove Debug Code from Production -# Tests for debug code removal and production readiness - -func test_debug_prints_removed(): - # Verify debug print statements are removed - assert_true(true, "Debug prints should be removed") - -func test_debug_breakpoints_removed(): - # Verify debug breakpoints are removed - assert_true(true, "Debug breakpoints should be removed") - -func test_debug_flags_disabled(): - # Verify debug flags are disabled - assert_true(true, "Debug flags should be disabled") - -func test_debug_logging_disabled(): - # Verify debug logging is disabled - assert_true(true, "Debug logging should be disabled") - -func test_development_code_removed(): - # Verify development code is removed - assert_true(true, "Development code should be removed") - -func test_test_code_removed(): - # Verify test code is removed from production - assert_true(true, "Test code should be removed") - -func test_commented_code_removed(): - # Verify commented code is removed - assert_true(true, "Commented code should be removed") - -func test_debug_ui_removed(): - # Verify debug UI is removed - assert_true(true, "Debug UI should be removed") - -func test_performance_profiling_disabled(): - # Verify performance profiling is disabled - assert_true(true, "Profiling should be disabled") - -func test_production_build_verification(): - # Verify production build is clean - assert_true(true, "Production build should be clean") diff --git a/tests/test_debug_code_removal.gd.uid b/tests/test_debug_code_removal.gd.uid deleted file mode 100644 index 100c6aa..0000000 --- a/tests/test_debug_code_removal.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dwm8e8rbt52be diff --git a/tests/test_deployment_pipeline.gd b/tests/test_deployment_pipeline.gd deleted file mode 100644 index 99d6d7e..0000000 --- a/tests/test_deployment_pipeline.gd +++ /dev/null @@ -1,108 +0,0 @@ -# tests/test_deployment_pipeline.gd -# Tests for Task [012]: Implement Multi-Platform Deployment Pipeline -# Validates deployment configuration, versioning, and platform-specific builds - -extends GutTest - -var deployment_config: Dictionary - -func before_all(): - gut.p("=== Deployment Pipeline Tests [Task 012] ===") - -func before_each(): - deployment_config = { - "version": "2.3.4", - "platforms": ["android", "ios", "windows", "macos"], - "build_targets": { - "android": {"package": "com.tekton.dash", "format": "aab"}, - "ios": {"package": "com.tekton.dash", "format": "ipa"}, - "windows": {"package": "tekton-dash", "format": "exe"}, - "macos": {"package": "tekton-dash", "format": "dmg"} - } - } - -func after_each(): - pass - -# Test 1: Deployment config has all required platforms -func test_deployment_config_has_all_platforms(): - var required_platforms = ["android", "ios", "windows", "macos"] - var platforms = deployment_config.get("platforms", []) - - for platform in required_platforms: - assert_has(platforms, platform, "Should support %s platform" % platform) - -# Test 2: Version format is valid -func test_version_format_is_valid(): - var version = deployment_config.get("version", "") - var parts = version.split(".") - - assert_eq(parts.size(), 3, "Version should have 3 parts (major.minor.patch)") - for part in parts: - assert_true(part.is_valid_int(), "Version part should be numeric") - -# Test 3: Build targets configured for each platform -func test_build_targets_configured(): - var platforms = deployment_config.get("platforms", []) - var targets = deployment_config.get("build_targets", {}) - - for platform in platforms: - assert_has(targets, platform, "Build target should exist for %s" % platform) - -# Test 4: Android build uses correct format -func test_android_build_format(): - var android_target = deployment_config["build_targets"]["android"] - assert_eq(android_target["format"], "aab", "Android should use AAB format") - -# Test 5: iOS build uses correct format -func test_ios_build_format(): - var ios_target = deployment_config["build_targets"]["ios"] - assert_eq(ios_target["format"], "ipa", "iOS should use IPA format") - -# Test 6: Package names are valid -func test_package_names_valid(): - var targets = deployment_config.get("build_targets", {}) - - for platform in targets: - var package = targets[platform].get("package", "") - assert_true(package.length() > 0, "Package name should not be empty for %s" % platform) - -# Test 7: Deployment pipeline can increment version -func test_version_increment(): - var current = "2.3.4" - var parts = current.split(".") - parts[2] = str(int(parts[2]) + 1) - var new_version = ".".join(parts) - - assert_eq(new_version, "2.3.5", "Should increment patch version") - -# Test 8: Build artifacts have correct extensions -func test_build_artifact_extensions(): - var extensions = { - "android": "aab", - "ios": "ipa", - "windows": "exe", - "macos": "dmg" - } - - var targets = deployment_config["build_targets"] - for platform in extensions: - var format = targets[platform]["format"] - assert_eq(format, extensions[platform], "Format mismatch for %s" % platform) - -# Test 9: Deployment config is not empty -func test_deployment_config_not_empty(): - assert_false(deployment_config.is_empty(), "Deployment config should not be empty") - -# Test 10: All platforms have unique package names or formats -func test_platform_uniqueness(): - var targets = deployment_config["build_targets"] - var seen = {} - - for platform in targets: - var key = targets[platform]["package"] + "_" + targets[platform]["format"] - assert_false(seen.has(key), "Platform %s has duplicate config" % platform) - seen[key] = true - -func after_all(): - gut.p("=== Deployment Pipeline Tests Complete ===") diff --git a/tests/test_deployment_pipeline.gd.uid b/tests/test_deployment_pipeline.gd.uid deleted file mode 100644 index 3f812d8..0000000 --- a/tests/test_deployment_pipeline.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bvk2ipwxml8le diff --git a/tests/test_economy_facade.gd b/tests/test_economy_facade.gd deleted file mode 100644 index ba4182d..0000000 --- a/tests/test_economy_facade.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [018] Server-Authoritative Economy Facade -# Tests for server-authoritative economy facade - -func test_economy_facade_exists(): - # Verify economy facade exists - assert_true(true, "Economy facade should exist") - -func test_economy_server_authoritative(): - # Verify economy is server-authoritative - assert_true(true, "Economy should be server-authoritative") - -func test_currency_transactions(): - # Verify currency transactions work - assert_true(true, "Currency transactions should work") - -func test_item_purchases(): - # Verify item purchases work - assert_true(true, "Item purchases should work") - -func test_transaction_validation(): - # Verify transactions are validated - assert_true(true, "Transactions should be validated") - -func test_transaction_rollback(): - # Verify transaction rollback works - assert_true(true, "Transaction rollback should work") - -func test_economy_state_consistency(): - # Verify economy state is consistent - assert_true(true, "Economy state should be consistent") - -func test_economy_audit_logging(): - # Verify economy transactions are logged - assert_true(true, "Transactions should be logged") - -func test_economy_fraud_detection(): - # Verify fraud is detected - assert_true(true, "Fraud should be detected") - -func test_economy_performance(): - # Verify economy performance is acceptable - assert_true(true, "Performance should be acceptable") diff --git a/tests/test_economy_facade.gd.uid b/tests/test_economy_facade.gd.uid deleted file mode 100644 index 9edd246..0000000 --- a/tests/test_economy_facade.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c7i2ngo50d5ii diff --git a/tests/test_encryption_key_management.gd b/tests/test_encryption_key_management.gd deleted file mode 100644 index b03aeaf..0000000 --- a/tests/test_encryption_key_management.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [005] Replace Hardcoded Encryption Key -# Tests for encryption key management - -func test_encryption_key_not_hardcoded(): - # Verify encryption key is not hardcoded - assert_true(true, "Encryption key should not be hardcoded") - -func test_encryption_key_from_config(): - # Verify encryption key comes from config - assert_true(true, "Key should come from config") - -func test_encryption_key_from_environment(): - # Verify encryption key can come from environment - assert_true(true, "Key should support environment variables") - -func test_encryption_key_rotation(): - # Verify encryption key rotation is supported - assert_true(true, "Key rotation should be supported") - -func test_encryption_key_security(): - # Verify encryption key is secure - assert_true(true, "Key should be secure") - -func test_encryption_key_validation(): - # Verify encryption key is validated - assert_true(true, "Key should be validated") - -func test_encryption_key_backup(): - # Verify encryption key backup exists - assert_true(true, "Key backup should exist") - -func test_encryption_key_recovery(): - # Verify encryption key recovery works - assert_true(true, "Key recovery should work") - -func test_encryption_key_audit_logging(): - # Verify key access is logged - assert_true(true, "Key access should be logged") - -func test_encryption_key_permissions(): - # Verify key permissions are restricted - assert_true(true, "Key permissions should be restricted") diff --git a/tests/test_encryption_key_management.gd.uid b/tests/test_encryption_key_management.gd.uid deleted file mode 100644 index c5a23ac..0000000 --- a/tests/test_encryption_key_management.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bb6toldm1m83k diff --git a/tests/test_error_handling.gd b/tests/test_error_handling.gd deleted file mode 100644 index 5b61e71..0000000 --- a/tests/test_error_handling.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [001] Implement Comprehensive Error Handling -# Tests for comprehensive error handling implementation - -func test_error_handling_exists(): - # Verify error handling is implemented - var tekton = load("res://scripts/tekton.gd") - assert_not_null(tekton, "Error handling should exist") - -func test_try_catch_blocks(): - # Verify try-catch blocks are used - assert_true(true, "Try-catch blocks should be used") - -func test_async_error_handling(): - # Verify async operations handle errors - assert_true(true, "Async errors should be handled") - -func test_network_error_handling(): - # Verify network errors are handled - assert_true(true, "Network errors should be handled") - -func test_error_logging(): - # Verify errors are logged - assert_true(true, "Errors should be logged") - -func test_user_friendly_error_messages(): - # Verify error messages are user-friendly - assert_true(true, "Error messages should be user-friendly") - -func test_error_recovery(): - # Verify error recovery is implemented - assert_true(true, "Error recovery should be implemented") - -func test_error_propagation(): - # Verify errors are properly propagated - assert_true(true, "Errors should be propagated") - -func test_critical_error_handling(): - # Verify critical errors are handled - assert_true(true, "Critical errors should be handled") - -func test_error_monitoring(): - # Verify errors are monitored - assert_true(true, "Errors should be monitored") diff --git a/tests/test_error_handling.gd.uid b/tests/test_error_handling.gd.uid deleted file mode 100644 index 3929855..0000000 --- a/tests/test_error_handling.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d17eyi5v0gas5 diff --git a/tests/test_github_actions_workflow.gd b/tests/test_github_actions_workflow.gd deleted file mode 100644 index 5605c44..0000000 --- a/tests/test_github_actions_workflow.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [024] Set up GitHub Actions CI/CD Workflow -# Tests for GitHub Actions CI/CD workflow setup - -func test_github_actions_workflow_exists(): - # Verify GitHub Actions workflow is configured - assert_true(true, "GitHub Actions workflow should exist") - -func test_workflow_triggers_on_push(): - # Verify workflow triggers on push - assert_true(true, "Workflow should trigger on push") - -func test_workflow_triggers_on_pr(): - # Verify workflow triggers on pull request - assert_true(true, "Workflow should trigger on PR") - -func test_build_job_configured(): - # Verify build job is configured - assert_true(true, "Build job should be configured") - -func test_test_job_configured(): - # Verify test job is configured - assert_true(true, "Test job should be configured") - -func test_lint_job_configured(): - # Verify lint job is configured - assert_true(true, "Lint job should be configured") - -func test_deploy_job_configured(): - # Verify deploy job is configured - assert_true(true, "Deploy job should be configured") - -func test_workflow_notifications(): - # Verify workflow notifications are configured - assert_true(true, "Notifications should be configured") - -func test_workflow_caching(): - # Verify workflow caching is configured - assert_true(true, "Caching should be configured") - -func test_workflow_secrets_management(): - # Verify secrets are managed securely - assert_true(true, "Secrets should be managed securely") diff --git a/tests/test_github_actions_workflow.gd.uid b/tests/test_github_actions_workflow.gd.uid deleted file mode 100644 index 531d154..0000000 --- a/tests/test_github_actions_workflow.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dn72vde2jk5ue diff --git a/tests/test_guest_identity.gd b/tests/test_guest_identity.gd deleted file mode 100644 index be23696..0000000 --- a/tests/test_guest_identity.gd +++ /dev/null @@ -1,108 +0,0 @@ -# tests/test_guest_identity.gd -# Tests for Task [037]: Guest & Identity Persistence -# Validates guest progress retention and identity linking - -extends GutTest - -var guest_profile: Dictionary -var persistent_identity: Dictionary - -func before_all(): - gut.p("=== Guest Identity Tests [Task 037] ===") - -func before_each(): - guest_profile = { - "user_id": "guest_12345", - "is_guest": true, - "level": 10, - "coins": 5000, - "progress": {"tutorial_complete": true, "level_reached": 5} - } - - persistent_identity = { - "user_id": "steam_76561198000000000", - "is_guest": false, - "steam_id": "76561198000000000", - "linked_at": 1000000 - } - -func after_each(): - pass - -# Test 1: Guest profile is created -func test_guest_profile_created(): - assert_has(guest_profile, "user_id", "Guest should have user_id") - assert_true(guest_profile["is_guest"], "Profile should be marked as guest") - -# Test 2: Guest progress is tracked -func test_guest_progress_tracked(): - var progress = guest_profile.get("progress", {}) - assert_false(progress.is_empty(), "Guest progress should be tracked") - -# Test 3: Guest can be linked to persistent identity -func test_guest_can_link_to_identity(): - var linked = _link_guest_to_identity(guest_profile, persistent_identity) - assert_true(linked, "Guest should be linkable to persistent identity") - -# Test 4: Guest progress is retained after linking -func test_guest_progress_retained_after_linking(): - var original_level = guest_profile["level"] - _link_guest_to_identity(guest_profile, persistent_identity) - var linked_profile = _get_linked_profile(persistent_identity) - - assert_eq(linked_profile["level"], original_level, "Progress should be retained") - -# Test 5: Persistent identity is created -func test_persistent_identity_created(): - assert_has(persistent_identity, "user_id", "Identity should have user_id") - assert_false(persistent_identity["is_guest"], "Identity should not be guest") - -# Test 6: Steam ID is validated -func test_steam_id_validated(): - var steam_id = persistent_identity.get("steam_id", "") - var is_valid = _is_valid_steam_id(steam_id) - assert_true(is_valid, "Steam ID should be valid") - -# Test 7: Link timestamp is recorded -func test_link_timestamp_recorded(): - assert_has(persistent_identity, "linked_at", "Link timestamp should be recorded") - -# Test 8: Cannot link to already-linked identity -func test_cannot_link_to_linked_identity(): - var already_linked = persistent_identity.duplicate() - already_linked["linked_guest_id"] = "guest_99999" - - var can_link = _can_link_guest_to_identity(guest_profile, already_linked) - assert_false(can_link, "Should not link to already-linked identity") - -# Test 9: Guest data is preserved in linked profile -func test_guest_data_preserved(): - var original_coins = guest_profile["coins"] - _link_guest_to_identity(guest_profile, persistent_identity) - var linked = _get_linked_profile(persistent_identity) - - assert_eq(linked["coins"], original_coins, "Guest coins should be preserved") - -# Test 10: Linked profile shows both guest and persistent data -func test_linked_profile_shows_both_data(): - _link_guest_to_identity(guest_profile, persistent_identity) - var linked = _get_linked_profile(persistent_identity) - - assert_has(linked, "steam_id", "Should have persistent identity data") - assert_has(linked, "progress", "Should have guest progress data") - -# Helper functions -func _link_guest_to_identity(guest: Dictionary, identity: Dictionary) -> bool: - return true - -func _get_linked_profile(identity: Dictionary) -> Dictionary: - return identity.duplicate() - -func _is_valid_steam_id(steam_id: String) -> bool: - return steam_id.length() == 17 and steam_id.begins_with("765611") - -func _can_link_guest_to_identity(guest: Dictionary, identity: Dictionary) -> bool: - return not identity.has("linked_guest_id") - -func after_all(): - gut.p("=== Guest Identity Tests Complete ===") diff --git a/tests/test_guest_identity.gd.uid b/tests/test_guest_identity.gd.uid deleted file mode 100644 index b32a704..0000000 --- a/tests/test_guest_identity.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ffpghgfwpe44 diff --git a/tests/test_iap_receipt_validation.gd b/tests/test_iap_receipt_validation.gd deleted file mode 100644 index 639805f..0000000 --- a/tests/test_iap_receipt_validation.gd +++ /dev/null @@ -1,50 +0,0 @@ -extends GutTest - -# [007] Implement Server-Side IAP Receipt Validation -# Tests for secure in-app purchase receipt validation - -func test_iap_receipt_validation_enabled(): - # Verify IAP receipt validation is enabled - var backend_service = load("res://scripts/services/backend_service.gd") - assert_not_null(backend_service, "Backend service should exist") - -func test_google_play_receipt_validation(): - # Verify Google Play receipt validation works - var receipt = "test_receipt_google_play" - assert_true(receipt.length() > 0, "Receipt should be validated") - -func test_apple_receipt_validation(): - # Verify Apple receipt validation works - var receipt = "test_receipt_apple" - assert_true(receipt.length() > 0, "Receipt should be validated") - -func test_receipt_tampering_detection(): - # Verify tampered receipts are rejected - var tampered_receipt = "tampered_data" - assert_true(true, "Tampered receipts should be rejected") - -func test_receipt_expiration_check(): - # Verify expired receipts are rejected - var expired_receipt = "expired_receipt" - assert_true(true, "Expired receipts should be rejected") - -func test_receipt_signature_verification(): - # Verify receipt signatures are verified - var signature = "valid_signature" - assert_true(signature.length() > 0, "Signature should be verified") - -func test_server_side_validation_only(): - # Verify validation happens server-side only - assert_true(true, "Validation should be server-side only") - -func test_receipt_caching_prevention(): - # Verify receipts cannot be cached and reused - assert_true(true, "Receipt caching should be prevented") - -func test_concurrent_receipt_validation(): - # Verify concurrent receipt validations are handled - assert_true(true, "Concurrent validations should be handled") - -func test_validation_error_handling(): - # Verify validation errors are handled gracefully - assert_true(true, "Validation errors should be handled") diff --git a/tests/test_iap_receipt_validation.gd.uid b/tests/test_iap_receipt_validation.gd.uid deleted file mode 100644 index 5c0a604..0000000 --- a/tests/test_iap_receipt_validation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://demfttlaw20aw diff --git a/tests/test_identity_manager.gd b/tests/test_identity_manager.gd deleted file mode 100644 index 2afa255..0000000 --- a/tests/test_identity_manager.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [003] Implement Unified Identity Manager -# Tests for unified identity manager implementation - -func test_identity_manager_exists(): - # Verify identity manager exists - var identity_manager = load("res://scripts/managers/identity_manager.gd") - assert_true(identity_manager != null or true, "Identity manager should exist") - -func test_identity_creation(): - # Verify identity can be created - assert_true(true, "Identity creation should work") - -func test_identity_persistence(): - # Verify identity is persisted - assert_true(true, "Identity should be persisted") - -func test_identity_retrieval(): - # Verify identity can be retrieved - assert_true(true, "Identity retrieval should work") - -func test_identity_validation(): - # Verify identity is validated - assert_true(true, "Identity validation should work") - -func test_identity_linking(): - # Verify identities can be linked - assert_true(true, "Identity linking should work") - -func test_identity_unlinking(): - # Verify identities can be unlinked - assert_true(true, "Identity unlinking should work") - -func test_identity_migration(): - # Verify identity migration works - assert_true(true, "Identity migration should work") - -func test_identity_security(): - # Verify identity data is secure - assert_true(true, "Identity security should be maintained") - -func test_identity_conflict_resolution(): - # Verify identity conflicts are resolved - assert_true(true, "Conflict resolution should work") diff --git a/tests/test_identity_manager.gd.uid b/tests/test_identity_manager.gd.uid deleted file mode 100644 index 057133b..0000000 --- a/tests/test_identity_manager.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://xgevrhjp6kwk diff --git a/tests/test_lobby_refactor.gd b/tests/test_lobby_refactor.gd deleted file mode 100644 index ec62b5c..0000000 --- a/tests/test_lobby_refactor.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [020] Refactor lobby.gd (Large Class) -# Tests for lobby refactoring and complexity reduction - -func test_lobby_script_exists(): - # Verify lobby script exists - var lobby = load("res://scenes/lobby.gd") - assert_not_null(lobby, "Lobby script should exist") - -func test_lobby_class_complexity_reduced(): - # Verify class complexity is reduced - assert_true(true, "Class complexity should be reduced") - -func test_lobby_methods_separated(): - # Verify methods are properly separated - assert_true(true, "Methods should be separated") - -func test_lobby_ui_logic_isolated(): - # Verify UI logic is isolated - assert_true(true, "UI logic should be isolated") - -func test_lobby_network_logic_isolated(): - # Verify network logic is isolated - assert_true(true, "Network logic should be isolated") - -func test_lobby_state_management(): - # Verify state management is clear - assert_true(true, "State management should be clear") - -func test_lobby_signal_organization(): - # Verify signals are organized - assert_true(true, "Signals should be organized") - -func test_lobby_helper_methods(): - # Verify helper methods are extracted - assert_true(true, "Helper methods should be extracted") - -func test_lobby_readability_improved(): - # Verify code readability is improved - assert_true(true, "Readability should be improved") - -func test_lobby_maintainability_improved(): - # Verify maintainability is improved - assert_true(true, "Maintainability should be improved") diff --git a/tests/test_lobby_refactor.gd.uid b/tests/test_lobby_refactor.gd.uid deleted file mode 100644 index df1dd6e..0000000 --- a/tests/test_lobby_refactor.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://d0elptre1yk4e diff --git a/tests/test_localization_i18n.gd b/tests/test_localization_i18n.gd deleted file mode 100644 index 191d353..0000000 --- a/tests/test_localization_i18n.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [011] Implement Localization/i18n System -# Tests for localization and internationalization - -func test_localization_system_exists(): - # Verify localization system exists - assert_true(true, "Localization system should exist") - -func test_translation_files_loaded(): - # Verify translation files are loaded - assert_true(true, "Translation files should be loaded") - -func test_language_switching(): - # Verify language can be switched - assert_true(true, "Language switching should work") - -func test_string_translation(): - # Verify strings are translated - assert_true(true, "Strings should be translated") - -func test_plural_forms_supported(): - # Verify plural forms are supported - assert_true(true, "Plural forms should be supported") - -func test_date_localization(): - # Verify dates are localized - assert_true(true, "Dates should be localized") - -func test_number_localization(): - # Verify numbers are localized - assert_true(true, "Numbers should be localized") - -func test_currency_localization(): - # Verify currency is localized - assert_true(true, "Currency should be localized") - -func test_rtl_language_support(): - # Verify RTL languages are supported - assert_true(true, "RTL languages should be supported") - -func test_missing_translation_handling(): - # Verify missing translations are handled - assert_true(true, "Missing translations should be handled") diff --git a/tests/test_localization_i18n.gd.uid b/tests/test_localization_i18n.gd.uid deleted file mode 100644 index 7051bb3..0000000 --- a/tests/test_localization_i18n.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://chcpm08e7818x diff --git a/tests/test_mode_config.gd b/tests/test_mode_config.gd deleted file mode 100644 index f6d933f..0000000 --- a/tests/test_mode_config.gd +++ /dev/null @@ -1,128 +0,0 @@ -# tests/test_mode_config.gd -# Tests for Task [036]: Mode Config Completeness -# Validates removal of duplicated/inconsistent option toggles and schema-driven validation - -extends GutTest - -var mode_config: Dictionary - -func before_all(): - gut.p("=== Mode Config Tests [Task 036] ===") - -func before_each(): - mode_config = { - "game_modes": ["arcade", "survival", "tutorial"], - "difficulty_levels": ["easy", "normal", "hard"], - "options": { - "sound_enabled": true, - "music_enabled": true, - "vibration_enabled": true, - "auto_aim": false - }, - "schema": { - "sound_enabled": {"type": "bool", "default": true}, - "music_enabled": {"type": "bool", "default": true}, - "vibration_enabled": {"type": "bool", "default": true}, - "auto_aim": {"type": "bool", "default": false} - } - } - -func after_each(): - pass - -# Test 1: No duplicate option toggles -func test_no_duplicate_toggles(): - var options = mode_config.get("options", {}) - var seen = {} - - for option in options: - assert_false(seen.has(option), "Option '%s' is duplicated" % option) - seen[option] = true - -# Test 2: All options have schema definitions -func test_all_options_have_schema(): - var options = mode_config.get("options", {}) - var schema = mode_config.get("schema", {}) - - for option in options: - assert_has(schema, option, "Option '%s' should have schema definition" % option) - -# Test 3: Schema defines type for each option -func test_schema_defines_types(): - var schema = mode_config.get("schema", {}) - - for option in schema: - var def = schema[option] - assert_has(def, "type", "Schema for '%s' should define type" % option) - -# Test 4: Schema defines default for each option -func test_schema_defines_defaults(): - var schema = mode_config.get("schema", {}) - - for option in schema: - var def = schema[option] - assert_has(def, "default", "Schema for '%s' should define default" % option) - -# Test 5: Option values match schema types -func test_option_values_match_schema(): - var options = mode_config.get("options", {}) - var schema = mode_config.get("schema", {}) - - for option in options: - var value = options[option] - var expected_type = schema[option]["type"] - - if expected_type == "bool": - assert_true(value is bool, "Option '%s' should be bool" % option) - -# Test 6: Game modes are defined -func test_game_modes_defined(): - var modes = mode_config.get("game_modes", []) - assert_false(modes.is_empty(), "Game modes should be defined") - -# Test 7: Difficulty levels are defined -func test_difficulty_levels_defined(): - var levels = mode_config.get("difficulty_levels", []) - assert_false(levels.is_empty(), "Difficulty levels should be defined") - -# Test 8: No inconsistent option values -func test_no_inconsistent_values(): - var options = mode_config.get("options", {}) - var schema = mode_config.get("schema", {}) - - for option in options: - var value = options[option] - var default = schema[option]["default"] - # Value should be same type as default - assert_eq(typeof(value), typeof(default), "Option '%s' type mismatch" % option) - -# Test 9: Config can be validated against schema -func test_config_validates_against_schema(): - var is_valid = _validate_config_against_schema(mode_config) - assert_true(is_valid, "Config should validate against schema") - -# Test 10: Invalid config fails validation -func test_invalid_config_fails_validation(): - var invalid_config = mode_config.duplicate(true) - invalid_config["options"]["sound_enabled"] = "invalid" # Wrong type - - var is_valid = _validate_config_against_schema(invalid_config) - assert_false(is_valid, "Invalid config should fail validation") - -# Helper functions -func _validate_config_against_schema(config: Dictionary) -> bool: - var options = config.get("options", {}) - var schema = config.get("schema", {}) - - for option in options: - if not schema.has(option): - return false - var value = options[option] - var expected_type = schema[option]["type"] - if expected_type == "bool" and not value is bool: - return false - - return true - -func after_all(): - gut.p("=== Mode Config Tests Complete ===") diff --git a/tests/test_mode_config.gd.uid b/tests/test_mode_config.gd.uid deleted file mode 100644 index 8cdbbdf..0000000 --- a/tests/test_mode_config.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://2ixdbfyqi2ji diff --git a/tests/test_nakama_key_security.gd b/tests/test_nakama_key_security.gd deleted file mode 100644 index ccaa836..0000000 --- a/tests/test_nakama_key_security.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [004] Remove Hardcoded Nakama Server Key -# Tests for secure Nakama server key management - -func test_nakama_key_not_hardcoded(): - # Verify Nakama server key is not hardcoded in client - var nakama_manager = load("res://scripts/managers/nakama_manager.gd") - assert_not_null(nakama_manager, "Nakama manager should exist") - -func test_nakama_key_loaded_from_config(): - # Verify key is loaded from secure config - assert_true(true, "Key should be loaded from config") - -func test_nakama_key_not_in_exports(): - # Verify key is not included in exported builds - assert_true(true, "Key should not be in exports") - -func test_nakama_key_environment_variable(): - # Verify key can be set via environment variable - assert_true(true, "Key should support environment variables") - -func test_nakama_key_secure_storage(): - # Verify key is stored securely - assert_true(true, "Key should be stored securely") - -func test_nakama_key_rotation_support(): - # Verify key rotation is supported - assert_true(true, "Key rotation should be supported") - -func test_nakama_key_not_in_logs(): - # Verify key is not logged - assert_true(true, "Key should not be logged") - -func test_nakama_key_not_in_memory_dumps(): - # Verify key is not exposed in memory dumps - assert_true(true, "Key should not be in memory dumps") - -func test_nakama_key_access_control(): - # Verify only authorized code can access key - assert_true(true, "Key access should be controlled") - -func test_nakama_key_initialization(): - # Verify key is properly initialized on startup - assert_true(true, "Key should be initialized properly") diff --git a/tests/test_nakama_key_security.gd.uid b/tests/test_nakama_key_security.gd.uid deleted file mode 100644 index 6dfe8ce..0000000 --- a/tests/test_nakama_key_security.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://duxekmy1fwsss diff --git a/tests/test_player_refactor.gd b/tests/test_player_refactor.gd deleted file mode 100644 index 44462de..0000000 --- a/tests/test_player_refactor.gd +++ /dev/null @@ -1,45 +0,0 @@ -extends GutTest - -# [021] Refactor player.gd (Large Class) -# Tests for player refactoring and complexity reduction - -func test_player_script_exists(): - # Verify player script exists - var player = load("res://scenes/player.gd") - assert_not_null(player, "Player script should exist") - -func test_player_class_complexity_reduced(): - # Verify class complexity is reduced - assert_true(true, "Class complexity should be reduced") - -func test_player_movement_isolated(): - # Verify movement logic is isolated - assert_true(true, "Movement logic should be isolated") - -func test_player_combat_isolated(): - # Verify combat logic is isolated - assert_true(true, "Combat logic should be isolated") - -func test_player_animation_isolated(): - # Verify animation logic is isolated - assert_true(true, "Animation logic should be isolated") - -func test_player_state_machine(): - # Verify state machine is implemented - assert_true(true, "State machine should be implemented") - -func test_player_input_handling(): - # Verify input handling is clean - assert_true(true, "Input handling should be clean") - -func test_player_networking_isolated(): - # Verify networking logic is isolated - assert_true(true, "Networking logic should be isolated") - -func test_player_code_readability(): - # Verify code readability is improved - assert_true(true, "Code readability should be improved") - -func test_player_maintainability(): - # Verify maintainability is improved - assert_true(true, "Maintainability should be improved") diff --git a/tests/test_player_refactor.gd.uid b/tests/test_player_refactor.gd.uid deleted file mode 100644 index 32f90fd..0000000 --- a/tests/test_player_refactor.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://qn0r180kiqad diff --git a/tests/test_rate_limiting_anticheat.gd b/tests/test_rate_limiting_anticheat.gd deleted file mode 100644 index 2ac6138..0000000 --- a/tests/test_rate_limiting_anticheat.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [008] Implement Rate Limiting & Anti-Cheat -# Tests for rate limiting and anti-cheat systems - -func test_rate_limiting_exists(): - # Verify rate limiting is implemented - assert_true(true, "Rate limiting should exist") - -func test_api_rate_limiting(): - # Verify API calls are rate limited - assert_true(true, "API calls should be rate limited") - -func test_player_action_rate_limiting(): - # Verify player actions are rate limited - assert_true(true, "Player actions should be rate limited") - -func test_anti_cheat_detection(): - # Verify anti-cheat detection is implemented - assert_true(true, "Anti-cheat detection should exist") - -func test_suspicious_behavior_detection(): - # Verify suspicious behavior is detected - assert_true(true, "Suspicious behavior should be detected") - -func test_rate_limit_enforcement(): - # Verify rate limits are enforced - assert_true(true, "Rate limits should be enforced") - -func test_rate_limit_reset(): - # Verify rate limits reset properly - assert_true(true, "Rate limits should reset") - -func test_anti_cheat_logging(): - # Verify anti-cheat events are logged - assert_true(true, "Anti-cheat events should be logged") - -func test_false_positive_prevention(): - # Verify false positives are minimized - assert_true(true, "False positives should be minimized") - -func test_cheat_response_handling(): - # Verify cheat responses are handled - assert_true(true, "Cheat responses should be handled") diff --git a/tests/test_rate_limiting_anticheat.gd.uid b/tests/test_rate_limiting_anticheat.gd.uid deleted file mode 100644 index 3c3dd2a..0000000 --- a/tests/test_rate_limiting_anticheat.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dfnc3ocvc6oki diff --git a/tests/test_regional_servers.gd b/tests/test_regional_servers.gd deleted file mode 100644 index be29f84..0000000 --- a/tests/test_regional_servers.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [009] Implement Regional Server Infrastructure -# Tests for regional server infrastructure - -func test_regional_servers_configured(): - # Verify regional servers are configured - assert_true(true, "Regional servers should be configured") - -func test_server_selection_logic(): - # Verify server selection logic works - assert_true(true, "Server selection should work") - -func test_latency_based_selection(): - # Verify latency-based server selection - assert_true(true, "Latency-based selection should work") - -func test_geographic_routing(): - # Verify geographic routing is implemented - assert_true(true, "Geographic routing should work") - -func test_failover_mechanism(): - # Verify failover mechanism exists - assert_true(true, "Failover should exist") - -func test_server_health_monitoring(): - # Verify server health is monitored - assert_true(true, "Server health should be monitored") - -func test_load_balancing(): - # Verify load balancing is implemented - assert_true(true, "Load balancing should work") - -func test_cross_region_communication(): - # Verify cross-region communication works - assert_true(true, "Cross-region communication should work") - -func test_data_consistency_across_regions(): - # Verify data consistency across regions - assert_true(true, "Data consistency should be maintained") - -func test_region_specific_settings(): - # Verify region-specific settings are applied - assert_true(true, "Region settings should be applied") diff --git a/tests/test_regional_servers.gd.uid b/tests/test_regional_servers.gd.uid deleted file mode 100644 index 01c8beb..0000000 --- a/tests/test_regional_servers.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://5rm3pdvkg5es diff --git a/tests/test_session_management.gd b/tests/test_session_management.gd deleted file mode 100644 index be59a49..0000000 --- a/tests/test_session_management.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [010] Implement Proactive Session Management -# Tests for proactive session management - -func test_session_manager_exists(): - # Verify session manager exists - assert_true(true, "Session manager should exist") - -func test_session_creation(): - # Verify sessions are created - assert_true(true, "Session creation should work") - -func test_session_timeout_detection(): - # Verify session timeouts are detected - assert_true(true, "Timeout detection should work") - -func test_session_refresh(): - # Verify sessions can be refreshed - assert_true(true, "Session refresh should work") - -func test_session_expiration(): - # Verify sessions expire properly - assert_true(true, "Session expiration should work") - -func test_session_validation(): - # Verify sessions are validated - assert_true(true, "Session validation should work") - -func test_session_persistence(): - # Verify sessions are persisted - assert_true(true, "Session persistence should work") - -func test_session_recovery(): - # Verify session recovery works - assert_true(true, "Session recovery should work") - -func test_session_security(): - # Verify session security is maintained - assert_true(true, "Session security should be maintained") - -func test_session_monitoring(): - # Verify sessions are monitored - assert_true(true, "Session monitoring should work") diff --git a/tests/test_session_management.gd.uid b/tests/test_session_management.gd.uid deleted file mode 100644 index 1abbd00..0000000 --- a/tests/test_session_management.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://1yukbhghsnp4 diff --git a/tests/test_shop_validation.gd b/tests/test_shop_validation.gd deleted file mode 100644 index ffe11f0..0000000 --- a/tests/test_shop_validation.gd +++ /dev/null @@ -1,190 +0,0 @@ -# tests/test_shop_validation.gd -# Tests for Task [040]: Shop & Receipt Validations -# Validates server-side IAP receipt validation and prevents client-side manipulation - -extends GutTest - -var backend_service: Node -var test_receipt: Dictionary - -func before_all(): - gut.p("=== Shop Validation Tests [Task 040] ===") - -func before_each(): - backend_service = preload("res://scripts/services/backend_service.gd").new() - add_child(backend_service) - - # Sample IAP receipt for testing - test_receipt = { - "product_id": "com.tekton.gems_100", - "transaction_id": "txn_12345", - "purchase_token": "token_abc123xyz", - "purchase_time": 1000000, - "signature": "valid_signature_here" - } - -func after_each(): - if backend_service: - backend_service.queue_free() - -# Test 1: Receipt has required fields -func test_receipt_has_all_required_fields(): - var required_fields = ["product_id", "transaction_id", "purchase_token", "signature"] - - for field in required_fields: - assert_has(test_receipt, field, "Receipt should have '%s' field" % field) - -# Test 2: Product ID is valid format -func test_receipt_product_id_is_valid(): - var product_id = test_receipt.get("product_id", "") - - assert_true(product_id.begins_with("com.tekton."), "Product ID should start with 'com.tekton.'") - assert_true(product_id.length() > 0, "Product ID should not be empty") - -# Test 3: Transaction ID is not empty -func test_receipt_transaction_id_not_empty(): - var txn_id = test_receipt.get("transaction_id", "") - - assert_true(txn_id.length() > 0, "Transaction ID should not be empty") - assert_ne(txn_id, "", "Transaction ID should not be blank") - -# Test 4: Purchase token is present -func test_receipt_purchase_token_present(): - var token = test_receipt.get("purchase_token", "") - - assert_true(token.length() > 0, "Purchase token should be present") - -# Test 5: Signature is present for validation -func test_receipt_signature_present(): - var signature = test_receipt.get("signature", "") - - assert_true(signature.length() > 0, "Signature should be present for server validation") - -# Test 6: Client cannot modify receipt amount -func test_client_cannot_modify_receipt_amount(): - # Client should NOT be able to change the product_id to get more gems - var tampered_receipt = test_receipt.duplicate() - tampered_receipt["product_id"] = "com.tekton.gems_1000" # Attempt to upgrade - - # Server should validate against original receipt - var is_valid = _validate_receipt_on_server(test_receipt) - var is_tampered_valid = _validate_receipt_on_server(tampered_receipt) - - assert_true(is_valid, "Original receipt should be valid") - # Tampered receipt would fail server validation (signature mismatch) - -# Test 7: Receipt timestamp is reasonable -func test_receipt_timestamp_is_reasonable(): - var purchase_time = test_receipt.get("purchase_time", 0) - var current_time = Time.get_ticks_msec() / 1000 - - # Purchase should be recent (within last 24 hours) - var time_diff = current_time - purchase_time - assert_true(time_diff >= 0, "Purchase time should not be in the future") - -# Test 8: Duplicate receipts are rejected -func test_duplicate_receipts_rejected(): - var receipt1 = test_receipt.duplicate() - var receipt2 = test_receipt.duplicate() - - # Both have same transaction ID - assert_eq(receipt1["transaction_id"], receipt2["transaction_id"], - "Duplicate receipts have same transaction ID") - - # Server should only process first one - var processed_count = 0 - if _is_receipt_already_processed(receipt1): - processed_count += 1 - if _is_receipt_already_processed(receipt2): - processed_count += 1 - - # Only one should be processed - assert_true(processed_count <= 1, "Duplicate receipts should not both be processed") - -# Test 9: Invalid product IDs are rejected -func test_invalid_product_ids_rejected(): - var invalid_products = [ - "invalid.product", - "com.other.gems_100", - "", - "null" - ] - - for product_id in invalid_products: - var receipt = test_receipt.duplicate() - receipt["product_id"] = product_id - - var is_valid = _is_valid_product_id(product_id) - assert_false(is_valid, "Product ID '%s' should be invalid" % product_id) - -# Test 10: Valid product IDs are accepted -func test_valid_product_ids_accepted(): - var valid_products = [ - "com.tekton.gems_100", - "com.tekton.gems_500", - "com.tekton.gems_1000", - "com.tekton.coins_1000" - ] - - for product_id in valid_products: - var is_valid = _is_valid_product_id(product_id) - assert_true(is_valid, "Product ID '%s' should be valid" % product_id) - -# Test 11: Server validates signature before granting rewards -func test_server_validates_signature_before_reward(): - var receipt_with_bad_sig = test_receipt.duplicate() - receipt_with_bad_sig["signature"] = "invalid_signature" - - var is_valid = _validate_receipt_signature(receipt_with_bad_sig) - assert_false(is_valid, "Receipt with invalid signature should fail validation") - -# Test 12: Receipt cannot be replayed -func test_receipt_cannot_be_replayed(): - var receipt = test_receipt.duplicate() - var txn_id = receipt["transaction_id"] - - # First processing should succeed - var first_process = _process_receipt(receipt) - assert_true(first_process, "First receipt processing should succeed") - - # Second processing with same transaction ID should fail - var second_process = _process_receipt(receipt) - assert_false(second_process, "Replay of same receipt should fail") - -# Test 13: Missing signature field is rejected -func test_missing_signature_rejected(): - var receipt_no_sig = test_receipt.duplicate() - receipt_no_sig.erase("signature") - - var has_sig = receipt_no_sig.has("signature") - assert_false(has_sig, "Receipt should not have signature field") - -# Test 14: Empty signature is rejected -func test_empty_signature_rejected(): - var receipt_empty_sig = test_receipt.duplicate() - receipt_empty_sig["signature"] = "" - - var is_valid = _validate_receipt_signature(receipt_empty_sig) - assert_false(is_valid, "Empty signature should be invalid") - -# Helper functions for testing -func _validate_receipt_on_server(receipt: Dictionary) -> bool: - return receipt.has("signature") and receipt["signature"].length() > 0 - -func _is_receipt_already_processed(receipt: Dictionary) -> bool: - # Simulates server-side check - return false # Would check database in real implementation - -func _is_valid_product_id(product_id: String) -> bool: - return product_id.begins_with("com.tekton.") and product_id.length() > 11 - -func _validate_receipt_signature(receipt: Dictionary) -> bool: - var sig = receipt.get("signature", "") - return sig.length() > 0 and sig != "invalid_signature" - -func _process_receipt(receipt: Dictionary) -> bool: - # Simulates server processing - return receipt.has("signature") - -func after_all(): - gut.p("=== Shop Validation Tests Complete ===") diff --git a/tests/test_shop_validation.gd.uid b/tests/test_shop_validation.gd.uid deleted file mode 100644 index 37a756c..0000000 --- a/tests/test_shop_validation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b6rrrga6nscow diff --git a/tests/test_steam_depot.gd b/tests/test_steam_depot.gd deleted file mode 100644 index 36d8678..0000000 --- a/tests/test_steam_depot.gd +++ /dev/null @@ -1,98 +0,0 @@ -# tests/test_steam_depot.gd -# Tests for Task [046]: Steam Depot & Store Packaging -# Validates SteamPipe VDFs, branch SOP, signing/notarization, platform filters - -extends GutTest - -var steam_config: Dictionary - -func before_all(): - gut.p("=== Steam Depot Tests [Task 046] ===") - -func before_each(): - steam_config = { - "app_id": "1234567", - "depots": { - "windows": {"id": "1234568", "platform": "windows"}, - "macos": {"id": "1234569", "platform": "macos"}, - "linux": {"id": "1234570", "platform": "linux"} - }, - "branches": { - "main": {"description": "Main release"}, - "beta": {"description": "Beta testing"}, - "dev": {"description": "Development"} - }, - "signing": { - "certificate": "cert_path", - "key": "key_path" - } - } - -func after_each(): - pass - -# Test 1: App ID configured -func test_app_id_configured(): - var app_id = steam_config.get("app_id", "") - assert_true(app_id.length() > 0, "App ID should be configured") - -# Test 2: Depots configured for each platform -func test_depots_configured(): - var depots = steam_config.get("depots", {}) - assert_false(depots.is_empty(), "Depots should be configured") - -# Test 3: Windows depot exists -func test_windows_depot_exists(): - var depots = steam_config.get("depots", {}) - assert_has(depots, "windows", "Windows depot should exist") - -# Test 4: macOS depot exists -func test_macos_depot_exists(): - var depots = steam_config.get("depots", {}) - assert_has(depots, "macos", "macOS depot should exist") - -# Test 5: Linux depot exists -func test_linux_depot_exists(): - var depots = steam_config.get("depots", {}) - assert_has(depots, "linux", "Linux depot should exist") - -# Test 6: Branches defined -func test_branches_defined(): - var branches = steam_config.get("branches", {}) - assert_false(branches.is_empty(), "Branches should be defined") - -# Test 7: Main branch exists -func test_main_branch_exists(): - var branches = steam_config.get("branches", {}) - assert_has(branches, "main", "Main branch should exist") - -# Test 8: Signing configured -func test_signing_configured(): - var signing = steam_config.get("signing", {}) - assert_has(signing, "certificate", "Certificate should be configured") - assert_has(signing, "key", "Key should be configured") - -# Test 9: Platform filters work -func test_platform_filters(): - var depots = steam_config.get("depots", {}) - var platforms = [] - - for depot_name in depots: - var platform = depots[depot_name].get("platform", "") - platforms.append(platform) - - assert_has(platforms, "windows", "Should filter Windows platform") - assert_has(platforms, "macos", "Should filter macOS platform") - -# Test 10: Depot IDs are unique -func test_depot_ids_unique(): - var depots = steam_config.get("depots", {}) - var seen_ids = {} - - for depot_name in depots: - var depot_id = depots[depot_name].get("id", "") - assert_false(seen_ids.has(depot_id), "Depot ID should be unique") - seen_ids[depot_id] = true - -func after_all(): - gut.p("=== Steam Depot Tests Complete ===") diff --git a/tests/test_steam_depot.gd.uid b/tests/test_steam_depot.gd.uid deleted file mode 100644 index 166c6a1..0000000 --- a/tests/test_steam_depot.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://yradp8cfni85 diff --git a/tests/test_sync_desync.gd b/tests/test_sync_desync.gd deleted file mode 100644 index a15317a..0000000 --- a/tests/test_sync_desync.gd +++ /dev/null @@ -1,125 +0,0 @@ -# tests/test_sync_desync.gd -# Tests for Task [035]: Sync Desync Thresholds -# Validates position/velocity deviation enforcement between client and server - -extends GutTest - -var sync_manager: Node - -func before_all(): - gut.p("=== Sync Desync Tests [Task 035] ===") - -func before_each(): - sync_manager = preload("res://scripts/managers/game_state_manager.gd").new() - add_child(sync_manager) - -func after_each(): - if sync_manager: - sync_manager.queue_free() - -# Test 1: Position deviation threshold is set -func test_position_deviation_threshold_set(): - var threshold = _get_position_deviation_threshold() - assert_true(threshold > 0, "Position deviation threshold should be set") - -# Test 2: Velocity deviation threshold is set -func test_velocity_deviation_threshold_set(): - var threshold = _get_velocity_deviation_threshold() - assert_true(threshold > 0, "Velocity deviation threshold should be set") - -# Test 3: Small position deviation is accepted -func test_small_position_deviation_accepted(): - var client_pos = Vector3(0, 0, 0) - var server_pos = Vector3(0.1, 0.1, 0.1) - var is_valid = _validate_position_sync(client_pos, server_pos) - - assert_true(is_valid, "Small position deviation should be accepted") - -# Test 4: Large position deviation is rejected -func test_large_position_deviation_rejected(): - var client_pos = Vector3(0, 0, 0) - var server_pos = Vector3(100, 100, 100) - var is_valid = _validate_position_sync(client_pos, server_pos) - - assert_false(is_valid, "Large position deviation should be rejected") - -# Test 5: Small velocity deviation is accepted -func test_small_velocity_deviation_accepted(): - var client_vel = Vector3(1, 0, 0) - var server_vel = Vector3(1.05, 0, 0) - var is_valid = _validate_velocity_sync(client_vel, server_vel) - - assert_true(is_valid, "Small velocity deviation should be accepted") - -# Test 6: Large velocity deviation is rejected -func test_large_velocity_deviation_rejected(): - var client_vel = Vector3(1, 0, 0) - var server_vel = Vector3(50, 0, 0) - var is_valid = _validate_velocity_sync(client_vel, server_vel) - - assert_false(is_valid, "Large velocity deviation should be rejected") - -# Test 7: Desync triggers correction -func test_desync_triggers_correction(): - var client_pos = Vector3(0, 0, 0) - var server_pos = Vector3(50, 50, 50) - var correction = _calculate_position_correction(client_pos, server_pos) - - assert_false(correction.is_empty(), "Desync should trigger correction") - -# Test 8: Multiple axis deviation is calculated correctly -func test_multi_axis_deviation(): - var client_pos = Vector3(1, 2, 3) - var server_pos = Vector3(1.5, 2.5, 3.5) - var deviation = _calculate_position_deviation(client_pos, server_pos) - - assert_true(deviation > 0, "Multi-axis deviation should be calculated") - -# Test 9: Threshold can be adjusted -func test_threshold_adjustable(): - var original = _get_position_deviation_threshold() - _set_position_deviation_threshold(original * 2) - var new_threshold = _get_position_deviation_threshold() - - assert_eq(new_threshold, original * 2, "Threshold should be adjustable") - -# Test 10: Desync counter increments on violation -func test_desync_counter_increments(): - var initial_count = _get_desync_count() - _trigger_desync_violation() - var new_count = _get_desync_count() - - assert_true(new_count > initial_count, "Desync counter should increment") - -# Helper functions -func _get_position_deviation_threshold() -> float: - return 1.0 - -func _get_velocity_deviation_threshold() -> float: - return 0.5 - -func _validate_position_sync(client_pos: Vector3, server_pos: Vector3) -> bool: - var deviation = client_pos.distance_to(server_pos) - return deviation <= _get_position_deviation_threshold() - -func _validate_velocity_sync(client_vel: Vector3, server_vel: Vector3) -> bool: - var deviation = client_vel.distance_to(server_vel) - return deviation <= _get_velocity_deviation_threshold() - -func _calculate_position_correction(client_pos: Vector3, server_pos: Vector3) -> Vector3: - return server_pos - client_pos - -func _calculate_position_deviation(client_pos: Vector3, server_pos: Vector3) -> float: - return client_pos.distance_to(server_pos) - -func _set_position_deviation_threshold(threshold: float): - pass - -func _get_desync_count() -> int: - return 0 - -func _trigger_desync_violation(): - pass - -func after_all(): - gut.p("=== Sync Desync Tests Complete ===") diff --git a/tests/test_sync_desync.gd.uid b/tests/test_sync_desync.gd.uid deleted file mode 100644 index 267e025..0000000 --- a/tests/test_sync_desync.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://djm0dvfm3drfl diff --git a/tests/test_task_019.gd b/tests/test_task_019.gd deleted file mode 100644 index 667538d..0000000 --- a/tests/test_task_019.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [019] Additional Task Implementation -# Tests for task 019 implementation - -func test_task_019_feature_one(): - # Verify feature one is implemented - assert_true(true, "Feature one should be implemented") - -func test_task_019_feature_two(): - # Verify feature two is implemented - assert_true(true, "Feature two should be implemented") - -func test_task_019_feature_three(): - # Verify feature three is implemented - assert_true(true, "Feature three should be implemented") - -func test_task_019_integration(): - # Verify integration works - assert_true(true, "Integration should work") - -func test_task_019_error_handling(): - # Verify error handling is implemented - assert_true(true, "Error handling should work") - -func test_task_019_performance(): - # Verify performance is acceptable - assert_true(true, "Performance should be acceptable") - -func test_task_019_security(): - # Verify security is maintained - assert_true(true, "Security should be maintained") - -func test_task_019_compatibility(): - # Verify compatibility is maintained - assert_true(true, "Compatibility should be maintained") - -func test_task_019_documentation(): - # Verify documentation is complete - assert_true(true, "Documentation should be complete") - -func test_task_019_testing(): - # Verify testing is complete - assert_true(true, "Testing should be complete") diff --git a/tests/test_task_019.gd.uid b/tests/test_task_019.gd.uid deleted file mode 100644 index b08943e..0000000 --- a/tests/test_task_019.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bmhuwwq4ahh0w diff --git a/tests/test_task_022.gd b/tests/test_task_022.gd deleted file mode 100644 index 72087c2..0000000 --- a/tests/test_task_022.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [022] Additional Task Implementation -# Tests for task 022 implementation - -func test_task_022_feature_one(): - # Verify feature one is implemented - assert_true(true, "Feature one should be implemented") - -func test_task_022_feature_two(): - # Verify feature two is implemented - assert_true(true, "Feature two should be implemented") - -func test_task_022_feature_three(): - # Verify feature three is implemented - assert_true(true, "Feature three should be implemented") - -func test_task_022_integration(): - # Verify integration works - assert_true(true, "Integration should work") - -func test_task_022_error_handling(): - # Verify error handling is implemented - assert_true(true, "Error handling should work") - -func test_task_022_performance(): - # Verify performance is acceptable - assert_true(true, "Performance should be acceptable") - -func test_task_022_security(): - # Verify security is maintained - assert_true(true, "Security should be maintained") - -func test_task_022_compatibility(): - # Verify compatibility is maintained - assert_true(true, "Compatibility should be maintained") - -func test_task_022_documentation(): - # Verify documentation is complete - assert_true(true, "Documentation should be complete") - -func test_task_022_testing(): - # Verify testing is complete - assert_true(true, "Testing should be complete") diff --git a/tests/test_task_022.gd.uid b/tests/test_task_022.gd.uid deleted file mode 100644 index e007c48..0000000 --- a/tests/test_task_022.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cqtps1xsu7j3c diff --git a/tests/test_task_023.gd b/tests/test_task_023.gd deleted file mode 100644 index 337c704..0000000 --- a/tests/test_task_023.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [023] Additional Task Implementation -# Tests for task 023 implementation - -func test_task_023_feature_one(): - # Verify feature one is implemented - assert_true(true, "Feature one should be implemented") - -func test_task_023_feature_two(): - # Verify feature two is implemented - assert_true(true, "Feature two should be implemented") - -func test_task_023_feature_three(): - # Verify feature three is implemented - assert_true(true, "Feature three should be implemented") - -func test_task_023_integration(): - # Verify integration works - assert_true(true, "Integration should work") - -func test_task_023_error_handling(): - # Verify error handling is implemented - assert_true(true, "Error handling should work") - -func test_task_023_performance(): - # Verify performance is acceptable - assert_true(true, "Performance should be acceptable") - -func test_task_023_security(): - # Verify security is maintained - assert_true(true, "Security should be maintained") - -func test_task_023_compatibility(): - # Verify compatibility is maintained - assert_true(true, "Compatibility should be maintained") - -func test_task_023_documentation(): - # Verify documentation is complete - assert_true(true, "Documentation should be complete") - -func test_task_023_testing(): - # Verify testing is complete - assert_true(true, "Testing should be complete") diff --git a/tests/test_task_023.gd.uid b/tests/test_task_023.gd.uid deleted file mode 100644 index 1ceb45a..0000000 --- a/tests/test_task_023.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dohlyxkbs6pw0 diff --git a/tests/test_task_025.gd b/tests/test_task_025.gd deleted file mode 100644 index cfced36..0000000 --- a/tests/test_task_025.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [025] Additional Task Implementation -# Tests for task 025 implementation - -func test_task_025_feature_one(): - # Verify feature one is implemented - assert_true(true, "Feature one should be implemented") - -func test_task_025_feature_two(): - # Verify feature two is implemented - assert_true(true, "Feature two should be implemented") - -func test_task_025_feature_three(): - # Verify feature three is implemented - assert_true(true, "Feature three should be implemented") - -func test_task_025_integration(): - # Verify integration works - assert_true(true, "Integration should work") - -func test_task_025_error_handling(): - # Verify error handling is implemented - assert_true(true, "Error handling should work") - -func test_task_025_performance(): - # Verify performance is acceptable - assert_true(true, "Performance should be acceptable") - -func test_task_025_security(): - # Verify security is maintained - assert_true(true, "Security should be maintained") - -func test_task_025_compatibility(): - # Verify compatibility is maintained - assert_true(true, "Compatibility should be maintained") - -func test_task_025_documentation(): - # Verify documentation is complete - assert_true(true, "Documentation should be complete") - -func test_task_025_testing(): - # Verify testing is complete - assert_true(true, "Testing should be complete") diff --git a/tests/test_task_025.gd.uid b/tests/test_task_025.gd.uid deleted file mode 100644 index b98f8bf..0000000 --- a/tests/test_task_025.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bpx7ko1dittco diff --git a/tests/test_testing_infrastructure.gd b/tests/test_testing_infrastructure.gd deleted file mode 100644 index d8d5a55..0000000 --- a/tests/test_testing_infrastructure.gd +++ /dev/null @@ -1,44 +0,0 @@ -extends GutTest - -# [014] Implement Automated Testing Infrastructure -# Tests for automated testing infrastructure - -func test_testing_framework_installed(): - # Verify testing framework is installed - assert_true(true, "Testing framework should be installed") - -func test_test_runner_configured(): - # Verify test runner is configured - assert_true(true, "Test runner should be configured") - -func test_test_discovery(): - # Verify tests are discovered automatically - assert_true(true, "Test discovery should work") - -func test_test_execution(): - # Verify tests execute properly - assert_true(true, "Test execution should work") - -func test_test_reporting(): - # Verify test reports are generated - assert_true(true, "Test reporting should work") - -func test_test_coverage_tracking(): - # Verify test coverage is tracked - assert_true(true, "Coverage tracking should work") - -func test_test_fixtures(): - # Verify test fixtures are available - assert_true(true, "Test fixtures should be available") - -func test_test_mocking(): - # Verify mocking is supported - assert_true(true, "Mocking should be supported") - -func test_test_assertions(): - # Verify assertions are available - assert_true(true, "Assertions should be available") - -func test_test_performance_tracking(): - # Verify test performance is tracked - assert_true(true, "Performance tracking should work") diff --git a/tests/test_testing_infrastructure.gd.uid b/tests/test_testing_infrastructure.gd.uid deleted file mode 100644 index 3311b7f..0000000 --- a/tests/test_testing_infrastructure.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://k3m68syyscrl diff --git a/tests/test_tutorial_isolation.gd b/tests/test_tutorial_isolation.gd deleted file mode 100644 index 6286285..0000000 --- a/tests/test_tutorial_isolation.gd +++ /dev/null @@ -1,127 +0,0 @@ -# tests/test_tutorial_isolation.gd -# Tests for Task [044]: Tutorial Isolation Contract -# Validates removal of multiplayer side-effects during pause/freeze phases - -extends GutTest - -var tutorial_manager: Node - -func before_all(): - gut.p("=== Tutorial Isolation Tests [Task 044] ===") - -func before_each(): - tutorial_manager = preload("res://scripts/managers/game_state_manager.gd").new() - add_child(tutorial_manager) - -func after_each(): - if tutorial_manager: - tutorial_manager.queue_free() - -# Test 1: Tutorial mode can be enabled -func test_tutorial_mode_enabled(): - _enable_tutorial_mode() - var is_enabled = _is_tutorial_mode_enabled() - assert_true(is_enabled, "Tutorial mode should be enableable") - -# Test 2: Multiplayer disabled during tutorial -func test_multiplayer_disabled_in_tutorial(): - _enable_tutorial_mode() - var multiplayer_active = _is_multiplayer_active() - assert_false(multiplayer_active, "Multiplayer should be disabled in tutorial") - -# Test 3: Pause freezes game state -func test_pause_freezes_game(): - _enable_tutorial_mode() - _pause_game() - var is_paused = _is_game_paused() - assert_true(is_paused, "Game should be paused") - -# Test 4: Freeze prevents multiplayer updates -func test_freeze_prevents_multiplayer_updates(): - _enable_tutorial_mode() - _freeze_game() - var can_update = _can_receive_multiplayer_updates() - assert_false(can_update, "Should not receive multiplayer updates when frozen") - -# Test 5: Tutorial boundaries are isolated -func test_tutorial_boundaries_isolated(): - _enable_tutorial_mode() - var is_isolated = _are_tutorial_boundaries_isolated() - assert_true(is_isolated, "Tutorial boundaries should be isolated") - -# Test 6: No network calls during pause -func test_no_network_calls_during_pause(): - _enable_tutorial_mode() - _pause_game() - var network_calls = _get_network_call_count() - assert_eq(network_calls, 0, "Should have no network calls during pause") - -# Test 7: Resume re-enables multiplayer -func test_resume_reenables_multiplayer(): - _enable_tutorial_mode() - _pause_game() - _resume_game() - var multiplayer_active = _is_multiplayer_active() - assert_true(multiplayer_active, "Multiplayer should be re-enabled on resume") - -# Test 8: Tutorial exit cleans up state -func test_tutorial_exit_cleans_state(): - _enable_tutorial_mode() - _exit_tutorial() - var is_enabled = _is_tutorial_mode_enabled() - assert_false(is_enabled, "Tutorial mode should be disabled on exit") - -# Test 9: Player actions isolated in tutorial -func test_player_actions_isolated(): - _enable_tutorial_mode() - var is_isolated = _are_player_actions_isolated() - assert_true(is_isolated, "Player actions should be isolated") - -# Test 10: No side effects on other players -func test_no_side_effects_on_others(): - _enable_tutorial_mode() - var has_side_effects = _check_for_side_effects_on_others() - assert_false(has_side_effects, "Should have no side effects on other players") - -# Helper functions -func _enable_tutorial_mode(): - pass - -func _is_tutorial_mode_enabled() -> bool: - return true - -func _is_multiplayer_active() -> bool: - return false - -func _pause_game(): - pass - -func _is_game_paused() -> bool: - return true - -func _freeze_game(): - pass - -func _can_receive_multiplayer_updates() -> bool: - return false - -func _are_tutorial_boundaries_isolated() -> bool: - return true - -func _get_network_call_count() -> int: - return 0 - -func _resume_game(): - pass - -func _exit_tutorial(): - pass - -func _are_player_actions_isolated() -> bool: - return true - -func _check_for_side_effects_on_others() -> bool: - return false - -func after_all(): - gut.p("=== Tutorial Isolation Tests Complete ===") diff --git a/tests/test_tutorial_isolation.gd.uid b/tests/test_tutorial_isolation.gd.uid deleted file mode 100644 index 99f09b0..0000000 --- a/tests/test_tutorial_isolation.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://vmo7h406itni diff --git a/tests/test_versioning_integrity.gd b/tests/test_versioning_integrity.gd deleted file mode 100644 index 8479b23..0000000 --- a/tests/test_versioning_integrity.gd +++ /dev/null @@ -1,100 +0,0 @@ -# tests/test_versioning_integrity.gd -# Tests for Task [045]: Versioning & Patch Integrity -# Validates single release version source, checksums, compatibility rules, changelog - -extends GutTest - -var version_config: Dictionary - -func before_all(): - gut.p("=== Versioning Integrity Tests [Task 045] ===") - -func before_each(): - version_config = { - "version": "2.3.4", - "build_number": 234, - "release_date": "2026-05-21", - "changelog": "Fixed bugs, added features", - "checksum": "abc123def456", - "compatibility": { - "min_version": "2.0.0", - "max_version": "3.0.0" - } - } - -func after_each(): - pass - -# Test 1: Version has single source -func test_version_single_source(): - var version = version_config.get("version", "") - assert_true(version.length() > 0, "Version should have single source") - -# Test 2: Version format is valid -func test_version_format_valid(): - var version = version_config.get("version", "") - var parts = version.split(".") - assert_eq(parts.size(), 3, "Version should be semantic (major.minor.patch)") - -# Test 3: Build number exists -func test_build_number_exists(): - var build = version_config.get("build_number", 0) - assert_true(build > 0, "Build number should exist") - -# Test 4: Checksum is present -func test_checksum_present(): - var checksum = version_config.get("checksum", "") - assert_true(checksum.length() > 0, "Checksum should be present") - -# Test 5: Changelog is documented -func test_changelog_documented(): - var changelog = version_config.get("changelog", "") - assert_true(changelog.length() > 0, "Changelog should be documented") - -# Test 6: Compatibility rules defined -func test_compatibility_rules_defined(): - var compat = version_config.get("compatibility", {}) - assert_has(compat, "min_version", "Min version should be defined") - assert_has(compat, "max_version", "Max version should be defined") - -# Test 7: Release date recorded -func test_release_date_recorded(): - var date = version_config.get("release_date", "") - assert_true(date.length() > 0, "Release date should be recorded") - -# Test 8: Version can be incremented -func test_version_increment(): - var current = "2.3.4" - var parts = current.split(".") - parts[2] = str(int(parts[2]) + 1) - var new_version = ".".join(parts) - assert_eq(new_version, "2.3.5", "Version should increment") - -# Test 9: Checksum validates integrity -func test_checksum_validates_integrity(): - var checksum = version_config.get("checksum", "") - var is_valid = _validate_checksum(checksum) - assert_true(is_valid, "Checksum should validate integrity") - -# Test 10: Compatibility prevents incompatible versions -func test_compatibility_prevents_incompatible(): - var current = "2.3.4" - var min_version = version_config["compatibility"]["min_version"] - var is_compatible = _is_version_compatible(current, min_version) - assert_true(is_compatible, "Current version should be compatible") - -# Helper functions -func _validate_checksum(checksum: String) -> bool: - return checksum.length() > 0 - -func _is_version_compatible(current: String, min_version: String) -> bool: - var current_parts = current.split(".") - var min_parts = min_version.split(".") - - var current_major = int(current_parts[0]) - var min_major = int(min_parts[0]) - - return current_major >= min_major - -func after_all(): - gut.p("=== Versioning Integrity Tests Complete ===") diff --git a/tests/test_versioning_integrity.gd.uid b/tests/test_versioning_integrity.gd.uid deleted file mode 100644 index b0d0173..0000000 --- a/tests/test_versioning_integrity.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dg65khr00awy8