Skip to content

Commit 7eb444c

Browse files
committed
feat: Add enhanced ObjectScript validation script
- Intelligent brace validation prevents compilation errors - Handles legitimate ObjectScript Try/Catch patterns - Catches problematic consecutive closing braces - Closes validation gap that allowed GitHub CI failures
1 parent 3d74f27 commit 7eb444c

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed

β€Žscripts/test_zpm_compilation.py

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test script to validate ZPM package compilation without requiring IRIS admin access.
4+
This script performs comprehensive validation of all ZPM package components.
5+
"""
6+
7+
import subprocess
8+
import sys
9+
from pathlib import Path
10+
import xml.etree.ElementTree as ET
11+
12+
def test_module_xml():
13+
"""Test module.xml structure and required elements."""
14+
print("πŸ” Testing module.xml structure...")
15+
16+
module_xml = Path("module.xml")
17+
if not module_xml.exists():
18+
print("❌ module.xml not found")
19+
return False
20+
21+
try:
22+
tree = ET.parse(module_xml)
23+
root = tree.getroot()
24+
25+
# Check required elements
26+
required_elements = [
27+
".//Name",
28+
".//Version",
29+
".//Description",
30+
".//Dependencies",
31+
".//Packaging",
32+
".//Lifecycle"
33+
]
34+
35+
for element_path in required_elements:
36+
element = root.find(element_path)
37+
if element is None:
38+
print(f"❌ Missing required element: {element_path}")
39+
return False
40+
41+
print("βœ… module.xml structure is valid")
42+
return True
43+
44+
except ET.ParseError as e:
45+
print(f"❌ module.xml parse error: {e}")
46+
return False
47+
48+
def test_objectscript_syntax():
49+
"""Test ObjectScript class file syntax."""
50+
print("πŸ” Testing ObjectScript class syntax...")
51+
52+
objectscript_dir = Path("objectscript")
53+
if not objectscript_dir.exists():
54+
print("❌ objectscript directory not found")
55+
return False
56+
57+
cls_files = list(objectscript_dir.rglob("*.CLS"))
58+
if not cls_files:
59+
print("❌ No .CLS files found")
60+
return False
61+
62+
for cls_file in cls_files:
63+
try:
64+
rel_path = cls_file.relative_to(Path.cwd())
65+
except ValueError:
66+
rel_path = cls_file
67+
print(f" Checking {rel_path}")
68+
69+
# Basic syntax checks
70+
content = cls_file.read_text()
71+
72+
# Check for basic ObjectScript class structure
73+
if not content.startswith("///") and "Class " not in content:
74+
print(f"❌ {cls_file.name}: Missing class declaration")
75+
return False
76+
77+
# Check for balanced braces (simple check)
78+
open_braces = content.count("{")
79+
close_braces = content.count("}")
80+
if open_braces != close_braces:
81+
print(f"❌ {cls_file.name}: Unbalanced braces ({open_braces} open, {close_braces} close)")
82+
return False
83+
84+
# Enhanced brace validation - check for problematic patterns that cause compilation errors
85+
lines = content.split('\n')
86+
brace_stack = []
87+
88+
for i, line in enumerate(lines, 1):
89+
stripped = line.strip()
90+
91+
# Track brace context for better validation
92+
if '{' in line:
93+
brace_stack.append(i)
94+
if '}' in line and brace_stack:
95+
brace_stack.pop()
96+
97+
# Only flag truly suspicious patterns - consecutive closing braces at same indentation level
98+
# that don't correspond to nested structures
99+
if (stripped == '}' and i < len(lines) - 1):
100+
next_line = lines[i].strip() if i < len(lines) else ""
101+
if next_line == '}':
102+
# Check indentation to see if these are at the same level (suspicious)
103+
current_indent = len(line) - len(line.lstrip())
104+
next_indent = len(lines[i]) - len(lines[i].lstrip()) if i < len(lines) else 0
105+
106+
# Flag only if same indentation (likely error) and not in obvious nested context
107+
if current_indent == next_indent and current_indent == 0:
108+
print(f"❌ {cls_file.name}: Suspicious consecutive closing braces at same indentation at lines {i} and {i+1}")
109+
print(f" Line {i}: '{line}'")
110+
print(f" Line {i+1}: '{next_line}'")
111+
return False
112+
113+
print(f"βœ… All {len(cls_files)} ObjectScript files have valid syntax")
114+
return True
115+
116+
def test_ipm_validators():
117+
"""Run the IPM package validators."""
118+
print("πŸ” Running IPM package validators...")
119+
120+
# Test basic IPM package validator
121+
try:
122+
result = subprocess.run([
123+
sys.executable, "scripts/validate_ipm_package.py", "."
124+
], capture_output=True, text=True, timeout=30)
125+
126+
if result.returncode != 0:
127+
print("❌ Basic IPM package validation failed")
128+
print("STDOUT:", result.stdout)
129+
print("STDERR:", result.stderr)
130+
return False
131+
132+
if "βœ… IPM package validation PASSED" not in result.stdout:
133+
print("❌ Basic IPM package validation did not pass")
134+
return False
135+
136+
except subprocess.TimeoutExpired:
137+
print("❌ Basic IPM package validator timed out")
138+
return False
139+
except Exception as e:
140+
print(f"❌ Error running basic IPM validator: {e}")
141+
return False
142+
143+
# Test comprehensive IPM module validator
144+
try:
145+
result = subprocess.run([
146+
sys.executable, "scripts/utilities/validate_ipm_module.py",
147+
"--project-root", "."
148+
], capture_output=True, text=True, timeout=30)
149+
150+
if result.returncode != 0:
151+
print("❌ Comprehensive IPM module validation failed")
152+
print("STDOUT:", result.stdout)
153+
print("STDERR:", result.stderr)
154+
return False
155+
156+
if "βœ… PASSED" not in result.stdout:
157+
print("❌ Comprehensive IPM module validation did not pass")
158+
return False
159+
160+
except subprocess.TimeoutExpired:
161+
print("❌ Comprehensive IPM module validator timed out")
162+
return False
163+
except Exception as e:
164+
print(f"❌ Error running comprehensive IPM validator: {e}")
165+
return False
166+
167+
print("βœ… All IPM validators passed")
168+
return True
169+
170+
def test_python_package_structure():
171+
"""Test Python package structure."""
172+
print("πŸ” Testing Python package structure...")
173+
174+
required_packages = ["iris_rag", "rag_templates", "common"]
175+
176+
for package in required_packages:
177+
package_path = Path(package)
178+
if not package_path.exists():
179+
print(f"❌ Missing Python package: {package}")
180+
return False
181+
182+
init_file = package_path / "__init__.py"
183+
if not init_file.exists():
184+
print(f"❌ Missing __init__.py in {package}")
185+
return False
186+
187+
print(f"βœ… All {len(required_packages)} Python packages are valid")
188+
return True
189+
190+
def main():
191+
"""Run all ZPM compilation tests."""
192+
print("πŸ§ͺ ZPM Package Compilation Test")
193+
print("=" * 50)
194+
195+
tests = [
196+
("Module XML Structure", test_module_xml),
197+
("ObjectScript Syntax", test_objectscript_syntax),
198+
("Python Package Structure", test_python_package_structure),
199+
("IPM Validators", test_ipm_validators),
200+
]
201+
202+
passed = 0
203+
total = len(tests)
204+
205+
for test_name, test_func in tests:
206+
print(f"\nπŸ“‹ {test_name}")
207+
try:
208+
if test_func():
209+
passed += 1
210+
else:
211+
print(f"❌ {test_name} FAILED")
212+
except Exception as e:
213+
print(f"❌ {test_name} ERROR: {e}")
214+
215+
print("\n" + "=" * 50)
216+
print(f"🎯 Results: {passed}/{total} tests passed")
217+
218+
if passed == total:
219+
print("πŸŽ‰ ZPM package compilation test PASSED")
220+
print("βœ… Package is ready for deployment")
221+
return 0
222+
else:
223+
print("❌ ZPM package compilation test FAILED")
224+
print("πŸ”§ Please fix the issues above before deployment")
225+
return 1
226+
227+
if __name__ == "__main__":
228+
sys.exit(main())

0 commit comments

Comments
Β (0)