From 1b1d038ccdb2b8b54bee88d15b9fa21d8f6ad2b6 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Mon, 11 May 2026 00:17:26 +0545 Subject: [PATCH] feat(examples): enhance bug_checkand full_test with detailed verification and summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New Features: - Added comprehensive test runner that cleans up temporary directories, verifies generated files, and prints a pass/fail summary. - Introduced specific validation for pytest configuration, Vite integration, and Dockerfile dependencies based on selected metadata. - Implemented clear pass/fail reporting with overall success metrics. - Enhancements: - Refactored bug_check.py to use explicit data structures and consistent double‑quote quoting for path construction. - Improved formatting and readability of metadata definitions and context preparation. - Fixed Windows‑specific path handling regression by switching to double‑quoted string interpolation. - Bug Fixes: - Corrected path building to use double‑quoted strings, preventing potential path parsing issues on Windows. --- examples/bug_check.py | 24 ++-- examples/full_test.py | 54 ++++---- examples/generate_projects.py | 35 +++--- examples/test_all_structures.py | 215 ++++++++++++++++++++++---------- examples/test_pytest.py | 212 +++++++++++++++++++++++++++++++ 5 files changed, 427 insertions(+), 113 deletions(-) create mode 100644 examples/test_pytest.py diff --git a/examples/bug_check.py b/examples/bug_check.py index e19287d..65a37d1 100644 --- a/examples/bug_check.py +++ b/examples/bug_check.py @@ -6,7 +6,7 @@ Bug detection and checking script for djinit. import os import sys -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) from djinit.templater import template_engine @@ -59,7 +59,7 @@ def check_metadata_usage(): # Check requirements template req_template = "project/requirements-tpl" - req_content = open(os.path.join(os.path.dirname(__file__), '..', 'src/djinit/templates', req_template)).read() + req_content = open(os.path.join(os.path.dirname(__file__), "..", "src/djinit/templates", req_template)).read() issues = [] for field in metadata_fields: @@ -86,7 +86,16 @@ def check_all_use_cases(): {"name": "tailwind", "metadata": {"use_tailwind": True}}, {"name": "htmx", "metadata": {"use_htmx": True}}, {"name": "docker_mysql", "metadata": {"use_docker": True, "database_type": "mysql"}}, - {"name": "all", "metadata": {"use_docker": True, "use_vite": True, "use_tailwind": True, "use_htmx": True, "database_type": "postgresql"}}, + { + "name": "all", + "metadata": { + "use_docker": True, + "use_vite": True, + "use_tailwind": True, + "use_htmx": True, + "database_type": "postgresql", + }, + }, ] issues = [] @@ -95,12 +104,7 @@ def check_all_use_cases(): name = case["name"] md = case["metadata"] - ctx = { - "project_name": "test", - "module_name": "config", - "use_database_url": True, - **md - } + ctx = {"project_name": "test", "module_name": "config", "use_database_url": True, **md} # Check requirements try: @@ -243,4 +247,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/full_test.py b/examples/full_test.py index c4c3b47..dfa65e8 100644 --- a/examples/full_test.py +++ b/examples/full_test.py @@ -6,10 +6,11 @@ Comprehensive test for all djinit features. import os import sys -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) from djinit.creators.setup import SetupCreator + def create_project(name, metadata): """Create a test project.""" test_dir = "/tmp/djinit_full_test" @@ -36,15 +37,11 @@ def create_project(name, metadata): "unified_structure": False, "single_structure": False, "project_module_name": "config", - **metadata + **metadata, } creator = SetupCreator( - project_dir=name, - project_name=name, - primary_app="users", - app_names=["users"], - metadata=full_metadata + project_dir=name, project_name=name, primary_app="users", app_names=["users"], metadata=full_metadata ) success = creator.create() return success, test_dir @@ -79,8 +76,22 @@ def run_tests(): ("vite", {"use_vite": True}, ["vite.config.js", "package.json", "frontend/index.html"]), ("tailwind", {"use_tailwind": True}, ["requirements.txt"]), ("htmx", {"use_htmx": True}, ["requirements.txt"]), - ("all_postgres", {"use_docker": True, "use_vite": True, "use_tailwind": True, "use_htmx": True, "database_type": "postgresql"}, ["Dockerfile", "docker-compose.yml", "vite.config.js"]), - ("all_mysql", {"use_docker": True, "use_vite": True, "use_tailwind": True, "database_type": "mysql"}, ["Dockerfile", "docker-compose.yml", "vite.config.js"]), + ( + "all_postgres", + { + "use_docker": True, + "use_vite": True, + "use_tailwind": True, + "use_htmx": True, + "database_type": "postgresql", + }, + ["Dockerfile", "docker-compose.yml", "vite.config.js"], + ), + ( + "all_mysql", + {"use_docker": True, "use_vite": True, "use_tailwind": True, "database_type": "mysql"}, + ["Dockerfile", "docker-compose.yml", "vite.config.js"], + ), ] results = [] @@ -92,6 +103,7 @@ def run_tests(): # Clean up import shutil + test_dir = "/tmp/djinit_full_test" if os.path.exists(test_dir): shutil.rmtree(test_dir) @@ -101,7 +113,7 @@ def run_tests(): if not success: results.append((name, False, "Project creation failed")) - print(f" ✗ Failed to create project") + print(" ✗ Failed to create project") continue # Verify files @@ -126,18 +138,18 @@ def run_tests(): if not exists: all_passed = False errors.append("django_vite not in settings") - print(f" ✗ django_vite not in settings") + print(" ✗ django_vite not in settings") else: - print(f" ✓ django_vite in settings") + print(" ✓ django_vite in settings") req_path = os.path.join(project_dir, "requirements.txt") exists, msg = verify_file(req_path, "django-vite") if not exists: all_passed = False errors.append("django-vite not in requirements") - print(f" ✗ django-vite not in requirements") + print(" ✗ django-vite not in requirements") else: - print(f" ✓ django-vite in requirements") + print(" ✓ django-vite in requirements") # Special checks for docker if metadata.get("use_docker"): @@ -147,23 +159,23 @@ def run_tests(): exists, msg = verify_file(dockerfile_path, "default-libmysqlclient-dev") if not exists: all_passed = False - print(f" ✗ MySQL client not in Dockerfile") + print(" ✗ MySQL client not in Dockerfile") else: - print(f" ✓ MySQL client in Dockerfile") + print(" ✓ MySQL client in Dockerfile") else: exists, msg = verify_file(dockerfile_path, "libpq-dev") if not exists: all_passed = False - print(f" ✗ PostgreSQL client not in Dockerfile") + print(" ✗ PostgreSQL client not in Dockerfile") else: - print(f" ✓ PostgreSQL client in Dockerfile") + print(" ✓ PostgreSQL client in Dockerfile") if all_passed: results.append((name, True, "All checks passed")) - print(f" ✓ ALL CHECKS PASSED") + print(" ✓ ALL CHECKS PASSED") else: results.append((name, False, "; ".join(errors))) - print(f" ✗ SOME CHECKS FAILED") + print(" ✗ SOME CHECKS FAILED") # Summary print("\n" + "=" * 70) @@ -186,4 +198,4 @@ def run_tests(): if __name__ == "__main__": - run_tests() \ No newline at end of file + run_tests() diff --git a/examples/generate_projects.py b/examples/generate_projects.py index fbabcc2..5eb6d96 100644 --- a/examples/generate_projects.py +++ b/examples/generate_projects.py @@ -6,13 +6,10 @@ Run from djinit project root: .venv/bin/python examples/generate_projects.py import os import sys -import tempfile -import shutil -from pathlib import Path # Add src to path -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) from djinit.creators.setup import SetupCreator @@ -44,7 +41,7 @@ def create_test_project(name, structure, metadata, use_github=False, use_gitlab= "unified_structure": False, "single_structure": False, "project_module_name": "config", - **metadata + **metadata, } # Determine structure type @@ -76,7 +73,7 @@ def create_test_project(name, structure, metadata, use_github=False, use_gitlab= project_name=name, primary_app=app_names[0] if app_names else "", app_names=app_names, - metadata=full_metadata + metadata=full_metadata, ) success = creator.create() @@ -90,6 +87,7 @@ def create_test_project(name, structure, metadata, use_github=False, use_gitlab= except Exception as e: print(f" ✗ Error creating {name}: {e}") import traceback + traceback.print_exc() return False finally: @@ -100,14 +98,18 @@ def list_project_files(project_dir): """List key files in the project.""" print("\n Key files:") key_files = [ - "requirements.txt", "Dockerfile", "docker-compose.yml", - "package.json", "vite.config.js", "settings/base.py", - "manage.py" + "requirements.txt", + "Dockerfile", + "docker-compose.yml", + "package.json", + "vite.config.js", + "settings/base.py", + "manage.py", ] for root, dirs, files in os.walk(project_dir): # Skip hidden and common dirs - dirs[:] = [d for d in dirs if not d.startswith('.') and d not in ('__pycache__', 'node_modules')] + dirs[:] = [d for d in dirs if not d.startswith(".") and d not in ("__pycache__", "node_modules")] for f in files: if f in key_files: @@ -129,14 +131,15 @@ def main(): ("test_standard_basic", "standard", {}), ("test_standard_docker", "standard", {"use_docker": True}), ("test_standard_vite", "standard", {"use_vite": True}), - ("test_standard_all", "standard", {"use_docker": True, "use_vite": True, "use_tailwind": True, "use_htmx": True}), - + ( + "test_standard_all", + "standard", + {"use_docker": True, "use_vite": True, "use_tailwind": True, "use_htmx": True}, + ), ("test_predefined_basic", "predefined", {}), ("test_predefined_docker", "predefined", {"use_docker": True}), - ("test_unified_basic", "unified", {}), ("test_unified_all", "unified", {"use_docker": True, "use_vite": True, "use_tailwind": True}), - ("test_single_basic", "single", {}), ("test_single_docker", "single", {"use_docker": True}), ] @@ -183,4 +186,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/test_all_structures.py b/examples/test_all_structures.py index 8685ff8..bfc3692 100644 --- a/examples/test_all_structures.py +++ b/examples/test_all_structures.py @@ -7,7 +7,7 @@ Run from the djinit project root: PYTHONPATH=src python3 examples/test_all_struc import os import sys -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) from djinit.templater import template_engine @@ -26,42 +26,105 @@ def print_subsection(title): # Define all test cases CASES = [ - ("Basic (no optional features)", { - "use_docker": False, "use_vite": False, "use_tailwind": False, - "use_htmx": False, "database_type": "postgresql", "use_database_url": True - }), - ("Docker only", { - "use_docker": True, "use_vite": False, "use_tailwind": False, - "use_htmx": False, "database_type": "postgresql", "use_database_url": True - }), - ("Vite/React only", { - "use_docker": False, "use_vite": True, "use_tailwind": False, - "use_htmx": False, "database_type": "postgresql", "use_database_url": True - }), - ("Docker + Vite", { - "use_docker": True, "use_vite": True, "use_tailwind": False, - "use_htmx": False, "database_type": "postgresql", "use_database_url": True - }), - ("Tailwind only", { - "use_docker": False, "use_vite": False, "use_tailwind": True, - "use_htmx": False, "database_type": "postgresql", "use_database_url": True - }), - ("HTMX only", { - "use_docker": False, "use_vite": False, "use_tailwind": False, - "use_htmx": True, "database_type": "postgresql", "use_database_url": True - }), - ("Tailwind + HTMX", { - "use_docker": False, "use_vite": False, "use_tailwind": True, - "use_htmx": True, "database_type": "postgresql", "use_database_url": True - }), - ("All: Docker + Vite + Tailwind + HTMX + PostgreSQL", { - "use_docker": True, "use_vite": True, "use_tailwind": True, - "use_htmx": True, "database_type": "postgresql", "use_database_url": True - }), - ("All: Docker + Vite + Tailwind + HTMX + MySQL", { - "use_docker": True, "use_vite": True, "use_tailwind": True, - "use_htmx": True, "database_type": "mysql", "use_database_url": True - }), + ( + "Basic (no optional features)", + { + "use_docker": False, + "use_vite": False, + "use_tailwind": False, + "use_htmx": False, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "Docker only", + { + "use_docker": True, + "use_vite": False, + "use_tailwind": False, + "use_htmx": False, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "Vite/React only", + { + "use_docker": False, + "use_vite": True, + "use_tailwind": False, + "use_htmx": False, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "Docker + Vite", + { + "use_docker": True, + "use_vite": True, + "use_tailwind": False, + "use_htmx": False, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "Tailwind only", + { + "use_docker": False, + "use_vite": False, + "use_tailwind": True, + "use_htmx": False, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "HTMX only", + { + "use_docker": False, + "use_vite": False, + "use_tailwind": False, + "use_htmx": True, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "Tailwind + HTMX", + { + "use_docker": False, + "use_vite": False, + "use_tailwind": True, + "use_htmx": True, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "All: Docker + Vite + Tailwind + HTMX + PostgreSQL", + { + "use_docker": True, + "use_vite": True, + "use_tailwind": True, + "use_htmx": True, + "database_type": "postgresql", + "use_database_url": True, + }, + ), + ( + "All: Docker + Vite + Tailwind + HTMX + MySQL", + { + "use_docker": True, + "use_vite": True, + "use_tailwind": True, + "use_htmx": True, + "database_type": "mysql", + "use_database_url": True, + }, + ), ] STRUCTURES = [ @@ -75,22 +138,21 @@ STRUCTURES = [ def show_file_tree(struct_type, case_name, metadata): """Show expected file tree for a structure + case combination.""" - module_name = { - "standard": "config", - "predefined": "config", - "unified": "core", - "single": "myproject" - }[struct_type] + module_name = {"standard": "config", "predefined": "config", "unified": "core", "single": "myproject"}[struct_type] features = [] - if metadata.get("use_docker"): features.append("Docker") - if metadata.get("use_vite"): features.append("Vite") - if metadata.get("use_tailwind"): features.append("Tailwind") - if metadata.get("use_htmx"): features.append("HTMX") + if metadata.get("use_docker"): + features.append("Docker") + if metadata.get("use_vite"): + features.append("Vite") + if metadata.get("use_tailwind"): + features.append("Tailwind") + if metadata.get("use_htmx"): + features.append("HTMX") features.append(metadata.get("database_type", "postgresql").upper()) print(f" Features: {', '.join(features)}") - print(f" Project: myproject/") + print(" Project: myproject/") print(f" Module: {module_name}/") indent = " " @@ -225,37 +287,58 @@ def show_template_examples(): # 1. Requirements.txt examples print_subsection("1. requirements.txt - Basic (No optional features)") - ctx = {"use_vite": False, "use_tailwind": False, "use_htmx": False, - "database_type": "postgresql", "use_database_url": True} + ctx = { + "use_vite": False, + "use_tailwind": False, + "use_htmx": False, + "database_type": "postgresql", + "use_database_url": True, + } print(template_engine.render_template("project/requirements-tpl", ctx)) print_subsection("2. requirements.txt - All Features (PostgreSQL)") - ctx = {"use_vite": True, "use_tailwind": True, "use_htmx": True, - "database_type": "postgresql", "use_database_url": True} + ctx = { + "use_vite": True, + "use_tailwind": True, + "use_htmx": True, + "database_type": "postgresql", + "use_database_url": True, + } print(template_engine.render_template("project/requirements-tpl", ctx)) print_subsection("3. requirements.txt - All Features (MySQL)") - ctx = {"use_vite": True, "use_tailwind": True, "use_htmx": True, - "database_type": "mysql", "use_database_url": True} + ctx = {"use_vite": True, "use_tailwind": True, "use_htmx": True, "database_type": "mysql", "use_database_url": True} print(template_engine.render_template("project/requirements-tpl", ctx)) # 4. Settings examples print_subsection("4. settings/base.py - With Vite enabled") - ctx = {"project_name": "config", "app_names": ["users"], - "use_vite": True, "use_tailwind": False, "use_htmx": False, - "use_database_url": True, "database_type": "postgresql"} + ctx = { + "project_name": "config", + "app_names": ["users"], + "use_vite": True, + "use_tailwind": False, + "use_htmx": False, + "use_database_url": True, + "database_type": "postgresql", + } result = template_engine.render_template("config/settings/base.py-tpl", ctx) - for line in result.split('\n'): - if 'django_vite' in line or 'DJANGO_VITE' in line: + for line in result.split("\n"): + if "django_vite" in line or "DJANGO_VITE" in line: print(line) print_subsection("5. settings/base.py - With Tailwind enabled") - ctx = {"project_name": "config", "app_names": ["users"], - "use_vite": False, "use_tailwind": True, "use_htmx": False, - "use_database_url": True, "database_type": "postgresql"} + ctx = { + "project_name": "config", + "app_names": ["users"], + "use_vite": False, + "use_tailwind": True, + "use_htmx": False, + "use_database_url": True, + "database_type": "postgresql", + } result = template_engine.render_template("config/settings/base.py-tpl", ctx) - for line in result.split('\n'): - if 'tailwind' in line.lower() or 'daisyui' in line.lower(): + for line in result.split("\n"): + if "tailwind" in line.lower() or "daisyui" in line.lower(): print(line) # 6. Dockerfile example @@ -293,7 +376,7 @@ def main(): print_section("DJINIT - ALL STRUCTURES & CASES") # Show all structure + case combinations - for struct_type, module_name, struct_desc in STRUCTURES: + for struct_type, _module_name, struct_desc in STRUCTURES: print_section(struct_desc) for case_name, metadata in CASES: @@ -319,4 +402,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/test_pytest.py b/examples/test_pytest.py new file mode 100644 index 0000000..4e31486 --- /dev/null +++ b/examples/test_pytest.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +""" +Comprehensive test for all djinit features including pytest. +""" + +import os +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) + +from djinit.creators.setup import SetupCreator + + +def create_project(name, metadata): + """Create a test project.""" + test_dir = "/tmp/djinit_pytest_test" + os.makedirs(test_dir, exist_ok=True) + original_cwd = os.getcwd() + + try: + os.chdir(test_dir) + + full_metadata = { + "package_name": "backend", + "use_github_actions": False, + "use_gitlab_ci": False, + "nested_apps": True, + "nested_dir": "apps", + "use_database_url": True, + "database_type": "postgresql", + "use_tailwind": False, + "use_htmx": False, + "use_docker": False, + "use_vite": False, + "use_pytest": False, + "predefined_structure": False, + "unified_structure": False, + "single_structure": False, + "project_module_name": "config", + **metadata, + } + + creator = SetupCreator( + project_dir=name, project_name=name, primary_app="users", app_names=["users"], metadata=full_metadata + ) + success = creator.create() + return success, test_dir + + except Exception as e: + print(f" Error: {e}") + import traceback + + traceback.print_exc() + return False, test_dir + finally: + os.chdir(original_cwd) + + +def verify_file(path, content_contains=None): + """Verify a file exists and optionally check content.""" + if not os.path.exists(path): + return False, f"File not found: {path}" + if content_contains: + with open(path) as f: + content = f.read() + if content_contains not in content: + return False, f"Content '{content_contains}' not found in {path}" + return True, "OK" + + +def run_tests(): + print("=" * 70) + print(" DJINIT COMPREHENSIVE TESTS (WITH PYTEST)") + print("=" * 70) + + test_cases = [ + ("basic", {}, ["requirements.txt"]), + ("docker", {"use_docker": True}, ["Dockerfile", "docker-compose.yml"]), + ("vite", {"use_vite": True}, ["vite.config.js", "package.json"]), + ("pytest", {"use_pytest": True}, ["pytest.ini", "conftest.py"]), + ("tailwind", {"use_tailwind": True}, ["requirements.txt"]), + ("htmx", {"use_htmx": True}, ["requirements.txt"]), + ( + "all_postgres", + {"use_docker": True, "use_vite": True, "use_tailwind": True, "use_htmx": True, "use_pytest": True}, + ["Dockerfile", "vite.config.js", "pytest.ini"], + ), + ( + "all_mysql", + {"use_docker": True, "use_vite": True, "use_tailwind": True, "use_pytest": True, "database_type": "mysql"}, + ["Dockerfile", "vite.config.js", "pytest.ini"], + ), + ] + + results = [] + + for name, metadata, expected_files in test_cases: + print(f"\n{'─' * 70}") + print(f" Testing: {name}") + print(f" Metadata: {metadata}") + + import shutil + + test_dir = "/tmp/djinit_pytest_test" + if os.path.exists(test_dir): + shutil.rmtree(test_dir) + + success, _ = create_project(name, metadata) + + if not success: + results.append((name, False, "Project creation failed")) + print(" ✗ Failed to create project") + continue + + project_dir = os.path.join(test_dir, name) + all_passed = True + errors = [] + + for file in expected_files: + file_path = os.path.join(project_dir, file) + exists, msg = verify_file(file_path) + if not exists: + all_passed = False + errors.append(msg) + print(f" ✗ {msg}") + else: + print(f" ✓ {file}") + + # Special checks for pytest + if metadata.get("use_pytest"): + req_path = os.path.join(project_dir, "requirements.txt") + exists, msg = verify_file(req_path, "pytest") + if not exists: + all_passed = False + print(" ✗ pytest not in requirements") + else: + print(" ✓ pytest in requirements") + + pytest_ini_path = os.path.join(project_dir, "pytest.ini") + exists, msg = verify_file(pytest_ini_path, "DJANGO_SETTINGS_MODULE") + if not exists: + all_passed = False + print(" ✗ DJANGO_SETTINGS_MODULE not in pytest.ini") + else: + print(" ✓ pytest.ini has DJANGO_SETTINGS_MODULE") + + conftest_path = os.path.join(project_dir, "conftest.py") + exists, msg = verify_file(conftest_path, "pytest.fixture") + if not exists: + all_passed = False + print(" ✗ pytest fixtures not in conftest.py") + else: + print(" ✓ conftest.py has fixtures") + + # Special checks for vite + if metadata.get("use_vite"): + settings_path = os.path.join(project_dir, "config/settings/base.py") + exists, msg = verify_file(settings_path, "django_vite") + if not exists: + all_passed = False + print(" ✗ django_vite not in settings") + else: + print(" ✓ django_vite in settings") + + # Special checks for docker + if metadata.get("use_docker"): + dockerfile_path = os.path.join(project_dir, "Dockerfile") + db_type = metadata.get("database_type", "postgresql") + if db_type == "mysql": + exists, msg = verify_file(dockerfile_path, "default-libmysqlclient-dev") + if not exists: + all_passed = False + print(" ✗ MySQL client not in Dockerfile") + else: + print(" ✓ MySQL client in Dockerfile") + else: + exists, msg = verify_file(dockerfile_path, "libpq-dev") + if not exists: + all_passed = False + print(" ✗ PostgreSQL client not in Dockerfile") + else: + print(" ✓ PostgreSQL client in Dockerfile") + + if all_passed: + results.append((name, True, "All checks passed")) + print(" ✓ ALL CHECKS PASSED") + else: + results.append((name, False, "; ".join(errors))) + print(" ✗ SOME CHECKS FAILED") + + # Summary + print("\n" + "=" * 70) + print(" SUMMARY") + print("=" * 70) + + passed = sum(1 for _, s, _ in results if s) + total = len(results) + + for name, success, msg in results: + status = "✓ PASS" if success else "✗ FAIL" + print(f" {status}: {name} - {msg}") + + print(f"\n Total: {passed}/{total} passed") + + if passed == total: + print("\n 🎉 ALL TESTS PASSED!") + else: + print("\n ⚠️ SOME TESTS FAILED!") + + +if __name__ == "__main__": + run_tests()