test(e2e,verify): enhance live API verification and dynamic port binding in E2E runner
- Update verify_counts.py to validate SQLite database records and live Go REST API endpoints directly - Harden E2E test runner with dynamic free port binding (get_free_port) to prevent port collisions - Add exact regex matching for dynamic (ƒ) and static (○) route table allocations - Ignore root build binary artifacts in backend/.gitignore
This commit is contained in:
@@ -86,31 +86,38 @@ def test_build():
|
||||
print(f"❌ Build failed:\n{res.stdout}\n{res.stderr}")
|
||||
return False
|
||||
|
||||
lines = res.stdout.splitlines()
|
||||
|
||||
# Verify exact dynamic routes
|
||||
# Parse exact Next.js route table output lines (e.g. "┌ ƒ /", "├ ○ /_not-found", "├ ƒ /publications", "├ ƒ /publications/[category]")
|
||||
route_table_map = {}
|
||||
for line in res.stdout.splitlines():
|
||||
m = re.match(r'^[┌├└]\s+([ƒ○●])\s+(\S+)', line.strip())
|
||||
if m:
|
||||
mode, route = m.group(1), m.group(2)
|
||||
route_table_map[route] = mode
|
||||
|
||||
print(f"Parsed route modes from build table ({len(route_table_map)} routes): {route_table_map}")
|
||||
|
||||
# Check exact match for each dynamic route
|
||||
for d_route in EXPECTED_DYNAMIC_ROUTES:
|
||||
found = False
|
||||
for line in lines:
|
||||
if d_route in line and ("ƒ" in line or "Dynamic" in line):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
print(f"❌ Gate 2 Violation: Expected dynamic route '{d_route}' not marked with 'ƒ Dynamic' in build report")
|
||||
mode = route_table_map.get(d_route)
|
||||
if mode != "ƒ":
|
||||
print(f"❌ Gate 2 Violation: Expected dynamic route '{d_route}' has mode '{mode}', expected 'ƒ'")
|
||||
return False
|
||||
|
||||
# Verify exact static routes
|
||||
# Check exact match for each static route
|
||||
for s_route in EXPECTED_STATIC_ROUTES:
|
||||
found = False
|
||||
for line in lines:
|
||||
if s_route in line and ("○" in line or "Static" in line):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
print(f"❌ Gate 2 Violation: Expected static route '{s_route}' not marked with '○ Static' in build report")
|
||||
mode = route_table_map.get(s_route)
|
||||
if mode not in ("○", "●"):
|
||||
print(f"❌ Gate 2 Violation: Expected static route '{s_route}' has mode '{mode}', expected '○' or '●'")
|
||||
return False
|
||||
|
||||
print("✅ Gate 2 PASSED: Hybrid dynamic/static route allocation verified (6 dynamic routes, 4 static routes)")
|
||||
# Check that no other unexpected routes exist in table
|
||||
all_expected = set(EXPECTED_DYNAMIC_ROUTES + EXPECTED_STATIC_ROUTES)
|
||||
found_routes = set(route_table_map.keys())
|
||||
if all_expected != found_routes:
|
||||
print(f"❌ Gate 2 Violation: Route set mismatch. Missing: {all_expected - found_routes}, Extra: {found_routes - all_expected}")
|
||||
return False
|
||||
|
||||
print("✅ Gate 2 PASSED: Hybrid dynamic/static route allocation verified (6 dynamic routes, 4 static routes with exact table matching)")
|
||||
return True
|
||||
|
||||
def test_tsc():
|
||||
@@ -122,6 +129,13 @@ def test_tsc():
|
||||
print("✅ Gate 3 PASSED: TypeScript type check clean (exit code 0)")
|
||||
return True
|
||||
|
||||
import socket
|
||||
|
||||
def get_free_port():
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(('', 0))
|
||||
return s.getsockname()[1]
|
||||
|
||||
def start_next_server(port=3000):
|
||||
print(f"\nStarting live Next.js server on port {port}...")
|
||||
server_env = os.environ.copy()
|
||||
@@ -222,10 +236,10 @@ def test_theme_tokens():
|
||||
print("✅ Gate 6 PASSED: Light/Dark theme CSS variables and Tailwind tokens present")
|
||||
return True
|
||||
|
||||
def test_unittest_suite():
|
||||
def test_unittest_suite(port=3000):
|
||||
print("\n--- Gate 7: DOM & HTML Content Assertions (Unittest) ---")
|
||||
test_path = os.path.join(REFER_DIR, "tests", "e2e_test_suite.py")
|
||||
res = run_cmd([sys.executable, "-m", "unittest", test_path], cwd=ROOT_DIR, env={"TEST_SERVER_URL": "http://localhost:3000"})
|
||||
res = run_cmd([sys.executable, "-m", "unittest", test_path], cwd=ROOT_DIR, env={"TEST_SERVER_URL": f"http://localhost:{port}"})
|
||||
if res.returncode != 0:
|
||||
print(f"❌ Unittest suite failed:\n{res.stdout}\n{res.stderr}")
|
||||
return False
|
||||
@@ -439,13 +453,14 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
# Start live Next.js server once and run Gate 4, Gate 7, Gate 8
|
||||
port = get_free_port()
|
||||
server_proc = None
|
||||
try:
|
||||
server_proc = start_next_server(port=3000)
|
||||
bodies = fetch_live_routes("http://localhost:3000")
|
||||
server_proc = start_next_server(port=port)
|
||||
bodies = fetch_live_routes(f"http://localhost:{port}")
|
||||
|
||||
success &= test_live_routes(bodies)
|
||||
success &= test_unittest_suite()
|
||||
success &= test_unittest_suite(port=port)
|
||||
success &= test_layout_architecture(bodies)
|
||||
finally:
|
||||
if server_proc:
|
||||
|
||||
Reference in New Issue
Block a user