33Integration Test Runner
44
55Runs pytest unit tests for integration directories that have test_*_unit.py files.
6+ Each integration is tested separately with its own dependencies installed from its
7+ requirements.txt, so SDK version pins are respected per integration.
68
79Usage:
810 python scripts/run_tests.py [dir ...]
@@ -68,12 +70,51 @@ def get_integration_dirs(args: list[str]) -> list[Path]:
6870
6971 # Auto-detect: all subdirectories with a config.json
7072 return sorted (
71- p
72- for p in Path ("." ).iterdir ()
73- if p .is_dir () and p .name not in SKIP_FOLDERS and (p / "config.json" ).exists ()
73+ p for p in Path ("." ).iterdir () if p .is_dir () and p .name not in SKIP_FOLDERS and (p / "config.json" ).exists ()
7474 )
7575
7676
77+ def install_integration_deps (integration_dir : Path ) -> bool :
78+ """Install an integration's requirements.txt. Returns True on success."""
79+ req_file = integration_dir / "requirements.txt"
80+ if not req_file .is_file ():
81+ return True
82+
83+ result = subprocess .run (
84+ [sys .executable , "-m" , "pip" , "install" , "-r" , str (req_file ), "-q" ],
85+ capture_output = True ,
86+ text = True ,
87+ )
88+ if result .returncode != 0 :
89+ print (f" ❌ Failed to install dependencies for { integration_dir .name } " )
90+ if result .stderr .strip ():
91+ for line in result .stderr .strip ().splitlines ():
92+ print (f" { line } " )
93+ return False
94+ return True
95+
96+
97+ def run_integration_tests (integration_dir : Path , test_files : list [Path ]) -> int :
98+ """Run pytest for a single integration. Returns the pytest exit code."""
99+ cmd = [
100+ sys .executable ,
101+ "-m" ,
102+ "pytest" ,
103+ "--import-mode=importlib" ,
104+ "-m" ,
105+ "unit" ,
106+ "-v" ,
107+ "--tb=short" ,
108+ "--no-header" ,
109+ "--cov" ,
110+ str (integration_dir ),
111+ "--cov-report=term-missing:skip-covered" ,
112+ * [str (f ) for f in test_files ],
113+ ]
114+
115+ return subprocess .run (cmd ).returncode
116+
117+
77118def main () -> int :
78119 args = sys .argv [1 :]
79120 dirs = get_integration_dirs (args )
@@ -99,38 +140,37 @@ def main() -> int:
99140 print ("⚠️ No unit tests to run" )
100141 return 0
101142
102- # Build pytest command
103- test_file_args = []
104- cov_args = []
105- for d , files in testable :
106- cov_args .extend (["--cov" , str (d )])
107- for f in files :
108- test_file_args .append (str (f ))
109-
110- cmd = [
111- sys .executable , "-m" , "pytest" ,
112- "--import-mode=importlib" ,
113- "-m" , "unit" ,
114- "-v" ,
115- "--tb=short" ,
116- "--no-header" ,
117- * cov_args ,
118- "--cov-report=term-missing:skip-covered" ,
119- * test_file_args ,
120- ]
121-
122143 print (f"🧪 Running unit tests for: { ', ' .join (d .name for d , _ in testable )} " )
123144 print ()
124145
125- result = subprocess .run (cmd )
126-
127- print ()
128- if result .returncode == 0 :
129- print (f"✅ Tests passed for { len (testable )} integration(s)" )
130- else :
131- print (f"❌ Tests failed (exit code { result .returncode } )" )
132-
133- return result .returncode
146+ # Run each integration separately with its own dependencies
147+ failed = []
148+ passed = []
149+ for d , test_files in testable :
150+ print (f"{ '=' * 60 } " )
151+ print (f" { d .name } " )
152+ print (f"{ '=' * 60 } " )
153+
154+ if not install_integration_deps (d ):
155+ failed .append (d .name )
156+ continue
157+
158+ exit_code = run_integration_tests (d , test_files )
159+ if exit_code == 0 :
160+ passed .append (d .name )
161+ else :
162+ failed .append (d .name )
163+ print ()
164+
165+ # Summary
166+ print ("=" * 60 )
167+ if passed :
168+ print (f"✅ Tests passed: { ', ' .join (passed )} " )
169+ if failed :
170+ print (f"❌ Tests failed: { ', ' .join (failed )} " )
171+ print ("=" * 60 )
172+
173+ return 1 if failed else 0
134174
135175
136176if __name__ == "__main__" :
0 commit comments