use pytest_language_server::{Config, FixtureDatabase};
use std::fs;
use tempfile::TempDir;
fn create_temp_project(pyproject_content: &str) -> TempDir {
let temp_dir = TempDir::new().unwrap();
let pyproject_path = temp_dir.path().join("pyproject.toml");
fs::write(&pyproject_path, pyproject_content).unwrap();
temp_dir
}
fn create_test_file(dir: &std::path::Path, filename: &str, content: &str) {
fs::write(dir.join(filename), content).unwrap();
}
#[test]
fn test_config_load_from_pyproject() {
let temp_dir = create_temp_project(
r#"
[project]
name = "myproject"
[tool.pytest-language-server]
exclude = ["build/**", "dist"]
disabled_diagnostics = ["undeclared-fixture"]
"#,
);
let config = Config::load(temp_dir.path());
assert_eq!(config.exclude.len(), 2);
assert_eq!(config.disabled_diagnostics, vec!["undeclared-fixture"]);
}
#[test]
fn test_config_load_missing_pyproject() {
let temp_dir = TempDir::new().unwrap();
let config = Config::load(temp_dir.path());
assert!(config.exclude.is_empty());
assert!(config.disabled_diagnostics.is_empty());
}
#[test]
fn test_config_load_empty_tool_section() {
let temp_dir = create_temp_project(
r#"
[project]
name = "myproject"
[tool.pytest-language-server]
"#,
);
let config = Config::load(temp_dir.path());
assert!(config.exclude.is_empty());
assert!(config.disabled_diagnostics.is_empty());
}
#[test]
fn test_config_load_no_tool_section() {
let temp_dir = create_temp_project(
r#"
[project]
name = "myproject"
version = "1.0.0"
"#,
);
let config = Config::load(temp_dir.path());
assert!(config.exclude.is_empty());
}
#[test]
fn test_exclude_patterns_in_workspace_scan() {
let temp_dir = TempDir::new().unwrap();
fs::write(
temp_dir.path().join("pyproject.toml"),
r#"
[tool.pytest-language-server]
exclude = ["excluded/**"]
"#,
)
.unwrap();
fs::create_dir_all(temp_dir.path().join("tests")).unwrap();
fs::create_dir_all(temp_dir.path().join("excluded")).unwrap();
create_test_file(
&temp_dir.path().join("tests"),
"test_included.py",
r#"
import pytest
@pytest.fixture
def included_fixture():
return "included"
def test_included(included_fixture):
pass
"#,
);
create_test_file(
&temp_dir.path().join("excluded"),
"test_excluded.py",
r#"
import pytest
@pytest.fixture
def excluded_fixture():
return "excluded"
def test_excluded(excluded_fixture):
pass
"#,
);
let config = Config::load(temp_dir.path());
let db = FixtureDatabase::new();
db.scan_workspace_with_excludes(temp_dir.path(), &config.exclude);
assert!(
db.definitions.contains_key("included_fixture"),
"included_fixture should be found"
);
assert!(
!db.definitions.contains_key("excluded_fixture"),
"excluded_fixture should be excluded"
);
}
#[test]
fn test_exclude_patterns_glob_matching() {
let temp_dir = TempDir::new().unwrap();
fs::write(
temp_dir.path().join("pyproject.toml"),
r#"
[tool.pytest-language-server]
exclude = ["**/generated/**", "legacy_*"]
"#,
)
.unwrap();
fs::create_dir_all(temp_dir.path().join("src/generated")).unwrap();
fs::create_dir_all(temp_dir.path().join("tests")).unwrap();
create_test_file(
&temp_dir.path().join("src/generated"),
"test_generated.py",
r#"
import pytest
@pytest.fixture
def generated_fixture():
pass
"#,
);
create_test_file(
temp_dir.path(),
"legacy_test_old.py",
r#"
import pytest
@pytest.fixture
def legacy_fixture():
pass
"#,
);
create_test_file(
&temp_dir.path().join("tests"),
"test_normal.py",
r#"
import pytest
@pytest.fixture
def normal_fixture():
pass
"#,
);
let config = Config::load(temp_dir.path());
let db = FixtureDatabase::new();
db.scan_workspace_with_excludes(temp_dir.path(), &config.exclude);
assert!(
db.definitions.contains_key("normal_fixture"),
"normal_fixture should be found"
);
assert!(
!db.definitions.contains_key("generated_fixture"),
"generated_fixture should be excluded"
);
assert!(
!db.definitions.contains_key("legacy_fixture"),
"legacy_fixture should be excluded"
);
}
#[test]
fn test_disabled_diagnostics_check() {
let temp_dir = create_temp_project(
r#"
[tool.pytest-language-server]
disabled_diagnostics = ["undeclared-fixture", "scope-mismatch"]
"#,
);
let config = Config::load(temp_dir.path());
assert!(config.is_diagnostic_disabled("undeclared-fixture"));
assert!(config.is_diagnostic_disabled("scope-mismatch"));
assert!(!config.is_diagnostic_disabled("circular-dependency"));
}
#[test]
fn test_all_diagnostics_disabled() {
let temp_dir = create_temp_project(
r#"
[tool.pytest-language-server]
disabled_diagnostics = ["undeclared-fixture", "scope-mismatch", "circular-dependency"]
"#,
);
let config = Config::load(temp_dir.path());
assert!(config.is_diagnostic_disabled("undeclared-fixture"));
assert!(config.is_diagnostic_disabled("scope-mismatch"));
assert!(config.is_diagnostic_disabled("circular-dependency"));
}
#[test]
fn test_skip_plugins_check() {
let temp_dir = create_temp_project(
r#"
[tool.pytest-language-server]
skip_plugins = ["pytest-xdist", "pytest-cov"]
"#,
);
let config = Config::load(temp_dir.path());
assert!(config.should_skip_plugin("pytest-xdist"));
assert!(config.should_skip_plugin("pytest-cov"));
assert!(!config.should_skip_plugin("pytest-mock"));
}
#[test]
fn test_config_with_invalid_toml_syntax() {
let temp_dir = TempDir::new().unwrap();
fs::write(
temp_dir.path().join("pyproject.toml"),
"this is not valid toml [[[",
)
.unwrap();
let config = Config::load(temp_dir.path());
assert!(config.exclude.is_empty());
assert!(config.disabled_diagnostics.is_empty());
}
#[test]
fn test_config_with_wrong_types() {
let temp_dir = create_temp_project(
r#"
[tool.pytest-language-server]
exclude = "should-be-array"
"#,
);
let config = Config::load(temp_dir.path());
assert!(config.exclude.is_empty());
}
#[test]
fn test_config_with_extra_unknown_fields() {
let temp_dir = create_temp_project(
r#"
[tool.pytest-language-server]
exclude = ["build"]
unknown_field = "should be ignored"
another_unknown = 42
"#,
);
let config = Config::load(temp_dir.path());
assert_eq!(config.exclude.len(), 1);
}
#[test]
fn test_config_fixture_paths() {
let temp_dir = create_temp_project(
r#"
[tool.pytest-language-server]
fixture_paths = ["fixtures/", "shared/fixtures/"]
"#,
);
let config = Config::load(temp_dir.path());
assert_eq!(config.fixture_paths, vec!["fixtures/", "shared/fixtures/"]);
}