#![expect(
clippy::cast_possible_truncation,
reason = "#673: fixed test-fixture lengths cast to u32 never approach truncation range; this \
whole module is #[cfg(test)]-gated fixture support"
)]
use super::test_support::{MockDep, MockParseResult, pkg};
use super::*;
use crate::{ConcreteVersion, PackageName, PublishTime, RemovalStatus, VersionReq};
use std::any::Any;
use tower_lsp_server::ls_types::{CodeAction, CodeActionKind};
pub(crate) const MOCK_GO_FORMATTER: crate::test_util::StubFormatter =
crate::test_util::StubFormatter::new()
.with_package_url_prefix("https://pkg.go.dev/")
.with_manifest_requirement_as_resolved_version();
pub(crate) const MOCK_WIDENED_RESOLVE_FORMATTER: crate::test_util::StubFormatter =
crate::test_util::StubFormatter::new().with_alternate_registry_resolution();
pub(crate) struct MockRegistry;
impl crate::Registry for MockRegistry {
fn get_versions<'a>(
&'a self,
_name: &'a PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a PackageName,
_req: &'a VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct ErrorRegistry;
impl crate::Registry for ErrorRegistry {
fn get_versions<'a>(
&'a self,
_name: &'a PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
Box::pin(async move {
Err(crate::error::DepsError::CacheError(
"mock registry error".to_string(),
))
})
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a PackageName,
_req: &'a VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct SlowRegistry {
pub(crate) delay: std::time::Duration,
}
impl crate::Registry for SlowRegistry {
fn get_versions<'a>(
&'a self,
_name: &'a PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
let delay = self.delay;
Box::pin(async move {
tokio::time::sleep(delay).await;
Ok(vec![Box::new(TestVersion {
version: ConcreteVersion::new("9.9.9"),
yanked: false,
}) as Box<dyn crate::Version>])
})
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a PackageName,
_req: &'a VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn select_latest_matching(
&self,
versions: &[Box<dyn crate::Version>],
_req: &crate::VersionReq,
_selection_context: &crate::SelectionContext,
) -> Option<usize> {
versions.iter().position(|v| v.is_stable())
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct NotFoundRegistry;
impl crate::Registry for NotFoundRegistry {
fn get_versions<'a>(
&'a self,
name: &'a PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
Box::pin(async move {
Err(crate::error::DepsError::PackageNotFound {
package: name.as_str().into(),
registry: "mock",
})
})
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a PackageName,
_req: &'a VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct MockVersionWithAge {
pub(crate) version: ConcreteVersion,
pub(crate) yanked: bool,
pub(crate) published_at: Option<PublishTime>,
}
impl crate::Version for MockVersionWithAge {
fn version_string(&self) -> &ConcreteVersion {
&self.version
}
fn removal_status(&self) -> crate::RemovalStatus {
crate::RemovalStatus::from_yanked(self.yanked)
}
fn published_at(&self) -> Option<PublishTime> {
self.published_at
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct TestVersion {
pub(crate) version: ConcreteVersion,
pub(crate) yanked: bool,
}
impl crate::Version for TestVersion {
fn version_string(&self) -> &ConcreteVersion {
&self.version
}
fn removal_status(&self) -> crate::RemovalStatus {
crate::RemovalStatus::from_yanked(self.yanked)
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct MockRegistryWithVersions {
pub(crate) versions: Vec<MockVersionWithAge>,
}
impl crate::Registry for MockRegistryWithVersions {
fn get_versions<'a>(
&'a self,
_name: &'a crate::PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
let versions = self
.versions
.iter()
.map(|v| {
Box::new(MockVersionWithAge {
version: v.version.clone(),
yanked: v.yanked,
published_at: v.published_at,
}) as Box<dyn crate::Version>
})
.collect();
Box::pin(async move { Ok(versions) })
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a crate::PackageName,
_req: &'a crate::VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn select_latest_matching(
&self,
versions: &[Box<dyn crate::Version>],
_req: &crate::VersionReq,
_selection_context: &crate::SelectionContext,
) -> Option<usize> {
versions.iter().position(|v| v.is_stable())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct MockVersionWithLicense {
pub(crate) version: ConcreteVersion,
pub(crate) license: Vec<String>,
}
impl crate::Version for MockVersionWithLicense {
fn version_string(&self) -> &ConcreteVersion {
&self.version
}
fn license(&self) -> &[String] {
&self.license
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct MockRegistryWithLicensedVersions {
pub(crate) versions: Vec<MockVersionWithLicense>,
}
impl crate::Registry for MockRegistryWithLicensedVersions {
fn get_versions<'a>(
&'a self,
_name: &'a crate::PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
let versions = self
.versions
.iter()
.map(|v| {
Box::new(MockVersionWithLicense {
version: v.version.clone(),
license: v.license.clone(),
}) as Box<dyn crate::Version>
})
.collect();
Box::pin(async move { Ok(versions) })
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a crate::PackageName,
_req: &'a crate::VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn select_latest_matching(
&self,
versions: &[Box<dyn crate::Version>],
_req: &crate::VersionReq,
_selection_context: &crate::SelectionContext,
) -> Option<usize> {
versions.iter().position(|v| v.is_stable())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct MockVersionWithStatus {
pub(crate) version: ConcreteVersion,
pub(crate) status: RemovalStatus,
}
impl crate::Version for MockVersionWithStatus {
fn version_string(&self) -> &ConcreteVersion {
&self.version
}
fn removal_status(&self) -> RemovalStatus {
self.status
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct MockRegistryPreferringUnflagged {
pub(crate) versions: Vec<MockVersionWithStatus>,
}
impl crate::Registry for MockRegistryPreferringUnflagged {
fn get_versions<'a>(
&'a self,
_name: &'a crate::PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
let versions = self
.versions
.iter()
.map(|v| {
Box::new(MockVersionWithStatus {
version: v.version.clone(),
status: v.status,
}) as Box<dyn crate::Version>
})
.collect();
Box::pin(async move { Ok(versions) })
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a crate::PackageName,
_req: &'a crate::VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn select_latest_matching(
&self,
versions: &[Box<dyn crate::Version>],
_req: &crate::VersionReq,
_selection_context: &crate::SelectionContext,
) -> Option<usize> {
crate::select_latest_for_existence(versions, |v| v.as_ref())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct MockRegistryListFailsLatestFallbackSucceeds {
pub(crate) versions: Vec<MockVersionWithAge>,
pub(crate) fallback_latest: MockVersionWithAge,
pub(crate) list_pick_index: Option<usize>,
pub(crate) get_latest_matching_calls: std::sync::atomic::AtomicUsize,
}
impl crate::Registry for MockRegistryListFailsLatestFallbackSucceeds {
fn get_versions<'a>(
&'a self,
_name: &'a crate::PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
let versions = self
.versions
.iter()
.map(|v| {
Box::new(MockVersionWithAge {
version: v.version.clone(),
yanked: v.yanked,
published_at: v.published_at,
}) as Box<dyn crate::Version>
})
.collect();
Box::pin(async move { Ok(versions) })
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a crate::PackageName,
_req: &'a crate::VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
self.get_latest_matching_calls
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let fallback = MockVersionWithAge {
version: self.fallback_latest.version.clone(),
yanked: self.fallback_latest.yanked,
published_at: self.fallback_latest.published_at,
};
Box::pin(async move { Ok(Some(Box::new(fallback) as Box<dyn crate::Version>)) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn select_latest_matching(
&self,
_versions: &[Box<dyn crate::Version>],
_req: &crate::VersionReq,
_selection_context: &crate::SelectionContext,
) -> Option<usize> {
self.list_pick_index
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) struct FixedVersionRegistry {
pub(crate) versions: Vec<(&'static str, bool)>,
}
impl crate::Registry for FixedVersionRegistry {
fn get_versions<'a>(
&'a self,
_name: &'a PackageName,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Version>>>> {
let versions: Vec<Box<dyn crate::Version>> = self
.versions
.iter()
.map(|(version, yanked)| {
Box::new(TestVersion {
version: (*version).into(),
yanked: *yanked,
}) as Box<dyn crate::Version>
})
.collect();
Box::pin(async move { Ok(versions) })
}
fn get_latest_matching<'a>(
&'a self,
_name: &'a PackageName,
_req: &'a VersionReq,
_selection_context: &'a crate::SelectionContext,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Option<Box<dyn crate::Version>>>>
{
Box::pin(async move { Ok(None) })
}
fn search_raw<'a>(
&'a self,
_query: &'a str,
_limit: usize,
) -> crate::ecosystem::BoxFuture<'a, crate::error::Result<Vec<Box<dyn crate::Metadata>>>> {
Box::pin(async move { Ok(Vec::new()) })
}
fn select_latest_matching(
&self,
versions: &[Box<dyn crate::Version>],
_req: &VersionReq,
_selection_context: &crate::SelectionContext,
) -> Option<usize> {
crate::select_latest_for_existence(versions, |v| v.as_ref())
}
fn as_any(&self) -> &dyn Any {
self
}
}
pub(crate) fn freshness_test_parse_result(name: &str) -> MockParseResult {
MockParseResult {
deps: vec![MockDep {
name: name.into(),
version_req: "1.0.0".into(),
version_range: Range::new(Position::new(0, 10), Position::new(0, 20)),
name_range: Range::new(Position::new(0, 0), Position::new(0, name.len() as u32)),
}],
uri: crate::test_util::test_uri("/test/Cargo.toml"),
}
}
pub(crate) const IDENTITY_FORMATTER: crate::test_util::StubFormatter =
crate::test_util::StubFormatter::DEFAULT;
pub(crate) struct CaretWrappingFormatter;
impl PackageNaming for CaretWrappingFormatter {}
impl PackageRendering for CaretWrappingFormatter {
fn format_version_for_text_edit(&self, version: &ConcreteVersion) -> String {
format!("^{version}")
}
fn package_url(&self, name: &PackageName) -> String {
format!("https://example.com/{}", name.as_str())
}
}
impl RequirementResolution for CaretWrappingFormatter {}
impl DiagnosticMessages for CaretWrappingFormatter {}
impl DiagnosticPolicy for CaretWrappingFormatter {}
impl SourcePolicy for CaretWrappingFormatter {}
impl OsvNaming for CaretWrappingFormatter {}
pub(crate) struct PinPreservingFormatter;
impl PackageNaming for PinPreservingFormatter {}
impl PackageRendering for PinPreservingFormatter {
fn format_version_for_text_edit(&self, version: &ConcreteVersion) -> String {
format!(">={version}")
}
fn format_version_replacing(&self, version: &ConcreteVersion, current: &str) -> String {
if current.starts_with("==") {
format!("=={version}")
} else {
self.format_version_for_text_edit(version)
}
}
fn package_url(&self, name: &PackageName) -> String {
format!("https://example.com/{}", name.as_str())
}
}
impl RequirementResolution for PinPreservingFormatter {}
impl DiagnosticMessages for PinPreservingFormatter {}
impl DiagnosticPolicy for PinPreservingFormatter {}
impl SourcePolicy for PinPreservingFormatter {}
impl OsvNaming for PinPreservingFormatter {}
pub(crate) fn vulnerable_dep(
version_req: &str,
) -> (MockDep, tower_lsp_server::ls_types::Range, String) {
use tower_lsp_server::ls_types::{Position, Range};
let content = format!("pkg = \"{version_req}\"");
let start = 7u32; let end = start + version_req.chars().count() as u32;
let version_range = Range::new(Position::new(0, start), Position::new(0, end));
(
MockDep {
name: pkg("pkg"),
version_req: VersionReq::new(version_req),
version_range: version_range.into(),
name_range: Range::new(Position::new(0, 0), Position::new(0, 3)).into(),
},
version_range,
content,
)
}
pub(crate) fn quickfix_titles(actions: &[CodeAction]) -> Vec<&str> {
actions
.iter()
.filter(|a| a.kind == Some(CodeActionKind::QUICKFIX))
.map(|a| a.title.as_str())
.collect()
}
pub(crate) fn refactor_titles(actions: &[CodeAction]) -> Vec<&str> {
actions
.iter()
.filter(|a| a.kind == Some(CodeActionKind::REFACTOR))
.map(|a| a.title.as_str())
.collect()
}
pub(crate) struct TrailingSpaceFormatter;
impl PackageNaming for TrailingSpaceFormatter {}
impl PackageRendering for TrailingSpaceFormatter {
fn format_version_for_text_edit(&self, version: &ConcreteVersion) -> String {
format!("{version} ")
}
fn package_url(&self, name: &PackageName) -> String {
format!("https://example.com/{}", name.as_str())
}
}
impl RequirementResolution for TrailingSpaceFormatter {}
impl DiagnosticMessages for TrailingSpaceFormatter {}
impl DiagnosticPolicy for TrailingSpaceFormatter {}
impl SourcePolicy for TrailingSpaceFormatter {}
impl OsvNaming for TrailingSpaceFormatter {}
pub(crate) struct TruncatingFormatter;
impl PackageNaming for TruncatingFormatter {}
impl PackageRendering for TruncatingFormatter {
fn format_version_for_text_edit(&self, version: &ConcreteVersion) -> String {
version.to_string()
}
fn format_version_replacing(&self, version: &ConcreteVersion, _current: &str) -> String {
let mut parts = version.as_str().split('.');
let major = parts.next().unwrap_or("0");
let minor = parts.next().unwrap_or("0");
format!("=={major}.{minor}")
}
fn package_url(&self, name: &PackageName) -> String {
format!("https://example.com/{}", name.as_str())
}
}
impl RequirementResolution for TruncatingFormatter {}
impl DiagnosticMessages for TruncatingFormatter {}
impl DiagnosticPolicy for TruncatingFormatter {}
impl SourcePolicy for TruncatingFormatter {}
impl OsvNaming for TruncatingFormatter {}