mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-13 12:15:10 +00:00
Formatting, removed unused vars and imports
This commit is contained in:
1 parent
6107b179bf
commit
5eba8a749b
9 files changed
+166
-177
No files matched your search
@@ -429,7 +429,12 @@ class Symbol(ToStringMixin):
|
||||
return result
|
||||
|
||||
def to_dict(
|
||||
self, kind: bool = False, location: bool = False, depth: int = 0, include_body: bool = False, include_children_body: bool = False,
|
||||
self,
|
||||
kind: bool = False,
|
||||
location: bool = False,
|
||||
depth: int = 0,
|
||||
include_body: bool = False,
|
||||
include_children_body: bool = False,
|
||||
include_relative_path=True,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
|
||||
@@ -1 +1 @@
|
||||
|
||||
|
||||
@@ -35,24 +35,27 @@ class ElixirTools(SolidLanguageServer):
|
||||
|
||||
def _is_next_ls_internal_file(self, abs_path: str) -> bool:
|
||||
"""Check if an absolute path is a Next LS internal file that should be ignored."""
|
||||
return any(pattern in abs_path for pattern in [
|
||||
".burrito", # Next LS runtime directory
|
||||
"next_ls_erts-", # Next LS Erlang runtime
|
||||
"_next_ls_private_", # Next LS private files
|
||||
"/priv/monkey/", # Next LS monkey patching directory
|
||||
])
|
||||
return any(
|
||||
pattern in abs_path
|
||||
for pattern in [
|
||||
".burrito", # Next LS runtime directory
|
||||
"next_ls_erts-", # Next LS Erlang runtime
|
||||
"_next_ls_private_", # Next LS private files
|
||||
"/priv/monkey/", # Next LS monkey patching directory
|
||||
]
|
||||
)
|
||||
|
||||
@override
|
||||
def _send_references_request(self, relative_file_path: str, line: int, column: int):
|
||||
"""Override to filter out Next LS internal files from references."""
|
||||
from solidlsp.ls_utils import PathUtils
|
||||
|
||||
|
||||
# Get the raw response from the parent implementation
|
||||
raw_response = super()._send_references_request(relative_file_path, line, column)
|
||||
|
||||
|
||||
if raw_response is None:
|
||||
return None
|
||||
|
||||
|
||||
# Filter out Next LS internal files
|
||||
filtered_response = []
|
||||
for item in raw_response:
|
||||
@@ -62,7 +65,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
self.logger.log(f"Filtering out Next LS internal file: {abs_path}", logging.DEBUG)
|
||||
continue
|
||||
filtered_response.append(item)
|
||||
|
||||
|
||||
return filtered_response
|
||||
|
||||
@staticmethod
|
||||
@@ -87,7 +90,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
raise RuntimeError(
|
||||
"Elixir is not installed. Please install Elixir from https://elixir-lang.org/install.html and make sure it is added to your PATH."
|
||||
)
|
||||
|
||||
|
||||
logger.log(f"Found Elixir: {elixir_version}", logging.INFO)
|
||||
|
||||
platformId = PlatformUtils.get_platform_id()
|
||||
@@ -101,17 +104,17 @@ class ElixirTools(SolidLanguageServer):
|
||||
# Map platform IDs to runtime dependency keys
|
||||
platform_mapping = {
|
||||
"linux-x64": "linux-x64",
|
||||
"osx-x64": "darwin-x64",
|
||||
"osx-x64": "darwin-x64",
|
||||
"osx-arm64": "darwin-arm64",
|
||||
"darwin-x64": "darwin-x64",
|
||||
"darwin-arm64": "darwin-arm64",
|
||||
"win-x64": "win-x64"
|
||||
"darwin-arm64": "darwin-arm64",
|
||||
"win-x64": "win-x64",
|
||||
}
|
||||
|
||||
platform_key = platform_mapping.get(platformId.value)
|
||||
if not platform_key:
|
||||
raise RuntimeError(f"Unsupported platform for Next LS: {platformId.value}")
|
||||
|
||||
|
||||
# Check for Windows and provide a helpful error message
|
||||
if platformId.value.startswith("win"):
|
||||
raise RuntimeError(
|
||||
@@ -122,18 +125,18 @@ class ElixirTools(SolidLanguageServer):
|
||||
dependency = runtimeDependencies["next_ls"][platform_key]
|
||||
next_ls_dir = str(PurePath(os.path.abspath(os.path.dirname(__file__)), "static", dependency["relative_extraction_path"]))
|
||||
os.makedirs(next_ls_dir, exist_ok=True)
|
||||
|
||||
|
||||
executable_path = str(PurePath(next_ls_dir, dependency["executable_name"]))
|
||||
binary_path = str(PurePath(next_ls_dir, dependency["binary_name"]))
|
||||
|
||||
if not os.path.exists(executable_path):
|
||||
logger.log(f"Downloading Next LS binary from {dependency['url']}", logging.INFO)
|
||||
FileUtils.download_file(logger, dependency["url"], binary_path)
|
||||
|
||||
|
||||
# Make the binary executable on Unix-like systems
|
||||
if not platformId.value.startswith("win"):
|
||||
os.chmod(binary_path, stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
|
||||
|
||||
|
||||
# Create a symlink or copy with the expected name
|
||||
if binary_path != executable_path:
|
||||
if os.path.exists(executable_path):
|
||||
@@ -141,13 +144,14 @@ class ElixirTools(SolidLanguageServer):
|
||||
if platformId.value.startswith("win"):
|
||||
# On Windows, copy the file
|
||||
import shutil
|
||||
|
||||
shutil.copy2(binary_path, executable_path)
|
||||
else:
|
||||
# On Unix-like systems, create a symlink
|
||||
os.symlink(os.path.basename(binary_path), executable_path)
|
||||
|
||||
assert os.path.exists(executable_path), f"Next LS executable not found at {executable_path}"
|
||||
|
||||
|
||||
logger.log(f"Next LS binary ready at: {executable_path}", logging.INFO)
|
||||
return executable_path
|
||||
|
||||
@@ -163,7 +167,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
)
|
||||
self.server_ready = threading.Event()
|
||||
self.request_id = 0
|
||||
|
||||
|
||||
# Set generous timeout for Next LS which can be slow to initialize and respond
|
||||
self.set_request_timeout(180.0) # 60 seconds for all environments
|
||||
|
||||
@@ -201,7 +205,7 @@ class ElixirTools(SolidLanguageServer):
|
||||
"""Handle window/logMessage notifications from Next LS"""
|
||||
message_text = msg.get("message", "")
|
||||
self.logger.log(f"LSP: window/logMessage: {message_text}", logging.INFO)
|
||||
|
||||
|
||||
# Check for the specific Next LS readiness signal
|
||||
# Based on Next LS source: "Runtime for folder #{name} is ready..."
|
||||
if "Runtime for folder" in message_text and "is ready..." in message_text:
|
||||
@@ -214,11 +218,11 @@ class ElixirTools(SolidLanguageServer):
|
||||
def check_server_ready(params):
|
||||
"""
|
||||
Handle $/progress notifications from Next LS.
|
||||
Keep as fallback for error detection, but primary readiness detection
|
||||
Keep as fallback for error detection, but primary readiness detection
|
||||
is now done via window/logMessage handler.
|
||||
"""
|
||||
value = params.get("value", {})
|
||||
|
||||
|
||||
# Check for initialization completion progress (fallback signal)
|
||||
if value.get("kind") == "end":
|
||||
message = value.get("message", "")
|
||||
@@ -255,10 +259,10 @@ class ElixirTools(SolidLanguageServer):
|
||||
|
||||
# Verify server capabilities - be more lenient with Next LS
|
||||
self.logger.log(f"Next LS capabilities: {list(init_response['capabilities'].keys())}", logging.INFO)
|
||||
|
||||
|
||||
# Next LS may not provide all capabilities immediately, so we check for basic ones
|
||||
assert "textDocumentSync" in init_response["capabilities"], f"Missing textDocumentSync in {init_response['capabilities']}"
|
||||
|
||||
|
||||
# Some capabilities might be optional or provided later
|
||||
if "completionProvider" not in init_response["capabilities"]:
|
||||
self.logger.log("Warning: completionProvider not available in initial capabilities", logging.WARNING)
|
||||
@@ -272,10 +276,10 @@ class ElixirTools(SolidLanguageServer):
|
||||
# This is the authoritative signal that Next LS is truly ready for requests
|
||||
ready_timeout = 180.0
|
||||
self.logger.log(f"Waiting up to {ready_timeout} seconds for Next LS runtime readiness...", logging.INFO)
|
||||
|
||||
|
||||
if self.server_ready.wait(timeout=ready_timeout):
|
||||
self.logger.log("Next LS is ready and available for requests", logging.INFO)
|
||||
|
||||
|
||||
# Add a small settling period to ensure background indexing is complete
|
||||
# Next LS often continues compilation/indexing in background after ready signal
|
||||
settling_time = 120.0
|
||||
@@ -285,4 +289,4 @@ class ElixirTools(SolidLanguageServer):
|
||||
else:
|
||||
error_msg = f"Next LS failed to initialize within {ready_timeout} seconds. This may indicate a problem with the Elixir installation, project compilation, or Next LS itself."
|
||||
self.logger.log(error_msg, logging.ERROR)
|
||||
raise RuntimeError(error_msg)
|
||||
raise RuntimeError(error_msg)
|
||||
@@ -1,25 +1,23 @@
|
||||
import platform
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def _test_nextls_available() -> str:
|
||||
"""Test if Next LS is available and return error reason if not."""
|
||||
|
||||
# Check if we're on Windows (Next LS doesn't support Windows)
|
||||
if platform.system() == "Windows":
|
||||
return "Next LS does not support Windows"
|
||||
|
||||
|
||||
# Try to import and check Elixir availability
|
||||
try:
|
||||
from solidlsp.language_servers.elixir_tools.elixir_tools import ElixirTools
|
||||
|
||||
|
||||
# Check if Elixir is installed
|
||||
elixir_version = ElixirTools._get_elixir_version()
|
||||
if not elixir_version:
|
||||
return "Elixir is not installed or not in PATH"
|
||||
|
||||
|
||||
return "" # No error, Next LS should be available
|
||||
|
||||
|
||||
except ImportError as e:
|
||||
return f"Failed to import ElixirTools: {e}"
|
||||
except Exception as e:
|
||||
@@ -27,4 +25,4 @@ def _test_nextls_available() -> str:
|
||||
|
||||
|
||||
NEXTLS_UNAVAILABLE_REASON = _test_nextls_available()
|
||||
NEXTLS_UNAVAILABLE = bool(NEXTLS_UNAVAILABLE_REASON)
|
||||
NEXTLS_UNAVAILABLE = bool(NEXTLS_UNAVAILABLE_REASON)
|
||||
@@ -1,137 +1,142 @@
|
||||
"""
|
||||
Elixir-specific test configuration and fixtures.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import pytest
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def ensure_elixir_test_repo_compiled(repo_path: str) -> None:
|
||||
"""Ensure the Elixir test repository dependencies are installed and project is compiled.
|
||||
|
||||
|
||||
Next LS requires the project to be fully compiled and indexed before providing
|
||||
complete references and symbol resolution. This function:
|
||||
1. Installs dependencies via 'mix deps.get'
|
||||
2. Compiles the project via 'mix compile'
|
||||
|
||||
|
||||
This is essential in CI environments where dependencies aren't pre-installed.
|
||||
|
||||
|
||||
Args:
|
||||
repo_path: Path to the Elixir project root directory
|
||||
|
||||
"""
|
||||
# Check if this looks like an Elixir project
|
||||
mix_file = os.path.join(repo_path, "mix.exs")
|
||||
if not os.path.exists(mix_file):
|
||||
return
|
||||
|
||||
|
||||
# Check if already compiled (optimization for repeated runs)
|
||||
build_path = os.path.join(repo_path, "_build")
|
||||
deps_path = os.path.join(repo_path, "deps")
|
||||
|
||||
|
||||
if os.path.exists(build_path) and os.path.exists(deps_path):
|
||||
print(f"Elixir test repository already compiled in {repo_path}")
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
print(f"Installing dependencies and compiling Elixir test repository for optimal Next LS performance...")
|
||||
|
||||
print("Installing dependencies and compiling Elixir test repository for optimal Next LS performance...")
|
||||
|
||||
# First, install dependencies with increased timeout for CI
|
||||
print("=" * 60)
|
||||
print("Step 1/2: Installing Elixir dependencies...")
|
||||
print("=" * 60)
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
deps_result = subprocess.run(
|
||||
["mix", "deps.get"],
|
||||
cwd=repo_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=180 # 3 minutes for dependency installation (CI can be slow)
|
||||
timeout=180,
|
||||
check=False, # 3 minutes for dependency installation (CI can be slow)
|
||||
)
|
||||
|
||||
|
||||
deps_duration = time.time() - start_time
|
||||
print(f"Dependencies installation completed in {deps_duration:.2f} seconds")
|
||||
|
||||
|
||||
# Always log the output for transparency
|
||||
if deps_result.stdout.strip():
|
||||
print("Dependencies stdout:")
|
||||
print("-" * 40)
|
||||
print(deps_result.stdout)
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
if deps_result.stderr.strip():
|
||||
print("Dependencies stderr:")
|
||||
print("-" * 40)
|
||||
print(deps_result.stderr)
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
if deps_result.returncode != 0:
|
||||
print(f"⚠️ Warning: Dependencies installation failed with exit code {deps_result.returncode}")
|
||||
# Continue anyway - some projects might not have dependencies
|
||||
else:
|
||||
print("✓ Dependencies installed successfully")
|
||||
|
||||
|
||||
# Then compile the project with increased timeout for CI
|
||||
print("=" * 60)
|
||||
print("Step 2/2: Compiling Elixir project...")
|
||||
print("=" * 60)
|
||||
start_time = time.time()
|
||||
|
||||
|
||||
compile_result = subprocess.run(
|
||||
["mix", "compile"],
|
||||
cwd=repo_path,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=300 # 5 minutes for compilation (Credo compilation can be slow in CI)
|
||||
timeout=300,
|
||||
check=False, # 5 minutes for compilation (Credo compilation can be slow in CI)
|
||||
)
|
||||
|
||||
|
||||
compile_duration = time.time() - start_time
|
||||
print(f"Compilation completed in {compile_duration:.2f} seconds")
|
||||
|
||||
|
||||
# Always log the output for transparency
|
||||
if compile_result.stdout.strip():
|
||||
print("Compilation stdout:")
|
||||
print("-" * 40)
|
||||
print(compile_result.stdout)
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
if compile_result.stderr.strip():
|
||||
print("Compilation stderr:")
|
||||
print("-" * 40)
|
||||
print(compile_result.stderr)
|
||||
print("-" * 40)
|
||||
|
||||
|
||||
if compile_result.returncode == 0:
|
||||
print(f"✓ Elixir test repository compiled successfully in {repo_path}")
|
||||
else:
|
||||
print(f"⚠️ Warning: Compilation completed with exit code {compile_result.returncode}")
|
||||
# Still continue - warnings are often non-fatal
|
||||
|
||||
|
||||
print("=" * 60)
|
||||
print(f"Total setup time: {time.time() - (start_time - compile_duration - deps_duration):.2f} seconds")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
except subprocess.TimeoutExpired as e:
|
||||
print("=" * 60)
|
||||
print(f"❌ TIMEOUT: Elixir setup timed out after {e.timeout} seconds")
|
||||
print(f"Command: {' '.join(e.cmd)}")
|
||||
print("This may indicate slow CI environment - Next LS may still work but with reduced functionality")
|
||||
|
||||
|
||||
# Try to get partial output if available
|
||||
if hasattr(e, 'stdout') and e.stdout:
|
||||
if hasattr(e, "stdout") and e.stdout:
|
||||
print("Partial stdout before timeout:")
|
||||
print("-" * 40)
|
||||
print(e.stdout)
|
||||
print("-" * 40)
|
||||
if hasattr(e, 'stderr') and e.stderr:
|
||||
if hasattr(e, "stderr") and e.stderr:
|
||||
print("Partial stderr before timeout:")
|
||||
print("-" * 40)
|
||||
print(e.stderr)
|
||||
print("-" * 40)
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
except FileNotFoundError:
|
||||
print("❌ ERROR: 'mix' command not found - Elixir test repository may not be compiled")
|
||||
print("Please ensure Elixir is installed and available in PATH")
|
||||
@@ -142,14 +147,14 @@ def ensure_elixir_test_repo_compiled(repo_path: str) -> None:
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def setup_elixir_test_environment():
|
||||
"""Automatically prepare Elixir test environment for all Elixir tests.
|
||||
|
||||
|
||||
This fixture runs once per test session and automatically:
|
||||
1. Installs dependencies via 'mix deps.get'
|
||||
2. Compiles the Elixir test repository via 'mix compile'
|
||||
|
||||
It uses autouse=True so it runs automatically without needing to be explicitly
|
||||
|
||||
It uses autouse=True so it runs automatically without needing to be explicitly
|
||||
requested by tests. This ensures Next LS has a fully prepared project to work with.
|
||||
|
||||
|
||||
Uses generous timeouts (3-5 minutes) to accommodate slow CI environments.
|
||||
All output is logged for transparency and debugging.
|
||||
"""
|
||||
@@ -162,8 +167,8 @@ def setup_elixir_test_environment():
|
||||
@pytest.fixture(scope="session")
|
||||
def elixir_test_repo_path(setup_elixir_test_environment):
|
||||
"""Get the path to the prepared Elixir test repository.
|
||||
|
||||
|
||||
This fixture depends on setup_elixir_test_environment to ensure dependencies
|
||||
are installed and compilation has completed before returning the path.
|
||||
"""
|
||||
return setup_elixir_test_environment
|
||||
return setup_elixir_test_environment
|
||||
@@ -6,19 +6,16 @@ like request_references using the test repository.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from solidlsp import SolidLanguageServer
|
||||
from solidlsp.ls_config import Language
|
||||
from solidlsp.ls_utils import SymbolUtils
|
||||
|
||||
from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON
|
||||
|
||||
# These marks will be applied to all tests in this module
|
||||
pytestmark = [
|
||||
pytest.mark.elixir,
|
||||
pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")
|
||||
]
|
||||
pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")]
|
||||
|
||||
|
||||
class TestElixirBasic:
|
||||
@@ -29,7 +26,7 @@ class TestElixirBasic:
|
||||
"""Test finding references to a function definition."""
|
||||
file_path = os.path.join("lib", "models.ex")
|
||||
symbols = language_server.request_document_symbols(file_path)
|
||||
|
||||
|
||||
# Find the User module's 'new' function
|
||||
user_new_symbol = None
|
||||
for symbol in symbols[0]: # Top level symbols
|
||||
@@ -39,22 +36,18 @@ class TestElixirBasic:
|
||||
user_new_symbol = child
|
||||
break
|
||||
break
|
||||
|
||||
|
||||
if not user_new_symbol or "selectionRange" not in user_new_symbol:
|
||||
pytest.skip("User.new function or its selectionRange not found")
|
||||
|
||||
sel_start = user_new_symbol["selectionRange"]["start"]
|
||||
references = language_server.request_references(
|
||||
file_path, sel_start["line"], sel_start["character"]
|
||||
)
|
||||
references = language_server.request_references(file_path, sel_start["line"], sel_start["character"])
|
||||
|
||||
assert references is not None
|
||||
assert len(references) > 0
|
||||
|
||||
|
||||
# Should find at least one reference (the definition itself)
|
||||
found_definition = any(
|
||||
ref["uri"].endswith("models.ex") for ref in references
|
||||
)
|
||||
found_definition = any(ref["uri"].endswith("models.ex") for ref in references)
|
||||
assert found_definition, "Should find the function definition"
|
||||
|
||||
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
||||
@@ -62,7 +55,7 @@ class TestElixirBasic:
|
||||
"""Test finding references to create_user function."""
|
||||
file_path = os.path.join("lib", "services.ex")
|
||||
symbols = language_server.request_document_symbols(file_path)
|
||||
|
||||
|
||||
# Find the UserService module's 'create_user' function
|
||||
create_user_symbol = None
|
||||
for symbol in symbols[0]: # Top level symbols
|
||||
@@ -72,14 +65,12 @@ class TestElixirBasic:
|
||||
create_user_symbol = child
|
||||
break
|
||||
break
|
||||
|
||||
|
||||
if not create_user_symbol or "selectionRange" not in create_user_symbol:
|
||||
pytest.skip("UserService.create_user function or its selectionRange not found")
|
||||
|
||||
sel_start = create_user_symbol["selectionRange"]["start"]
|
||||
references = language_server.request_references(
|
||||
file_path, sel_start["line"], sel_start["character"]
|
||||
)
|
||||
references = language_server.request_references(file_path, sel_start["line"], sel_start["character"])
|
||||
|
||||
assert references is not None
|
||||
assert len(references) > 0
|
||||
@@ -89,7 +80,7 @@ class TestElixirBasic:
|
||||
"""Test finding symbols that reference a specific function."""
|
||||
file_path = os.path.join("lib", "models.ex")
|
||||
symbols = language_server.request_document_symbols(file_path)
|
||||
|
||||
|
||||
# Find the User module's 'new' function
|
||||
user_new_symbol = None
|
||||
for symbol in symbols[0]: # Top level symbols
|
||||
@@ -99,14 +90,12 @@ class TestElixirBasic:
|
||||
user_new_symbol = child
|
||||
break
|
||||
break
|
||||
|
||||
|
||||
if not user_new_symbol or "selectionRange" not in user_new_symbol:
|
||||
pytest.skip("User.new function or its selectionRange not found")
|
||||
|
||||
sel_start = user_new_symbol["selectionRange"]["start"]
|
||||
referencing_symbols = language_server.request_referencing_symbols(
|
||||
file_path, sel_start["line"], sel_start["character"]
|
||||
)
|
||||
referencing_symbols = language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"])
|
||||
|
||||
assert referencing_symbols is not None
|
||||
|
||||
@@ -120,4 +109,4 @@ class TestElixirBasic:
|
||||
# Test multiple symbol requests in succession
|
||||
for _ in range(3):
|
||||
symbols = language_server.request_document_symbols("lib/services.ex")
|
||||
assert symbols is not None
|
||||
assert symbols is not None
|
||||
@@ -10,10 +10,7 @@ from test.conftest import create_ls
|
||||
from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON
|
||||
|
||||
# These marks will be applied to all tests in this module
|
||||
pytestmark = [
|
||||
pytest.mark.elixir,
|
||||
pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")
|
||||
]
|
||||
pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@@ -34,7 +31,7 @@ def test_symbol_tree_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer):
|
||||
root = ls_with_ignored_dirs.request_full_symbol_tree()[0]
|
||||
root_children = root["children"]
|
||||
children_names = {child["name"] for child in root_children}
|
||||
|
||||
|
||||
# Should have lib and test directories, but not scripts or ignored_dir
|
||||
expected_dirs = {"lib", "test"}
|
||||
assert expected_dirs.issubset(children_names), f"Expected {expected_dirs} to be in {children_names}"
|
||||
@@ -47,7 +44,7 @@ def test_find_references_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer):
|
||||
"""Tests that find_references ignores the configured directory."""
|
||||
# Location of User struct, which is referenced in scripts and ignored_dir
|
||||
definition_file = "lib/models.ex"
|
||||
|
||||
|
||||
# Find the User struct definition
|
||||
symbols = ls_with_ignored_dirs.request_document_symbols(definition_file)
|
||||
user_symbol = None
|
||||
@@ -55,7 +52,7 @@ def test_find_references_ignores_dir(ls_with_ignored_dirs: SolidLanguageServer):
|
||||
user_symbol = next((s for s in symbol_group if "User" in s.get("name", "")), None)
|
||||
if user_symbol:
|
||||
break
|
||||
|
||||
|
||||
if not user_symbol or "selectionRange" not in user_symbol:
|
||||
pytest.skip("User symbol not found for reference testing")
|
||||
|
||||
@@ -73,13 +70,13 @@ def test_refs_and_symbols_with_glob_patterns(repo_path: Path) -> None:
|
||||
ignored_paths = ["*cripts", "ignored_*"] # codespell:ignore cripts
|
||||
ls = create_ls(ignored_paths=ignored_paths, repo_path=str(repo_path), language=Language.ELIXIR)
|
||||
ls.start()
|
||||
|
||||
|
||||
try:
|
||||
# Same as in the above tests
|
||||
root = ls.request_full_symbol_tree()[0]
|
||||
root_children = root["children"]
|
||||
children_names = {child["name"] for child in root_children}
|
||||
|
||||
|
||||
# Should have lib and test directories, but not scripts or ignored_dir
|
||||
expected_dirs = {"lib", "test"}
|
||||
assert expected_dirs.issubset(children_names), f"Expected {expected_dirs} to be in {children_names}"
|
||||
@@ -88,7 +85,7 @@ def test_refs_and_symbols_with_glob_patterns(repo_path: Path) -> None:
|
||||
|
||||
# Test that the refs and symbols with glob patterns are ignored
|
||||
definition_file = "lib/models.ex"
|
||||
|
||||
|
||||
# Find the User struct definition
|
||||
symbols = ls.request_document_symbols(definition_file)
|
||||
user_symbol = None
|
||||
@@ -96,11 +93,11 @@ def test_refs_and_symbols_with_glob_patterns(repo_path: Path) -> None:
|
||||
user_symbol = next((s for s in symbol_group if "User" in s.get("name", "")), None)
|
||||
if user_symbol:
|
||||
break
|
||||
|
||||
|
||||
if user_symbol and "selectionRange" in user_symbol:
|
||||
sel_start = user_symbol["selectionRange"]["start"]
|
||||
references = ls.request_references(definition_file, sel_start["line"], sel_start["character"])
|
||||
|
||||
|
||||
# Assert that scripts and ignored_dir do not appear in references
|
||||
assert not any("scripts" in ref["relativePath"] for ref in references), "scripts should be ignored (glob)"
|
||||
assert not any("ignored_dir" in ref["relativePath"] for ref in references), "ignored_dir should be ignored (glob)"
|
||||
@@ -117,7 +114,7 @@ def test_default_ignored_directories(language_server: SolidLanguageServer):
|
||||
assert language_server.is_ignored_dirname(".elixir_ls"), ".elixir_ls should be ignored"
|
||||
assert language_server.is_ignored_dirname("cover"), "cover should be ignored"
|
||||
assert language_server.is_ignored_dirname("node_modules"), "node_modules should be ignored"
|
||||
|
||||
|
||||
# Test that important directories are not ignored
|
||||
assert not language_server.is_ignored_dirname("lib"), "lib should not be ignored"
|
||||
assert not language_server.is_ignored_dirname("test"), "test should not be ignored"
|
||||
@@ -129,17 +126,17 @@ def test_default_ignored_directories(language_server: SolidLanguageServer):
|
||||
def test_symbol_tree_excludes_build_dirs(language_server: SolidLanguageServer):
|
||||
"""Test that symbol tree excludes build and dependency directories."""
|
||||
symbol_tree = language_server.request_full_symbol_tree()
|
||||
|
||||
|
||||
if symbol_tree:
|
||||
root = symbol_tree[0]
|
||||
children_names = {child["name"] for child in root.get("children", [])}
|
||||
|
||||
|
||||
# Build and dependency directories should not appear
|
||||
ignored_dirs = {"_build", "deps", ".elixir_ls", "cover", "node_modules"}
|
||||
found_ignored = ignored_dirs.intersection(children_names)
|
||||
assert len(found_ignored) == 0, f"Found ignored directories in symbol tree: {found_ignored}"
|
||||
|
||||
|
||||
# Important directories should appear
|
||||
important_dirs = {"lib", "test"}
|
||||
found_important = important_dirs.intersection(children_names)
|
||||
assert len(found_important) > 0, f"Expected to find important directories: {important_dirs}, got: {children_names}"
|
||||
assert len(found_important) > 0, f"Expected to find important directories: {important_dirs}, got: {children_names}"
|
||||
@@ -5,20 +5,18 @@ These tests verify that the language server works correctly with a real Elixir p
|
||||
and can perform advanced operations like cross-file symbol resolution.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from solidlsp import SolidLanguageServer
|
||||
from solidlsp.ls_config import Language
|
||||
|
||||
from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON
|
||||
|
||||
# These marks will be applied to all tests in this module
|
||||
pytestmark = [
|
||||
pytest.mark.elixir,
|
||||
pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")
|
||||
]
|
||||
pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")]
|
||||
|
||||
|
||||
class TestElixirIntegration:
|
||||
@@ -33,7 +31,7 @@ class TestElixirIntegration:
|
||||
def test_elixir_repo_structure(self, elixir_test_repo_path):
|
||||
"""Test that the Elixir test repository has the expected structure."""
|
||||
repo_path = Path(elixir_test_repo_path)
|
||||
|
||||
|
||||
# Check that key files exist
|
||||
assert (repo_path / "mix.exs").exists(), "mix.exs should exist"
|
||||
assert (repo_path / "lib" / "test_repo.ex").exists(), "main module should exist"
|
||||
@@ -49,8 +47,7 @@ class TestElixirIntegration:
|
||||
"""Test that symbols can be resolved across different files."""
|
||||
# Test that User struct from models.ex can be found when referenced in services.ex
|
||||
services_file = os.path.join("lib", "services.ex")
|
||||
models_file = os.path.join("lib", "models.ex")
|
||||
|
||||
|
||||
# Find where User is referenced in services.ex
|
||||
content = language_server.retrieve_full_file_content(services_file)
|
||||
lines = content.split("\n")
|
||||
@@ -59,13 +56,13 @@ class TestElixirIntegration:
|
||||
if "alias TestRepo.Models.{User" in line:
|
||||
user_reference_line = i
|
||||
break
|
||||
|
||||
|
||||
if user_reference_line is None:
|
||||
pytest.skip("Could not find User reference in services.ex")
|
||||
|
||||
# Try to find the definition
|
||||
defining_symbol = language_server.request_defining_symbol(services_file, user_reference_line, 30)
|
||||
|
||||
|
||||
if defining_symbol and "location" in defining_symbol:
|
||||
# Should point to models.ex
|
||||
assert "models.ex" in defining_symbol["location"]["uri"]
|
||||
@@ -76,14 +73,14 @@ class TestElixirIntegration:
|
||||
# Search for all function definitions
|
||||
function_pattern = r"def\s+\w+\s*[\(\s]"
|
||||
function_matches = language_server.search_files_for_pattern(function_pattern)
|
||||
|
||||
|
||||
# Should find functions across multiple files
|
||||
if function_matches:
|
||||
files_with_functions = set()
|
||||
for match in function_matches:
|
||||
if match.source_file_path:
|
||||
files_with_functions.add(os.path.basename(match.source_file_path))
|
||||
|
||||
|
||||
# Should find functions in multiple files
|
||||
expected_files = {"models.ex", "services.ex", "examples.ex", "utils.ex", "test_repo.ex"}
|
||||
found_files = expected_files.intersection(files_with_functions)
|
||||
@@ -92,7 +89,7 @@ class TestElixirIntegration:
|
||||
# Search for struct definitions
|
||||
struct_pattern = r"defstruct\s+\["
|
||||
struct_matches = language_server.search_files_for_pattern(struct_pattern)
|
||||
|
||||
|
||||
if struct_matches:
|
||||
# Should find structs primarily in models.ex
|
||||
models_structs = [m for m in struct_matches if m.source_file_path and "models.ex" in m.source_file_path]
|
||||
@@ -103,7 +100,7 @@ class TestElixirIntegration:
|
||||
"""Test that the language server understands Elixir module hierarchy."""
|
||||
models_file = os.path.join("lib", "models.ex")
|
||||
symbols = language_server.request_document_symbols(models_file)
|
||||
|
||||
|
||||
if symbols:
|
||||
# Flatten symbol structure
|
||||
all_symbols = []
|
||||
@@ -112,9 +109,9 @@ class TestElixirIntegration:
|
||||
all_symbols.extend(symbol_group)
|
||||
else:
|
||||
all_symbols.append(symbol_group)
|
||||
|
||||
|
||||
symbol_names = [s.get("name", "") for s in all_symbols]
|
||||
|
||||
|
||||
# Should understand nested module structure
|
||||
expected_modules = ["TestRepo.Models", "User", "Item", "Order"]
|
||||
found_modules = [name for name in expected_modules if any(name in symbol_name for symbol_name in symbol_names)]
|
||||
@@ -124,7 +121,7 @@ class TestElixirIntegration:
|
||||
"""Test that the Elixir language recognizes the correct file extensions."""
|
||||
language = Language.ELIXIR
|
||||
matcher = language.get_source_fn_matcher()
|
||||
|
||||
|
||||
# Test Elixir file extensions
|
||||
assert matcher.is_relevant_filename("lib/test_repo.ex")
|
||||
assert matcher.is_relevant_filename("test/test_repo_test.exs")
|
||||
@@ -132,7 +129,7 @@ class TestElixirIntegration:
|
||||
assert matcher.is_relevant_filename("mix.exs")
|
||||
assert matcher.is_relevant_filename("lib/models.ex")
|
||||
assert matcher.is_relevant_filename("lib/services.ex")
|
||||
|
||||
|
||||
# Test non-Elixir files
|
||||
assert not matcher.is_relevant_filename("README.md")
|
||||
assert not matcher.is_relevant_filename("lib/test_repo.py")
|
||||
@@ -142,12 +139,10 @@ class TestElixirIntegration:
|
||||
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
||||
def test_protocol_and_implementation_understanding(self, language_server: SolidLanguageServer):
|
||||
"""Test that the language server understands Elixir protocols and implementations."""
|
||||
models_file = os.path.join("lib", "models.ex")
|
||||
|
||||
# Search for protocol definitions
|
||||
protocol_pattern = r"defprotocol\s+\w+"
|
||||
protocol_matches = language_server.search_files_for_pattern(protocol_pattern, paths_include_glob="**/models.ex")
|
||||
|
||||
|
||||
if protocol_matches:
|
||||
# Should find the Serializable protocol
|
||||
serializable_matches = [m for m in protocol_matches if "Serializable" in str(m)]
|
||||
@@ -156,7 +151,7 @@ class TestElixirIntegration:
|
||||
# Search for protocol implementations
|
||||
impl_pattern = r"defimpl\s+\w+"
|
||||
impl_matches = language_server.search_files_for_pattern(impl_pattern, paths_include_glob="**/models.ex")
|
||||
|
||||
|
||||
if impl_matches:
|
||||
# Should find multiple implementations
|
||||
assert len(impl_matches) >= 3, f"Should find at least 3 protocol implementations, found {len(impl_matches)}"
|
||||
assert len(impl_matches) >= 3, f"Should find at least 3 protocol implementations, found {len(impl_matches)}"
|
||||
@@ -8,6 +8,7 @@ These tests focus on the following methods:
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from solidlsp import SolidLanguageServer
|
||||
@@ -17,10 +18,7 @@ from solidlsp.ls_types import SymbolKind
|
||||
from . import NEXTLS_UNAVAILABLE, NEXTLS_UNAVAILABLE_REASON
|
||||
|
||||
# These marks will be applied to all tests in this module
|
||||
pytestmark = [
|
||||
pytest.mark.elixir,
|
||||
pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")
|
||||
]
|
||||
pytestmark = [pytest.mark.elixir, pytest.mark.skipif(NEXTLS_UNAVAILABLE, reason=f"Next LS not available: {NEXTLS_UNAVAILABLE_REASON}")]
|
||||
|
||||
|
||||
class TestElixirLanguageServerSymbols:
|
||||
@@ -31,7 +29,7 @@ class TestElixirLanguageServerSymbols:
|
||||
"""Test request_containing_symbol for a function."""
|
||||
# Test for a position inside the create_user function
|
||||
file_path = os.path.join("lib", "services.ex")
|
||||
|
||||
|
||||
# Find the create_user function in the file
|
||||
content = language_server.retrieve_full_file_content(file_path)
|
||||
lines = content.split("\n")
|
||||
@@ -40,7 +38,7 @@ class TestElixirLanguageServerSymbols:
|
||||
if "def create_user(" in line:
|
||||
create_user_line = i + 2 # Go inside the function body
|
||||
break
|
||||
|
||||
|
||||
if create_user_line is None:
|
||||
pytest.skip("Could not find create_user function")
|
||||
|
||||
@@ -59,7 +57,7 @@ class TestElixirLanguageServerSymbols:
|
||||
"""Test request_containing_symbol for a module."""
|
||||
# Test for a position inside the UserService module but outside any function
|
||||
file_path = os.path.join("lib", "services.ex")
|
||||
|
||||
|
||||
# Find the UserService module definition
|
||||
content = language_server.retrieve_full_file_content(file_path)
|
||||
lines = content.split("\n")
|
||||
@@ -68,7 +66,7 @@ class TestElixirLanguageServerSymbols:
|
||||
if "defmodule UserService do" in line:
|
||||
user_service_line = i + 1 # Go inside the module
|
||||
break
|
||||
|
||||
|
||||
if user_service_line is None:
|
||||
pytest.skip("Could not find UserService module")
|
||||
|
||||
@@ -84,7 +82,7 @@ class TestElixirLanguageServerSymbols:
|
||||
"""Test request_containing_symbol with nested scopes."""
|
||||
# Test for a position inside a function which is inside a module
|
||||
file_path = os.path.join("lib", "services.ex")
|
||||
|
||||
|
||||
# Find a function inside UserService
|
||||
content = language_server.retrieve_full_file_content(file_path)
|
||||
lines = content.split("\n")
|
||||
@@ -93,7 +91,7 @@ class TestElixirLanguageServerSymbols:
|
||||
if "def create_user(" in line:
|
||||
function_body_line = i + 3 # Go deeper into the function body
|
||||
break
|
||||
|
||||
|
||||
if function_body_line is None:
|
||||
pytest.skip("Could not find function body")
|
||||
|
||||
@@ -116,29 +114,27 @@ class TestElixirLanguageServerSymbols:
|
||||
# This is acceptable behavior for module-level positions
|
||||
assert containing_symbol is None or containing_symbol == {} or "TestRepo.Services" in str(containing_symbol)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
||||
def test_request_referencing_symbols_struct(self, language_server: SolidLanguageServer) -> None:
|
||||
"""Test request_referencing_symbols for a struct."""
|
||||
# Test referencing symbols for User struct
|
||||
file_path = os.path.join("lib", "models.ex")
|
||||
|
||||
|
||||
symbols = language_server.request_document_symbols(file_path)
|
||||
user_symbol = None
|
||||
for symbol_group in symbols:
|
||||
user_symbol = next((s for s in symbol_group if "User" in s.get("name", "")), None)
|
||||
if user_symbol:
|
||||
break
|
||||
|
||||
|
||||
if not user_symbol or "selectionRange" not in user_symbol:
|
||||
pytest.skip("User symbol or its selectionRange not found")
|
||||
|
||||
|
||||
sel_start = user_symbol["selectionRange"]["start"]
|
||||
ref_symbols = [
|
||||
ref.symbol for ref in language_server.request_referencing_symbols(file_path, sel_start["line"], sel_start["character"])
|
||||
]
|
||||
|
||||
|
||||
if ref_symbols:
|
||||
services_references = [
|
||||
symbol
|
||||
@@ -175,7 +171,7 @@ class TestElixirLanguageServerSymbols:
|
||||
if "User.new(" in line:
|
||||
user_new_call_line = i
|
||||
break
|
||||
|
||||
|
||||
if user_new_call_line is None:
|
||||
pytest.skip("Could not find User.new call")
|
||||
|
||||
@@ -199,7 +195,7 @@ class TestElixirLanguageServerSymbols:
|
||||
if "alias TestRepo.Models.{User" in line:
|
||||
user_usage_line = i
|
||||
break
|
||||
|
||||
|
||||
if user_usage_line is None:
|
||||
pytest.skip("Could not find User struct usage")
|
||||
|
||||
@@ -210,9 +206,9 @@ class TestElixirLanguageServerSymbols:
|
||||
|
||||
@pytest.mark.xfail(
|
||||
reason="Known intermittent bug in Next LS v0.23.3: Protocol.UndefinedError for :timeout atom. "
|
||||
"Occurs in CI environments but may pass locally. "
|
||||
"See https://github.com/elixir-tools/next-ls/issues/543",
|
||||
strict=False
|
||||
"Occurs in CI environments but may pass locally. "
|
||||
"See https://github.com/elixir-tools/next-ls/issues/543",
|
||||
strict=False,
|
||||
)
|
||||
@pytest.mark.parametrize("language_server", [Language.ELIXIR], indirect=True)
|
||||
def test_request_defining_symbol_none(self, language_server: SolidLanguageServer) -> None:
|
||||
@@ -229,7 +225,7 @@ class TestElixirLanguageServerSymbols:
|
||||
def test_symbol_methods_integration(self, language_server: SolidLanguageServer) -> None:
|
||||
"""Test integration between different symbol methods."""
|
||||
file_path = os.path.join("lib", "models.ex")
|
||||
|
||||
|
||||
# Find User struct definition
|
||||
content = language_server.retrieve_full_file_content(file_path)
|
||||
lines = content.split("\n")
|
||||
@@ -238,20 +234,20 @@ class TestElixirLanguageServerSymbols:
|
||||
if "defmodule User do" in line:
|
||||
user_struct_line = i
|
||||
break
|
||||
|
||||
|
||||
if user_struct_line is None:
|
||||
pytest.skip("Could not find User struct")
|
||||
|
||||
# Test containing symbol
|
||||
containing = language_server.request_containing_symbol(file_path, user_struct_line + 5, 10)
|
||||
|
||||
|
||||
if containing:
|
||||
# Test that we can find references to this symbol
|
||||
if "location" in containing and "range" in containing["location"]:
|
||||
start_pos = containing["location"]["range"]["start"]
|
||||
refs = [ref.symbol for ref in language_server.request_referencing_symbols(
|
||||
file_path, start_pos["line"], start_pos["character"]
|
||||
)]
|
||||
refs = [
|
||||
ref.symbol for ref in language_server.request_referencing_symbols(file_path, start_pos["line"], start_pos["character"])
|
||||
]
|
||||
# We should find some references or none (both are valid outcomes)
|
||||
assert isinstance(refs, list)
|
||||
|
||||
@@ -259,21 +255,21 @@ class TestElixirLanguageServerSymbols:
|
||||
def test_symbol_tree_structure(self, language_server: SolidLanguageServer) -> None:
|
||||
"""Test that symbol tree structure is correctly built."""
|
||||
symbol_tree = language_server.request_full_symbol_tree()
|
||||
|
||||
|
||||
# Should get a tree structure
|
||||
assert len(symbol_tree) > 0
|
||||
|
||||
|
||||
# Should have our test repository structure
|
||||
root = symbol_tree[0]
|
||||
assert "children" in root
|
||||
|
||||
|
||||
# Look for lib directory
|
||||
lib_dir = None
|
||||
for child in root["children"]:
|
||||
if child["name"] == "lib":
|
||||
lib_dir = child
|
||||
break
|
||||
|
||||
|
||||
if lib_dir:
|
||||
# Next LS returns module names instead of file names (e.g., 'services' instead of 'services.ex')
|
||||
file_names = [child["name"] for child in lib_dir.get("children", [])]
|
||||
@@ -285,14 +281,14 @@ class TestElixirLanguageServerSymbols:
|
||||
def test_request_dir_overview(self, language_server: SolidLanguageServer) -> None:
|
||||
"""Test request_dir_overview functionality."""
|
||||
lib_overview = language_server.request_dir_overview("lib")
|
||||
|
||||
|
||||
# Should get an overview of the lib directory
|
||||
assert lib_overview is not None
|
||||
# Next LS returns keys like 'lib/services.ex' instead of just 'lib'
|
||||
overview_keys = list(lib_overview.keys()) if hasattr(lib_overview, 'keys') else []
|
||||
lib_files = [key for key in overview_keys if key.startswith('lib/')]
|
||||
overview_keys = list(lib_overview.keys()) if hasattr(lib_overview, "keys") else []
|
||||
lib_files = [key for key in overview_keys if key.startswith("lib/")]
|
||||
assert len(lib_files) > 0, f"Expected to find lib/ files in overview keys: {overview_keys}"
|
||||
|
||||
|
||||
# Should contain information about our modules
|
||||
overview_text = str(lib_overview).lower()
|
||||
expected_terms = ["models", "services", "user", "item"]
|
||||
@@ -309,10 +305,10 @@ class TestElixirLanguageServerSymbols:
|
||||
# #
|
||||
# file_path = os.path.join("lib", "models.ex")
|
||||
# doc_overview = language_server.request_document_overview(file_path)
|
||||
#
|
||||
#
|
||||
# # Should get an overview of the models.ex file
|
||||
# assert doc_overview is not None
|
||||
#
|
||||
#
|
||||
# # Should contain information about our structs and functions
|
||||
# overview_text = str(doc_overview).lower()
|
||||
# expected_terms = ["user", "item", "order", "struct", "defmodule"]
|
||||
@@ -323,7 +319,7 @@ class TestElixirLanguageServerSymbols:
|
||||
def test_containing_symbol_of_module_attribute(self, language_server: SolidLanguageServer) -> None:
|
||||
"""Test containing symbol for module attributes."""
|
||||
file_path = os.path.join("lib", "models.ex")
|
||||
|
||||
|
||||
# Find a module attribute like @type or @doc
|
||||
content = language_server.retrieve_full_file_content(file_path)
|
||||
lines = content.split("\n")
|
||||
@@ -332,15 +328,15 @@ class TestElixirLanguageServerSymbols:
|
||||
if line.strip().startswith("@type") or line.strip().startswith("@doc"):
|
||||
attribute_line = i
|
||||
break
|
||||
|
||||
|
||||
if attribute_line is None:
|
||||
pytest.skip("Could not find module attribute")
|
||||
|
||||
containing_symbol = language_server.request_containing_symbol(file_path, attribute_line, 5)
|
||||
|
||||
|
||||
if containing_symbol:
|
||||
# Should be contained within a module
|
||||
assert "name" in containing_symbol
|
||||
# The containing symbol should be a module
|
||||
expected_names = ["User", "Item", "Order", "TestRepo.Models"]
|
||||
assert any(name in containing_symbol["name"] for name in expected_names)
|
||||
assert any(name in containing_symbol["name"] for name in expected_names)
|
||||
Reference in new issue
Block a user