From 31091350c9585b3884070c33bf43c0e5fdf3ea4b Mon Sep 17 00:00:00 2001 From: ali Date: Wed, 28 Jan 2026 22:19:40 +0200 Subject: [PATCH] cleanup --- .../aiservice/common/markdown_utils.py | 3 - .../validators/javascript_validator.py | 1 + .../code_repair/code_repair_context.py | 5 +- django/aiservice/languages/__init__.py | 148 +---------- django/aiservice/languages/base.py | 98 ------- django/aiservice/languages/js_ts/__init__.py | 213 +-------------- django/aiservice/languages/python/__init__.py | 80 ------ .../aiservice/languages/python/validator.py | 35 --- .../context_utils/optimizer_context.py | 2 +- .../context_utils/refiner_context.py | 21 +- django/aiservice/optimizer/optimizer.py | 5 - django/aiservice/tests/languages/__init__.py | 1 - .../tests/languages/test_registry.py | 249 ------------------ 13 files changed, 30 insertions(+), 831 deletions(-) delete mode 100644 django/aiservice/languages/base.py delete mode 100644 django/aiservice/languages/python/__init__.py delete mode 100644 django/aiservice/languages/python/validator.py delete mode 100644 django/aiservice/tests/languages/__init__.py delete mode 100644 django/aiservice/tests/languages/test_registry.py diff --git a/django/aiservice/aiservice/common/markdown_utils.py b/django/aiservice/aiservice/common/markdown_utils.py index 6f6b6359e..83d9933c3 100644 --- a/django/aiservice/aiservice/common/markdown_utils.py +++ b/django/aiservice/aiservice/common/markdown_utils.py @@ -10,9 +10,6 @@ import re from aiservice.common.llm_output_utils import truncate_pathological_output -# Matches ```python:filepath blocks, captures (filepath, content) -MARKDOWN_CODE_BLOCK_WITH_PATH_PATTERN = re.compile(r"```python:([^\n]+)\n(.*?)\n```", re.DOTALL) - # Matches both ```python and ```python:filepath blocks, captures content only MARKDOWN_CODE_BLOCK_PATTERN = re.compile(r"```python(?::[^\n]*)?\n(.*?)```", re.DOTALL) diff --git a/django/aiservice/aiservice/validators/javascript_validator.py b/django/aiservice/aiservice/validators/javascript_validator.py index a49d406f9..05a94e1c4 100644 --- a/django/aiservice/aiservice/validators/javascript_validator.py +++ b/django/aiservice/aiservice/validators/javascript_validator.py @@ -14,6 +14,7 @@ from functools import lru_cache @lru_cache(maxsize=100) def validate_javascript_syntax(code: str) -> tuple[bool, str | None]: # TODO(claude): DON'T do this, use some pytohn lib for this instead of spawning a new subprocess + # Note: code can be a multi-file code (markdown with file paths) return True, None """Validate JavaScript syntax using Node.js. diff --git a/django/aiservice/code_repair/code_repair_context.py b/django/aiservice/code_repair/code_repair_context.py index b9e3a833d..1936f9b8c 100644 --- a/django/aiservice/code_repair/code_repair_context.py +++ b/django/aiservice/code_repair/code_repair_context.py @@ -139,6 +139,7 @@ class CodeRepairContext: def validate_module(self) -> None: """Validate the module syntax based on language.""" # Skip validation for non-Python languages for now + # TODO: have some way to validate the syntax of the code for other languages like js & ts if self.data.language != "python": return for _code in split_markdown_code(self.data.modified_source_code).values(): @@ -147,7 +148,3 @@ class CodeRepairContext: CodeAndExplanation(cst_module, "") except (ValueError, ValidationError, cst.ParserSyntaxError): # noqa: TRY203 raise - - # Keep for backward compatibility - def validate_python_module(self) -> None: - self.validate_module() diff --git a/django/aiservice/languages/__init__.py b/django/aiservice/languages/__init__.py index 3d241cd9b..b5b2a30f1 100644 --- a/django/aiservice/languages/__init__.py +++ b/django/aiservice/languages/__init__.py @@ -1,146 +1,8 @@ -"""Multi-language support registry. +"""Multi-language support module. -This module provides a registry for language implementations and factory -functions to retrieve language-specific functionality. +This package contains language-specific implementations for code optimization +and test generation. -Usage: - from languages import get_language, register_language - - # Get a language implementation - lang = get_language("python") - validator = lang.get_validator() - is_valid, error = validator.validate_syntax(code) - - # Register a new language (typically done in language module __init__) - @register_language("python") - class PythonLanguage: - ... +Subpackages: + js_ts: JavaScript and TypeScript support """ - -from __future__ import annotations - -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from languages.base import LanguageSupport - -# Registry mapping language identifiers to their implementation classes -_LANGUAGE_REGISTRY: dict[str, type[LanguageSupport]] = {} - -# Aliases for language names (e.g., "js" -> "javascript") -_LANGUAGE_ALIASES: dict[str, str] = { - "js": "javascript", - "ts": "typescript", -} - -# Flag to track whether languages have been loaded -_languages_loaded = False - - -class UnsupportedLanguageError(ValueError): - """Raised when an unsupported language is requested.""" - - def __init__(self, language: str) -> None: - supported = ", ".join(sorted(_LANGUAGE_REGISTRY.keys())) - super().__init__(f"Unsupported language: {language}. Supported languages: {supported}") - self.language = language - - -def register_language(lang_id: str): - """Decorator to register a language implementation. - - Args: - lang_id: The language identifier (e.g., "python", "javascript"). - - Returns: - A decorator that registers the class in the language registry. - - Example: - @register_language("python") - class PythonLanguage: - language = "python" - ... - - """ - - def decorator(cls: type[LanguageSupport]) -> type[LanguageSupport]: - _LANGUAGE_REGISTRY[lang_id] = cls - return cls - - return decorator - - -def _load_languages() -> None: - """Load all language implementations. - - This is called lazily to avoid import issues during module initialization. - """ - # Import Python language support - try: - import languages.python # noqa: F401 - except ImportError: - pass - - # Import JavaScript/TypeScript language support - try: - import languages.js_ts # noqa: F401 - except ImportError: - pass - - -def _ensure_languages_loaded() -> None: - """Ensure language implementations are loaded.""" - global _languages_loaded - if not _languages_loaded: - _load_languages() - _languages_loaded = True - - -def get_language(language: str) -> LanguageSupport: - """Get language support implementation for the given language. - - Args: - language: The language identifier (e.g., "python", "javascript", "js"). - - Returns: - An instance of the language support class. - - Raises: - UnsupportedLanguageError: If the language is not registered. - - """ - _ensure_languages_loaded() - - # Normalize language identifier using aliases - normalized = _LANGUAGE_ALIASES.get(language.lower(), language.lower()) - - if normalized not in _LANGUAGE_REGISTRY: - raise UnsupportedLanguageError(language) - - return _LANGUAGE_REGISTRY[normalized]() - - -def get_supported_languages() -> list[str]: - """Get a list of supported language identifiers. - - Returns: - A sorted list of supported language names. - - """ - _ensure_languages_loaded() - return sorted(_LANGUAGE_REGISTRY.keys()) - - -def is_language_supported(language: str) -> bool: - """Check if a language is supported. - - Args: - language: The language identifier to check. - - Returns: - True if the language is supported, False otherwise. - - """ - _ensure_languages_loaded() - normalized = _LANGUAGE_ALIASES.get(language.lower(), language.lower()) - return normalized in _LANGUAGE_REGISTRY diff --git a/django/aiservice/languages/base.py b/django/aiservice/languages/base.py deleted file mode 100644 index 3d4607f85..000000000 --- a/django/aiservice/languages/base.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Base protocols and interfaces for multi-language support. - -This module defines the contracts that language implementations must satisfy. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Protocol, runtime_checkable - -if TYPE_CHECKING: - pass - - -@runtime_checkable -class CodeValidator(Protocol): - """Protocol for code syntax validation.""" - - def validate_syntax(self, code: str) -> tuple[bool, str | None]: - """Validate code syntax. - - Args: - code: The source code to validate. - - Returns: - A tuple of (is_valid, error_message). - - is_valid: True if the code is syntactically valid. - - error_message: None if valid, otherwise a description of the error. - - """ - ... - - -@runtime_checkable -class LanguageSupport(Protocol): - """Protocol for language-specific implementations. - - Each supported language should implement this protocol to provide - language-specific functionality like validation and code formatting. - """ - - @property - def language(self) -> str: - """The language identifier (e.g., 'python', 'javascript').""" - ... - - def get_validator(self) -> CodeValidator: - """Get the code validator for this language.""" - ... - - def is_multi_context(self, code: str) -> bool: - """Check if code is in multi-file markdown format. - - Multi-file format uses ```language:filepath blocks. - - Args: - code: The source code to check. - - Returns: - True if the code is in multi-file markdown format. - - """ - ... - - def get_code_block_tag(self) -> str: - """Get the markdown code block tag for this language. - - Returns: - The language tag used in markdown code blocks (e.g., 'python', 'javascript'). - - """ - ... - - def split_markdown_code(self, markdown: str) -> dict[str, str]: - """Split markdown into filepath -> code dict. - - Parses markdown with ```language:filepath blocks and returns - a dictionary mapping file paths to their code content. - - Args: - markdown: The markdown text containing code blocks. - - Returns: - A dictionary mapping file paths to code content. - - """ - ... - - def group_code(self, file_to_code: dict[str, str]) -> str: - """Combine code files into markdown format. - - Args: - file_to_code: A dictionary mapping file paths to code content. - - Returns: - Markdown-formatted string with code blocks for each file. - - """ - ... diff --git a/django/aiservice/languages/js_ts/__init__.py b/django/aiservice/languages/js_ts/__init__.py index 6dff6d52f..7f328731e 100644 --- a/django/aiservice/languages/js_ts/__init__.py +++ b/django/aiservice/languages/js_ts/__init__.py @@ -1,204 +1,17 @@ -"""JavaScript/TypeScript language support for the aiservice. +"""JavaScript/TypeScript language support. -This module provides JS/TS-specific implementations for code validation, -multi-file context detection, and code formatting. - -Note: The full implementation will be moved here from optimizer_javascript.py -and testgen_javascript.py in Phase 3. +This package contains JS/TS-specific implementations for: +- Code optimization (optimizer.py) +- Line profiler optimization (optimizer_lp.py) +- Test generation (testgen.py) """ -from __future__ import annotations +from languages.js_ts.optimizer import optimize_javascript +from languages.js_ts.optimizer_lp import optimize_javascript_code_line_profiler +from languages.js_ts.testgen import testgen_javascript -from aiservice.common.markdown_utils import split_markdown_code as _split_markdown_code - -from languages import register_language - - -class JavaScriptValidator: - """Validator for JavaScript code syntax. - - Note: Currently uses a permissive fallback validation. - Full validation will be implemented in a future PR. - """ - - def validate_syntax(self, code: str) -> tuple[bool, str | None]: - """Validate JavaScript syntax. - - Args: - code: The JavaScript code to validate. - - Returns: - A tuple of (is_valid, error_message). - - Note: - Currently returns (True, None) as a permissive default. - The full validation logic from javascript_validator.py - will be integrated in Phase 3. - - """ - # TODO: Integrate full validation from aiservice/validators/javascript_validator.py - # For now, use permissive validation to match current behavior - return True, None - - -class TypeScriptValidator: - """Validator for TypeScript code syntax. - - Note: Currently uses a permissive fallback validation. - Full validation will be implemented in a future PR. - """ - - def validate_syntax(self, code: str) -> tuple[bool, str | None]: - """Validate TypeScript syntax. - - Args: - code: The TypeScript code to validate. - - Returns: - A tuple of (is_valid, error_message). - - Note: - Currently returns (True, None) as a permissive default. - The full validation logic will be integrated in Phase 3. - - """ - # TODO: Integrate full validation from aiservice/validators/javascript_validator.py - return True, None - - -@register_language("javascript") -class JavaScriptLanguage: - """JavaScript language support implementation.""" - - @property - def language(self) -> str: - """The language identifier.""" - return "javascript" - - def get_validator(self) -> JavaScriptValidator: - """Get the JavaScript code validator.""" - return JavaScriptValidator() - - def is_multi_context(self, code: str) -> bool: - """Check if code is in multi-file markdown format. - - Multi-file JavaScript code starts with ```javascript: or ```js: - followed by a filepath. - - Args: - code: The source code to check. - - Returns: - True if the code is in multi-file markdown format. - - """ - stripped = code.strip() - return stripped.startswith("```javascript:") or stripped.startswith("```js:") - - def get_code_block_tag(self) -> str: - """Get the markdown code block tag for JavaScript. - - Returns: - The string 'javascript' used in markdown code blocks. - - """ - return "javascript" - - def split_markdown_code(self, markdown: str) -> dict[str, str]: - """Split markdown into filepath -> code dict. - - Parses markdown with ```javascript:filepath or ```js:filepath blocks. - - Args: - markdown: The markdown text containing code blocks. - - Returns: - A dictionary mapping file paths to code content. - - """ - return _split_markdown_code(markdown, language="javascript") - - def group_code(self, file_to_code: dict[str, str]) -> str: - """Combine code files into markdown format. - - Args: - file_to_code: A dictionary mapping file paths to code content. - - Returns: - Markdown-formatted string with ```javascript:filepath blocks. - - """ - blocks = [] - for file_path, code in file_to_code.items(): - normalized = code if code.endswith("\n") else code + "\n" - blocks.append(f"```javascript:{file_path}\n{normalized}```") - return "\n".join(blocks) - - -@register_language("typescript") -class TypeScriptLanguage: - """TypeScript language support implementation.""" - - @property - def language(self) -> str: - """The language identifier.""" - return "typescript" - - def get_validator(self) -> TypeScriptValidator: - """Get the TypeScript code validator.""" - return TypeScriptValidator() - - def is_multi_context(self, code: str) -> bool: - """Check if code is in multi-file markdown format. - - Multi-file TypeScript code starts with ```typescript: or ```ts: - followed by a filepath. - - Args: - code: The source code to check. - - Returns: - True if the code is in multi-file markdown format. - - """ - stripped = code.strip() - return stripped.startswith("```typescript:") or stripped.startswith("```ts:") - - def get_code_block_tag(self) -> str: - """Get the markdown code block tag for TypeScript. - - Returns: - The string 'typescript' used in markdown code blocks. - - """ - return "typescript" - - def split_markdown_code(self, markdown: str) -> dict[str, str]: - """Split markdown into filepath -> code dict. - - Parses markdown with ```typescript:filepath or ```ts:filepath blocks. - - Args: - markdown: The markdown text containing code blocks. - - Returns: - A dictionary mapping file paths to code content. - - """ - return _split_markdown_code(markdown, language="typescript") - - def group_code(self, file_to_code: dict[str, str]) -> str: - """Combine code files into markdown format. - - Args: - file_to_code: A dictionary mapping file paths to code content. - - Returns: - Markdown-formatted string with ```typescript:filepath blocks. - - """ - blocks = [] - for file_path, code in file_to_code.items(): - normalized = code if code.endswith("\n") else code + "\n" - blocks.append(f"```typescript:{file_path}\n{normalized}```") - return "\n".join(blocks) +__all__ = [ + "optimize_javascript", + "optimize_javascript_code_line_profiler", + "testgen_javascript", +] diff --git a/django/aiservice/languages/python/__init__.py b/django/aiservice/languages/python/__init__.py deleted file mode 100644 index b76a449ea..000000000 --- a/django/aiservice/languages/python/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Python language support for the aiservice. - -This module provides Python-specific implementations for code validation, -multi-file context detection, and code formatting. -""" - -from __future__ import annotations - -from aiservice.common.markdown_utils import split_markdown_code as _split_markdown_code -from languages import register_language -from languages.python.validator import PythonValidator - - -@register_language("python") -class PythonLanguage: - """Python language support implementation.""" - - @property - def language(self) -> str: - """The language identifier.""" - return "python" - - def get_validator(self) -> PythonValidator: - """Get the Python code validator.""" - return PythonValidator() - - def is_multi_context(self, code: str) -> bool: - """Check if code is in multi-file markdown format. - - Multi-file Python code starts with ```python: followed by a filepath. - - Args: - code: The source code to check. - - Returns: - True if the code is in multi-file markdown format. - - """ - return code.strip().startswith("```python:") - - def get_code_block_tag(self) -> str: - """Get the markdown code block tag for Python. - - Returns: - The string 'python' used in markdown code blocks. - - """ - return "python" - - def split_markdown_code(self, markdown: str) -> dict[str, str]: - """Split markdown into filepath -> code dict. - - Parses markdown with ```python:filepath blocks and returns - a dictionary mapping file paths to their code content. - - Args: - markdown: The markdown text containing code blocks. - - Returns: - A dictionary mapping file paths to code content. - - """ - return _split_markdown_code(markdown, language="python") - - def group_code(self, file_to_code: dict[str, str]) -> str: - """Combine code files into markdown format. - - Args: - file_to_code: A dictionary mapping file paths to code content. - - Returns: - Markdown-formatted string with ```python:filepath blocks. - - """ - blocks = [] - for file_path, code in file_to_code.items(): - # Ensure code ends with newline before closing ``` - normalized = code if code.endswith("\n") else code + "\n" - blocks.append(f"```python:{file_path}\n{normalized}```") - return "\n".join(blocks) diff --git a/django/aiservice/languages/python/validator.py b/django/aiservice/languages/python/validator.py deleted file mode 100644 index 9837abadc..000000000 --- a/django/aiservice/languages/python/validator.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Python code syntax validation. - -This module provides a validator that uses libcst for Python syntax validation. -""" - -from __future__ import annotations - -import libcst as cst - -from aiservice.common.cst_utils import parse_module_to_cst - - -class PythonValidator: - """Validator for Python code syntax using libcst.""" - - def validate_syntax(self, code: str) -> tuple[bool, str | None]: - """Validate Python syntax using libcst. - - Args: - code: The Python code to validate. - - Returns: - A tuple of (is_valid, error_message). - - is_valid: True if the code is syntactically valid Python. - - error_message: None if valid, otherwise the parse error message. - - """ - try: - parse_module_to_cst(code) - return True, None - except cst.ParserSyntaxError as e: - return False, str(e) - except Exception as e: - # Catch any other parsing errors - return False, f"Parse error: {e}" diff --git a/django/aiservice/optimizer/context_utils/optimizer_context.py b/django/aiservice/optimizer/context_utils/optimizer_context.py index b65b78ca7..e44023f6d 100644 --- a/django/aiservice/optimizer/context_utils/optimizer_context.py +++ b/django/aiservice/optimizer/context_utils/optimizer_context.py @@ -53,7 +53,7 @@ class OptimizeErrorResponseSchema(Schema): ########################################################################################## -# BaseOptimizerContext # +# BaseOptimizerContext [PYTHON ONLY] # ########################################################################################## class BaseOptimizerContext: def __init__(self, base_system_prompt: str, base_user_prompt: str, source_code: str) -> None: diff --git a/django/aiservice/optimizer/context_utils/refiner_context.py b/django/aiservice/optimizer/context_utils/refiner_context.py index d2fb9a76b..cb09b332f 100644 --- a/django/aiservice/optimizer/context_utils/refiner_context.py +++ b/django/aiservice/optimizer/context_utils/refiner_context.py @@ -8,6 +8,7 @@ from pydantic import ValidationError from aiservice.common.cst_utils import parse_module_to_cst from aiservice.common.markdown_utils import wrap_code_in_markdown +from aiservice.validators.javascript_validator import validate_javascript_syntax from optimizer.context_utils.context_helpers import ( group_code, is_markdown_structure_changed, @@ -90,10 +91,9 @@ class BaseRefinerContext: if stripped_code == self.data.optimized_source_code.strip(): return False - # For JavaScript/TypeScript, skip Python-specific syntax validation if self.data.language in ("javascript", "typescript"): - # Basic validation: check it's not empty and has some code-like content - return len(stripped_code) > 10 and any(c in stripped_code for c in "{}();") + valid, _ = validate_javascript_syntax(stripped_code) + return bool(valid) try: parse_module_to_cst(new_refined_code) @@ -153,12 +153,10 @@ class SingleRefinerContext(BaseRefinerContext): def validate_code_syntax(self, code: str) -> None: """Validate code syntax based on language.""" - # For JavaScript/TypeScript, skip Python-specific validation if self.data.language in ("javascript", "typescript"): - # Basic validation: non-empty code - if not code.strip(): - msg = "Empty code" - raise ValueError(msg) + valid, _ = validate_javascript_syntax(code) + if not valid: + raise ValueError("Invalid JavaScript syntax") return # Python validation using libcst @@ -199,10 +197,9 @@ class MultiRefinerContext(BaseRefinerContext): """Validate code syntax based on language.""" # For JavaScript/TypeScript, skip Python-specific validation if self.data.language in ("javascript", "typescript"): - # Basic validation: non-empty code - if not code.strip(): - msg = "Empty code" - raise ValueError(msg) + valid, _ = validate_javascript_syntax(code) + if not valid: + raise ValueError("Invalid JavaScript syntax") return # Python validation using libcst diff --git a/django/aiservice/optimizer/optimizer.py b/django/aiservice/optimizer/optimizer.py index a75e9a296..29cf78681 100644 --- a/django/aiservice/optimizer/optimizer.py +++ b/django/aiservice/optimizer/optimizer.py @@ -287,13 +287,8 @@ async def optimize( request: AuthenticatedRequest, data: OptimizeSchema ) -> tuple[int, OptimizeResponseSchema | OptimizeErrorResponseSchema]: # Route based on language - logging.warning(f"[OPTIMIZE DEBUG] Received request with language='{data.language}' (type: {type(data.language)})") if data.language in ("javascript", "typescript"): - logging.warning("[OPTIMIZE DEBUG] Routing to optimize_javascript") return await optimize_javascript(request, data) - - # Default: Python optimization - logging.warning("[OPTIMIZE DEBUG] Routing to optimize_python") return await optimize_python(request, data) diff --git a/django/aiservice/tests/languages/__init__.py b/django/aiservice/tests/languages/__init__.py deleted file mode 100644 index d74f4d34d..000000000 --- a/django/aiservice/tests/languages/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the languages module.""" diff --git a/django/aiservice/tests/languages/test_registry.py b/django/aiservice/tests/languages/test_registry.py deleted file mode 100644 index 97ece0194..000000000 --- a/django/aiservice/tests/languages/test_registry.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Tests for the language registry and language implementations.""" - -import pytest - -from languages import ( - UnsupportedLanguageError, - get_language, - get_supported_languages, - is_language_supported, -) -from languages.base import CodeValidator, LanguageSupport - - -class TestLanguageRegistry: - """Tests for the language registry.""" - - def test_get_supported_languages_returns_all_registered(self): - """Test that all registered languages are returned.""" - languages = get_supported_languages() - assert "python" in languages - assert "javascript" in languages - assert "typescript" in languages - - def test_is_language_supported_for_registered_languages(self): - """Test is_language_supported for registered languages.""" - assert is_language_supported("python") - assert is_language_supported("javascript") - assert is_language_supported("typescript") - - def test_is_language_supported_for_aliases(self): - """Test is_language_supported for language aliases.""" - assert is_language_supported("js") - assert is_language_supported("ts") - - def test_is_language_supported_false_for_unknown(self): - """Test is_language_supported returns False for unknown languages.""" - assert not is_language_supported("ruby") - assert not is_language_supported("go") - - def test_get_language_returns_instance(self): - """Test get_language returns an instance of the language class.""" - py = get_language("python") - assert py is not None - assert py.language == "python" - - def test_get_language_with_alias(self): - """Test get_language works with aliases.""" - js = get_language("js") - assert js.language == "javascript" - - ts = get_language("ts") - assert ts.language == "typescript" - - def test_get_language_case_insensitive(self): - """Test get_language is case insensitive.""" - py1 = get_language("Python") - py2 = get_language("PYTHON") - py3 = get_language("python") - assert py1.language == py2.language == py3.language == "python" - - def test_get_language_raises_for_unknown(self): - """Test get_language raises UnsupportedLanguageError for unknown languages.""" - with pytest.raises(UnsupportedLanguageError) as exc_info: - get_language("ruby") - assert "ruby" in str(exc_info.value) - assert "Unsupported language" in str(exc_info.value) - - -class TestPythonLanguage: - """Tests for the Python language implementation.""" - - @pytest.fixture - def python_lang(self): - """Get Python language instance.""" - return get_language("python") - - def test_language_property(self, python_lang): - """Test language property returns 'python'.""" - assert python_lang.language == "python" - - def test_get_code_block_tag(self, python_lang): - """Test get_code_block_tag returns 'python'.""" - assert python_lang.get_code_block_tag() == "python" - - def test_get_validator_returns_validator(self, python_lang): - """Test get_validator returns a CodeValidator.""" - validator = python_lang.get_validator() - assert isinstance(validator, CodeValidator) - - def test_validator_valid_python(self, python_lang): - """Test validator accepts valid Python code.""" - validator = python_lang.get_validator() - is_valid, error = validator.validate_syntax("def foo():\n return 42") - assert is_valid is True - assert error is None - - def test_validator_invalid_python(self, python_lang): - """Test validator rejects invalid Python code.""" - validator = python_lang.get_validator() - is_valid, error = validator.validate_syntax("def foo( return 42") - assert is_valid is False - assert error is not None - - def test_is_multi_context_false_for_plain_code(self, python_lang): - """Test is_multi_context returns False for plain Python code.""" - assert not python_lang.is_multi_context("def foo(): pass") - assert not python_lang.is_multi_context("```python\ndef foo(): pass\n```") - - def test_is_multi_context_true_for_markdown_with_path(self, python_lang): - """Test is_multi_context returns True for markdown with filepath.""" - code = "```python:test.py\ndef foo(): pass\n```" - assert python_lang.is_multi_context(code) - - def test_split_markdown_code(self, python_lang): - """Test split_markdown_code parses markdown correctly.""" - markdown = """```python:file1.py -def foo(): - pass -``` - -```python:file2.py -def bar(): - pass -```""" - result = python_lang.split_markdown_code(markdown) - assert "file1.py" in result - assert "file2.py" in result - assert "def foo():" in result["file1.py"] - assert "def bar():" in result["file2.py"] - - def test_group_code(self, python_lang): - """Test group_code formats code correctly.""" - file_to_code = { - "file1.py": "def foo():\n pass", - "file2.py": "def bar():\n pass", - } - result = python_lang.group_code(file_to_code) - assert "```python:file1.py" in result - assert "```python:file2.py" in result - assert "def foo():" in result - assert "def bar():" in result - - -class TestJavaScriptLanguage: - """Tests for the JavaScript language implementation.""" - - @pytest.fixture - def js_lang(self): - """Get JavaScript language instance.""" - return get_language("javascript") - - def test_language_property(self, js_lang): - """Test language property returns 'javascript'.""" - assert js_lang.language == "javascript" - - def test_get_code_block_tag(self, js_lang): - """Test get_code_block_tag returns 'javascript'.""" - assert js_lang.get_code_block_tag() == "javascript" - - def test_get_validator_returns_validator(self, js_lang): - """Test get_validator returns a CodeValidator.""" - validator = js_lang.get_validator() - assert isinstance(validator, CodeValidator) - - def test_is_multi_context_false_for_plain_code(self, js_lang): - """Test is_multi_context returns False for plain JavaScript code.""" - assert not js_lang.is_multi_context("function foo() {}") - assert not js_lang.is_multi_context("```javascript\nfunction foo() {}\n```") - - def test_is_multi_context_true_for_markdown_with_path(self, js_lang): - """Test is_multi_context returns True for markdown with filepath.""" - code = "```javascript:test.js\nfunction foo() {}\n```" - assert js_lang.is_multi_context(code) - - code_short = "```js:test.js\nfunction foo() {}\n```" - assert js_lang.is_multi_context(code_short) - - def test_group_code(self, js_lang): - """Test group_code formats code correctly.""" - file_to_code = { - "file1.js": "function foo() {}", - "file2.js": "function bar() {}", - } - result = js_lang.group_code(file_to_code) - assert "```javascript:file1.js" in result - assert "```javascript:file2.js" in result - - -class TestTypeScriptLanguage: - """Tests for the TypeScript language implementation.""" - - @pytest.fixture - def ts_lang(self): - """Get TypeScript language instance.""" - return get_language("typescript") - - def test_language_property(self, ts_lang): - """Test language property returns 'typescript'.""" - assert ts_lang.language == "typescript" - - def test_get_code_block_tag(self, ts_lang): - """Test get_code_block_tag returns 'typescript'.""" - assert ts_lang.get_code_block_tag() == "typescript" - - def test_is_multi_context_true_for_markdown_with_path(self, ts_lang): - """Test is_multi_context returns True for markdown with filepath.""" - code = "```typescript:test.ts\nfunction foo(): void {}\n```" - assert ts_lang.is_multi_context(code) - - code_short = "```ts:test.ts\nfunction foo(): void {}\n```" - assert ts_lang.is_multi_context(code_short) - - def test_group_code(self, ts_lang): - """Test group_code formats code correctly.""" - file_to_code = {"file1.ts": "function foo(): void {}"} - result = ts_lang.group_code(file_to_code) - assert "```typescript:file1.ts" in result - - -class TestLanguageProtocolCompliance: - """Tests to verify language implementations satisfy the protocols.""" - - @pytest.mark.parametrize("lang_id", ["python", "javascript", "typescript"]) - def test_language_satisfies_protocol(self, lang_id): - """Test that each language satisfies the LanguageSupport protocol.""" - lang = get_language(lang_id) - assert isinstance(lang, LanguageSupport) - - # Verify all protocol methods exist and return expected types - assert isinstance(lang.language, str) - assert isinstance(lang.get_code_block_tag(), str) - assert isinstance(lang.get_validator(), CodeValidator) - assert isinstance(lang.is_multi_context("test"), bool) - assert isinstance(lang.split_markdown_code("test"), dict) - assert isinstance(lang.group_code({"a.py": "code"}), str) - - @pytest.mark.parametrize("lang_id", ["python", "javascript", "typescript"]) - def test_validator_satisfies_protocol(self, lang_id): - """Test that each validator satisfies the CodeValidator protocol.""" - lang = get_language(lang_id) - validator = lang.get_validator() - assert isinstance(validator, CodeValidator) - - # Verify validate_syntax returns expected tuple - result = validator.validate_syntax("test code") - assert isinstance(result, tuple) - assert len(result) == 2 - assert isinstance(result[0], bool) - assert result[1] is None or isinstance(result[1], str)