1+ import os
2+ import shutil
3+ import subprocess
4+ import sys
5+ from pathlib import Path
6+
7+ import pytest
8+
9+ #region Global Variables
10+
11+ ROOT = Path (__file__ ).resolve ().parent .parent
12+ SCRIPT = ROOT / 'tools.sh'
13+ VENV_TEST = ROOT / '.venv_test'
14+ PACKAGE_NAME = 'stat_log_db'
15+
16+ #endregion
17+
18+
19+ #region testing tools
20+
21+ def _ensure_test_venv ():
22+ """Ensure the test virtual environment is created."""
23+ if not VENV_TEST .exists ():
24+ subprocess .run ([sys .executable , '-m' , 'venv' , str (VENV_TEST )], check = True )
25+
26+ def _venv_python ():
27+ """Return path to the virtual environment's python interpreter."""
28+ return VENV_TEST / ('Scripts' if os .name == 'nt' else 'bin' ) / ('python.exe' if os .name == 'nt' else 'python' )
29+
30+ def is_installed (package : str ) -> bool :
31+ """
32+ Check if a package is installed in the test virtual environment using 'pip show'.
33+ Assumes the test venv has been created.
34+ """
35+ _ensure_test_venv ()
36+ python_executable = _venv_python ()
37+ result = subprocess .run ([str (python_executable ), '-m' , 'pip' , 'show' , package ], capture_output = True , text = True )
38+ return result .returncode == 0
39+
40+ def run_tools (args , use_test_venv = False ):
41+ """Run tools.sh returning (code, stdout+stderr)."""
42+ env = os .environ .copy ()
43+ if use_test_venv :
44+ _ensure_test_venv ()
45+ scripts_dir = VENV_TEST / ('Scripts' if os .name == 'nt' else 'bin' )
46+ env ['PATH' ] = str (scripts_dir ) + os .pathsep + env .get ('PATH' , '' )
47+ env ['VIRTUAL_ENV' ] = str (VENV_TEST )
48+ env ['PYTHONHOME' ] = '' # ensure venv python resolution
49+ bash = r'C:\Program Files\Git\bin\bash.exe' if os .name == 'nt' else 'bash' # TODO: indicate to the user that they need git bash
50+ proc = subprocess .run ([bash , str (SCRIPT ), * args ], capture_output = True , text = True , cwd = ROOT , env = env )
51+ return proc .returncode , proc .stdout + proc .stderr
52+
53+ #endregion
54+
55+
56+ @pytest .fixture () # scope="module"
57+ def test_venv ():
58+ """
59+ Provision an isolated virtual environment used for install/uninstall tests.
60+ The directory is removed after all related tests complete.
61+ """
62+ _ensure_test_venv ()
63+ yield VENV_TEST
64+ # Teardown: remove the virtual environment directory
65+ if VENV_TEST .exists ():
66+ shutil .rmtree (VENV_TEST )
67+
68+
69+ def test_help ():
70+ code , out = run_tools (['-h' ])
71+ assert code == 0
72+ # Read README.md
73+ readme_path = ROOT / 'README.md'
74+ assert readme_path .exists (), f"README not found at { readme_path } "
75+ readme_content = None
76+ with open (readme_path , 'r' , encoding = 'utf-8' ) as f :
77+ readme_content = f .read ().strip ()
78+ assert not (readme_content is None ), "Unable to read README"
79+ # Compare README content with help output
80+ try :
81+ assert out == readme_content , "Help output does not match README content"
82+ except AssertionError :
83+ assert out .strip () == readme_content .strip (), "Help output does not match README content (leading & trailing whitespace stripped)"
84+
85+ def test_install_dev (test_venv ):
86+ code , out = run_tools (['-id' ], use_test_venv = True )
87+ assert code == 0
88+ assert 'Installing' in out
89+ assert 'dev' in out
90+ assert is_installed (PACKAGE_NAME ), 'Package should be installed after dev install'
91+
92+ def test_install_normal (test_venv ):
93+ code , out = run_tools (['-in' ], use_test_venv = True )
94+ assert code == 0
95+ assert 'Installing' in out
96+ assert 'dev' not in out
97+ assert is_installed (PACKAGE_NAME ), 'Package should be installed after normal install'
98+
99+ def test_install_invalid_arg (test_venv ):
100+ code , out = run_tools (['-ix' ], use_test_venv = True )
101+ assert code == 1
102+ assert ('Unsupported argument' in out ) or ('Invalid install mode' in out )
103+ assert not is_installed (PACKAGE_NAME ), 'Package should not be installed after invalid install argument'
104+
105+ def test_uninstall (test_venv ):
106+ # Ensure something installed first (dev mode)
107+ icode , iout = run_tools (['-id' ], use_test_venv = True )
108+ assert icode == 0
109+ assert is_installed (PACKAGE_NAME ), 'Package should be installed (before uninstall)'
110+ ucode , uout = run_tools (['-u' ], use_test_venv = True )
111+ assert ucode == 0
112+ assert 'Uninstalling' in uout
113+ assert 'Uninstall complete' in uout
114+ assert not is_installed (PACKAGE_NAME ), 'Package should not be installed after uninstall'
115+
116+ def test_install_and_clean_multi_flag (test_venv ):
117+ code , out = run_tools (['-id' , '-c' ], use_test_venv = True )
118+ assert code == 0
119+ assert is_installed (PACKAGE_NAME ), 'Package should be installed'
120+ assert 'Installing' in out
121+ assert 'Cleaning up workspace' in out
122+ assert 'Cleanup complete' in out
123+ assert is_installed (PACKAGE_NAME ), 'Cleanup should not remove installed package'
124+
125+ def test_test_no_arg ():
126+ code , out = run_tools (['-t' ])
127+ assert code == 1
128+ try :
129+ assert out == 'Option -t requires an argument'
130+ except AssertionError :
131+ assert out .strip () == 'Option -t requires an argument'
132+
133+ def test_test_invalid_arg ():
134+ code , out = run_tools (['-tx' ])
135+ assert code == 1
136+ assert ('Unsupported argument' in out ) or ('Invalid test mode' in out )
137+
138+ def test_clean ():
139+ code , out = run_tools (['-c' ])
140+ assert code == 0
141+ assert 'Cleaning up workspace' in out
142+ assert 'Cleanup complete' in out
0 commit comments