diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9a10ca..e5f5f10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: run: | C:\Miniconda\condabin\conda.bat activate botty python -c "import sys; print(sys.version)" - coverage run -m pytest test/smoke_test.py test/nip/ test/game_stats_test.py test/closest_non_hud_test.py test/transmute/ -v -s + coverage run -m pytest test/smoke_test.py test/nip/ test/game_stats_test.py test/closest_non_hud_test.py test/transmute/ test/test_setup_bat_files.py -v -s - name: Coverage shell: powershell diff --git a/install.bat b/install.bat index 91fc1b5..1abc81a 100644 --- a/install.bat +++ b/install.bat @@ -35,6 +35,20 @@ exit /b 1 echo Found conda: %CONDA_EXE% echo. +:: --- Test conda works (catches broken base env, missing pywin32, etc.) --- +echo Testing conda... +"%CONDA_EXE%" --version >nul 2>&1 +if %errorlevel% neq 0 ( + echo. + echo ERROR: conda found but failed to run. Your conda installation may be broken. + echo Try re-installing Miniforge: https://github.com/conda-forge/miniforge + echo. + pause + exit /b 1 +) +echo conda is working. +echo. + :: --- Create or update the botty env --- "%CONDA_EXE%" env list | findstr /C:"botty" >nul 2>&1 if %errorlevel% equ 0 ( @@ -52,6 +66,43 @@ if %errorlevel% neq 0 ( exit /b 1 ) +:: --- Verify botty env has Python --- +set "PYTHON=" +for %%C in ( + "%USERPROFILE%\miniforge3\envs\botty\python.exe" + "%USERPROFILE%\miniconda3\envs\botty\python.exe" + "%USERPROFILE%\anaconda3\envs\botty\python.exe" + "%ProgramData%\miniforge3\envs\botty\python.exe" + "%ProgramData%\miniconda3\envs\botty\python.exe" + "%ProgramData%\anaconda3\envs\botty\python.exe" +) do ( + if exist %%C ( + set "PYTHON=%%~C" + goto :env_created + ) +) + +echo. +echo ERROR: botty env was created but python.exe was not found. +echo This usually means the env build failed. Check output above. +pause +exit /b 1 + +:env_created +echo Botty Python: %PYTHON% + +:: --- Smoke test: import key dependencies --- +echo. +echo Verifying dependencies... +"%PYTHON%" -c "import cv2, mss, numpy, tesserocr, discord, transitions, rapidfuzz" >nul 2>&1 +if %errorlevel% neq 0 ( + echo. + echo WARNING: Some imports failed. Try running install.bat again, or check: + echo development.md for manual troubleshooting. +) else ( + echo All key dependencies verified. +) + echo. echo ============================================ echo Installation complete! diff --git a/test/test_setup_bat_files.py b/test/test_setup_bat_files.py new file mode 100644 index 0000000..b122211 --- /dev/null +++ b/test/test_setup_bat_files.py @@ -0,0 +1,115 @@ +"""Test that launcher bat files are well-formed and portable. + +Checks: + 1. All expected .bat files exist + 2. No hardcoded usernames or absolute paths (except conda paths) + 3. find_python.bat is referenced by run_*.bat +""" + +import os +import re + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +EXPECTED_BATS = [ + "install.bat", + "find_python.bat", + "run_botty.bat", + "run_asset_extractor.bat", + "run_quest_debug.bat", +] + +USERNAMES_TO_BLOCK = ["alex", "alexpolo", "ultimate"] + + +def _read(name): + return open(os.path.join(ROOT, name)).read() + + +def _bat_exists(name): + assert os.path.isfile(os.path.join(ROOT, name)), f"{name} is missing from repo root" + + +class TestBatFilesExist: + def test_all_bats_present(self): + for name in EXPECTED_BATS: + _bat_exists(name) + + +class TestNoHardcodedUsernames: + """run_*.bat and find_python.bat must never contain a real username.""" + + _CHECKED = [ + "find_python.bat", + "run_botty.bat", + "run_asset_extractor.bat", + "run_quest_debug.bat", + ] + + def test_no_hardcoded_usernames(self): + for name in self._CHECKED: + content = _read(name).lower() + for uname in USERNAMES_TO_BLOCK: + for line in content.split("\n"): + stripped = line.strip().lstrip(":").lstrip("*") + if uname in stripped and not stripped.startswith(":"): + raise AssertionError( + f"{name} contains hardcoded username '{uname}' on: " + f"{line.strip()}" + ) + + def test_no_absolute_home_paths(self): + for name in self._CHECKED: + content = _read(name) + # Find C:\Users\ followed by a literal username (not %USERNAME%) + bad = re.findall(r"C:\\Users\\([^%\s]+)", content) + bad = [b for b in bad if b != "%USERNAME%"] + assert not bad, f"{name} has hardcoded home path(s): {bad}" + + +class TestFindPythonUsage: + """All run_*.bat should source find_python.bat.""" + + _RUN_BATS = [ + "run_botty.bat", + "run_asset_extractor.bat", + "run_quest_debug.bat", + ] + + def test_run_bats_call_find_python(self): + for name in self._RUN_BATS: + content = _read(name) + assert "find_python.bat" in content, ( + f"{name} does not call find_python.bat -- " + f"source the shared helper instead of duplicating conda detection" + ) + + def test_find_python_has_conda_locations(self): + content = _read("find_python.bat") + location_count = ( + content.count("miniforge3") + + content.count("miniconda3") + + content.count("anaconda3") + ) + assert location_count >= 6, ( + f"find_python.bat only checks {location_count} conda locations, " + f"expected >= 6" + ) + + +class TestInstallBat: + def test_conda_self_test(self): + content = _read("install.bat") + has_test = any( + kw in content + for kw in ["--version", "conda test", "testing conda", "test conda"] + ) + assert has_test, ( + "install.bat should verify conda works before creating the env" + ) + + def test_verifies_python_after_env_create(self): + content = _read("install.bat") + assert "python.exe" in content, ( + "install.bat should verify botty env python.exe exists after env create" + )