From f5ce8fcba7e85f7aad0462b04975c37056a18137 Mon Sep 17 00:00:00 2001 From: Gadi Evron Date: Thu, 28 May 2026 12:54:40 -0700 Subject: [PATCH] fix(js-parser): export FILE_BOUNDARY at module level for cross-parser parity The JavaScript unit generator declared FILE_BOUNDARY as a function-local `const` inside _assembleEnhancedCode and exported only `{ UnitGenerator }`, so `require(...).FILE_BOUNDARY` was undefined. Every sibling parser (python:60, php/c/ruby:35) declares this marker at module level and makes it importable; the JS parser was the outlier, trapping the canonical boundary string in a method body where no external consumer could reach it. Move the declaration to module level next to the requires, reference it unchanged inside _assembleEnhancedCode (resolves via lexical scope, output byte-identical), and add it to module.exports. No runtime/behavior change. Tests: new tests/parsers/javascript/test_unit_generator_file_boundary.py (node-subprocess seam) asserts the export exists and equals the canonical marker string. RED 2 failed pre-fix; GREEN 2 passed. Full suite 219 passed, 22 skipped, 0 failed; ruff + node --check clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../parsers/javascript/unit_generator.js | 8 ++- .../test_unit_generator_file_boundary.py | 71 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 libs/openant-core/tests/parsers/javascript/test_unit_generator_file_boundary.py diff --git a/libs/openant-core/parsers/javascript/unit_generator.js b/libs/openant-core/parsers/javascript/unit_generator.js index 7b76219c..c2f8ee51 100644 --- a/libs/openant-core/parsers/javascript/unit_generator.js +++ b/libs/openant-core/parsers/javascript/unit_generator.js @@ -49,6 +49,11 @@ const fs = require('fs'); const path = require('path'); const { DependencyResolver } = require('./dependency_resolver'); +// File boundary marker for enhanced code (module-level for parity with the +// python/php/c/ruby parsers, so external consumers can import the canonical +// marker instead of re-defining it and risking silent drift). +const FILE_BOUNDARY = '\n\n// ========== File Boundary ==========\n\n'; + class UnitGenerator { constructor(repoPath, datasetName = null, options = {}) { this.repoPath = repoPath; @@ -162,7 +167,6 @@ class UnitGenerator { * Matches DVNA enhanced dataset format expected by experiment.py */ _assembleEnhancedCode(funcData, upstreamDependencies, downstreamCallers) { - const FILE_BOUNDARY = '\n\n// ========== File Boundary ==========\n\n'; const parts = []; const includedCode = new Set(); @@ -479,4 +483,4 @@ if (require.main === module) { } } -module.exports = { UnitGenerator }; +module.exports = { UnitGenerator, FILE_BOUNDARY }; diff --git a/libs/openant-core/tests/parsers/javascript/test_unit_generator_file_boundary.py b/libs/openant-core/tests/parsers/javascript/test_unit_generator_file_boundary.py new file mode 100644 index 00000000..239df081 --- /dev/null +++ b/libs/openant-core/tests/parsers/javascript/test_unit_generator_file_boundary.py @@ -0,0 +1,71 @@ +"""Tests for FILE_BOUNDARY module-level export parity in the JS unit generator. + +The JavaScript parser declared FILE_BOUNDARY as a +function-local `const` inside `_assembleEnhancedCode` and never exported it, +unlike the python/php/c/ruby parsers which expose it as a module-level constant +(python/unit_generator.py:60, php/c/ruby :35). That made the canonical boundary +marker un-importable and risked silent drift across language parsers. + +These exercise the JS module's exports by running Node.js as a subprocess +(mirroring tests/parsers/javascript/test_express_route_handlers.py). They skip +when Node.js or the parser's npm dependencies aren't installed. +""" +import json +import shutil +import subprocess +from pathlib import Path + +import pytest + + +PARSERS_JS_DIR = Path(__file__).parent.parent.parent.parent / "parsers" / "javascript" +NODE_MODULES = PARSERS_JS_DIR / "node_modules" +UNIT_GENERATOR_JS = PARSERS_JS_DIR / "unit_generator.js" + +# Canonical boundary marker shared with the python/php/c/ruby parsers. +# (Comment syntax differs by language: JS/PHP/C/Zig use `//`, python/ruby use `#`.) +EXPECTED_FILE_BOUNDARY = "\n\n// ========== File Boundary ==========\n\n" + +pytestmark = pytest.mark.skipif( + not shutil.which("node") or not NODE_MODULES.exists(), + reason="Node.js or JS parser npm dependencies not available", +) + + +def _require_exports() -> dict: + """require() the module in Node and return its exports as JSON.""" + script = ( + f"const m = require({json.dumps(str(UNIT_GENERATOR_JS))});" + "process.stdout.write(JSON.stringify({" + "keys: Object.keys(m)," + "fileBoundaryType: typeof m.FILE_BOUNDARY," + "fileBoundary: m.FILE_BOUNDARY === undefined ? null : m.FILE_BOUNDARY," + "}));" + ) + result = subprocess.run( + ["node", "-e", script], capture_output=True, text=True, timeout=30 + ) + assert result.returncode == 0, ( + f"node require() failed:\nstdout={result.stdout}\nstderr={result.stderr}" + ) + return json.loads(result.stdout) + + +def test_FILE_BOUNDARY_exported(): + """FILE_BOUNDARY must be a module-level export (importable by other modules).""" + exports = _require_exports() + assert "FILE_BOUNDARY" in exports["keys"], ( + "FILE_BOUNDARY is not exported from unit_generator.js; " + f"module.exports keys = {exports['keys']}" + ) + + +def test_FILE_BOUNDARY_is_string(): + """The exported FILE_BOUNDARY must be the canonical boundary marker string.""" + exports = _require_exports() + assert exports["fileBoundaryType"] == "string", ( + f"FILE_BOUNDARY should be a string, got type {exports['fileBoundaryType']!r}" + ) + assert exports["fileBoundary"] == EXPECTED_FILE_BOUNDARY, ( + f"FILE_BOUNDARY value mismatch: {exports['fileBoundary']!r}" + )