mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-14 21:20:48 +08:00
test: remove redundant backend coverage
This commit is contained in:
@@ -704,14 +704,14 @@ const fn transport_name(transport: ListenerTransport) -> &'static str {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
BindClaim, ListenerBindScope, ListenerProbe, ListenerProbeOutcome, ListenerTransport, nothing_is_serving,
|
||||
probe_listener, probe_proxy_port_change,
|
||||
ListenerBindScope, ListenerProbe, ListenerProbeOutcome, ListenerTransport, probe_listener,
|
||||
probe_proxy_port_change,
|
||||
};
|
||||
use serde_json::json;
|
||||
use serde_yaml_ng::Mapping;
|
||||
#[cfg(unix)]
|
||||
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
|
||||
use std::net::{IpAddr, Ipv4Addr, TcpListener, UdpSocket};
|
||||
use std::net::{Ipv4Addr, TcpListener, UdpSocket};
|
||||
#[cfg(unix)]
|
||||
use std::{
|
||||
io::Read as _,
|
||||
@@ -793,53 +793,6 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Guards the startup false positive: another user's `TIME_WAIT` makes the kernel refuse the
|
||||
/// bind while nothing is serving. That refusal needs a second uid to stage, so this asserts
|
||||
/// on the classifier directly.
|
||||
#[test]
|
||||
fn a_refused_bind_with_nothing_answering_is_not_a_conflict() -> anyhow::Result<()> {
|
||||
let vacant = TcpListener::bind((Ipv4Addr::LOCALHOST, 0))?;
|
||||
let port = vacant.local_addr()?.port();
|
||||
drop(vacant);
|
||||
|
||||
assert!(
|
||||
nothing_is_serving(&BindClaim::new(
|
||||
"mixed",
|
||||
IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||
port,
|
||||
ListenerTransport::Tcp
|
||||
)),
|
||||
"a port nobody listens on must be dismissable"
|
||||
);
|
||||
|
||||
let serving = TcpListener::bind((Ipv4Addr::LOCALHOST, port))?;
|
||||
assert!(
|
||||
!nothing_is_serving(&BindClaim::new(
|
||||
"mixed",
|
||||
IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||
port,
|
||||
ListenerTransport::Tcp
|
||||
)),
|
||||
"a live listener must never be dismissed"
|
||||
);
|
||||
drop(serving);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// UDP has no lingering state, so a refused UDP bind is always a live socket.
|
||||
#[test]
|
||||
fn a_refused_udp_bind_is_never_dismissed() -> anyhow::Result<()> {
|
||||
let socket = UdpSocket::bind((Ipv4Addr::LOCALHOST, 0))?;
|
||||
let port = socket.local_addr()?.port();
|
||||
assert!(!nothing_is_serving(&BindClaim::new(
|
||||
"mixed",
|
||||
IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||
port,
|
||||
ListenerTransport::Udp
|
||||
)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn probe_reuses_port_held_only_by_a_stale_tcp_connection() -> anyhow::Result<()> {
|
||||
|
||||
@@ -395,19 +395,6 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_staged_runtime_is_reloaded_from_where_the_service_put_it() {
|
||||
let attempt = StageAttempt::Answered(StageRuntimeOutcome::Staged {
|
||||
config_path: "/service/runtime.generation-1/config.yaml".to_owned(),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
plan_config_application(&attempt),
|
||||
ConfigApplication::ReloadFrom("/service/runtime.generation-1/config.yaml".into()),
|
||||
"the core must be pointed at the service's copy, never at the app's own"
|
||||
);
|
||||
}
|
||||
|
||||
/// Exhaustively maps every service rejection into the policy under test.
|
||||
fn declined(reason: StageRejection) -> StageAttempt {
|
||||
match &reason {
|
||||
@@ -488,15 +475,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_request_that_never_came_back_makes_the_core_be_replaced() {
|
||||
// Silence gives no trustworthy path to reload.
|
||||
assert_eq!(
|
||||
plan_config_application(&StageAttempt::Unanswered("connection reset".into())),
|
||||
ConfigApplication::ReplaceCore
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn an_answer_is_taken_at_its_word_and_not_asked_for_twice() {
|
||||
// Do not pay for a confirmation after a conclusive answer.
|
||||
|
||||
@@ -858,24 +858,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn controlled_sidecar_stop_clears_proxy_before_stopping() {
|
||||
let calls = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let result = run_controlled_stop_transition(
|
||||
true,
|
||||
RunningMode::Sidecar,
|
||||
ProxyStopIntent::Clear,
|
||||
|| future::ready(true),
|
||||
|| transition_step(&calls, "proxy_clear", None),
|
||||
|| transition_step(&calls, "core_stop", None),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(&*calls.lock(), &["proxy_clear", "core_stop"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_a_macos_sidecar_on_its_way_to_the_service_leaves_the_proxy_alone() {
|
||||
use super::proxy_stop_intent;
|
||||
@@ -944,24 +926,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_sidecar_handed_over_still_stops_even_when_a_user_space_clear_would_fail() {
|
||||
let calls = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let result = run_controlled_stop_transition(
|
||||
true,
|
||||
RunningMode::Sidecar,
|
||||
ProxyStopIntent::HandOverToService,
|
||||
|| future::ready(true),
|
||||
|| transition_step(&calls, "proxy_clear", Some("proxy_clear")),
|
||||
|| transition_step(&calls, "core_stop", None),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(&*calls.lock(), &["core_stop"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn an_ordinary_sidecar_stop_still_clears_even_off_macos() {
|
||||
for is_macos in [true, false] {
|
||||
@@ -1051,46 +1015,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn controlled_sidecar_stop_aborts_when_proxy_clear_fails() {
|
||||
let calls = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let result = run_controlled_stop_transition(
|
||||
true,
|
||||
RunningMode::Sidecar,
|
||||
ProxyStopIntent::Clear,
|
||||
|| future::ready(true),
|
||||
|| transition_step(&calls, "proxy_clear", Some("proxy_clear")),
|
||||
|| transition_step(&calls, "core_stop", None),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(&*calls.lock(), &["proxy_clear"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn failed_sidecar_transition_rollback_surfaces_clear_failure_without_terminating() {
|
||||
let calls = Arc::new(Mutex::new(Vec::new()));
|
||||
let sidecar_alive = AtomicBool::new(true);
|
||||
|
||||
let result = run_sidecar_termination_transition(
|
||||
|| transition_step(&calls, "rollback-proxy-clear", Some("rollback-proxy-clear")),
|
||||
|| {
|
||||
sidecar_alive.store(false, Ordering::Release);
|
||||
calls.lock().push("rollback-sidecar-stop");
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(error) if error.to_string().contains("rollback-proxy-clear failed")
|
||||
));
|
||||
assert!(sidecar_alive.load(Ordering::Acquire));
|
||||
assert_eq!(&*calls.lock(), &["rollback-proxy-clear"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handoff_sidecar_cleanup_is_clear_gated_and_ordered() {
|
||||
for fail_at in [None, Some("handoff-proxy-clear")] {
|
||||
@@ -1236,21 +1160,6 @@ mod tests {
|
||||
assert!(!restored_new_core.load(Ordering::Acquire));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn service_config_start_failure_uses_the_same_fail_closed_replacement_order() {
|
||||
let calls = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let result = run_core_replacement_transition(
|
||||
|| transition_step(&calls, "controlled-stop", None),
|
||||
|| transition_step(&calls, "start-service-config", Some("start-service-config")),
|
||||
|| transition_step(&calls, "restore-proxy", None),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(&*calls.lock(), &["controlled-stop", "start-service-config"]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn service_config_replacement_is_controlled_and_generation_safe_on_every_platform() {
|
||||
for is_macos in [false, true] {
|
||||
@@ -1404,25 +1313,6 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_sidecar_that_failed_to_start_can_be_tried_again() {
|
||||
// Revoking a failed attempt's allowance keeps retries reachable without changing Service health.
|
||||
for health in [
|
||||
ServiceStatus::NotInstalled,
|
||||
ServiceStatus::NeedsReinstall,
|
||||
ServiceStatus::Unavailable("boom".into()),
|
||||
] {
|
||||
assert!(
|
||||
can_allow_sidecar_for_session(&RunningMode::NotRunning, &health),
|
||||
"{health:?} must still admit a second attempt"
|
||||
);
|
||||
}
|
||||
assert!(!can_allow_sidecar_for_session(
|
||||
&RunningMode::NotRunning,
|
||||
&ServiceStatus::SidecarAllowed,
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn proxy_restore_requires_the_same_ready_core_generation() {
|
||||
let sidecar =
|
||||
|
||||
@@ -136,11 +136,6 @@ impl FailureTable {
|
||||
mod failure_table_tests {
|
||||
use super::{FailedOperation, FailureTable};
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_write_nobody_asked_for_is_a_restore() {
|
||||
assert_eq!(super::what_was_asked(), FailedOperation::SystemProxyRestore);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_declared_intent_reaches_the_place_the_write_fails() {
|
||||
let asked = super::asking_for(FailedOperation::SystemProxyEnable, async {
|
||||
|
||||
@@ -573,7 +573,7 @@ mod tests {
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
use std::task::Poll;
|
||||
use tokio::sync::Barrier;
|
||||
@@ -609,16 +609,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_periodic_refresh_stops_after_owner_loss() {
|
||||
let generation = AtomicU64::new(7);
|
||||
let captured_generation = generation.load(Ordering::Acquire);
|
||||
|
||||
assert!(guard_generation_is_current(&generation, captured_generation));
|
||||
generation.fetch_add(1, Ordering::AcqRel);
|
||||
assert!(!guard_generation_is_current(&generation, captured_generation));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn final_service_mutation_finishes_after_in_flight_guard_request() {
|
||||
let operations = Arc::new(ServiceProxyOperations::new());
|
||||
@@ -786,15 +776,6 @@ mod tests {
|
||||
assert!(refreshed.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_replaced_guard_does_not_get_to_report_its_last_failure() {
|
||||
let operations = ServiceProxyOperations::new();
|
||||
let generation = operations.invalidate_guard();
|
||||
operations.invalidate_guard();
|
||||
|
||||
assert!(!operations.record_if_current(generation, || true).await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_report_waiting_on_the_lock_says_nothing_if_it_is_invalidated_meanwhile() {
|
||||
// The generation check must happen after acquiring the operation lock.
|
||||
@@ -920,32 +901,6 @@ mod tests {
|
||||
wrapped_privilege_failure().context(SysproxyFailure::PrivilegeRequired)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_classification_survives_the_anyhow_layers_above_it() {
|
||||
let classified = classified_privilege_failure()
|
||||
.context("failed to apply system proxy after start")
|
||||
.context("failed to restart the core");
|
||||
|
||||
assert_eq!(
|
||||
SysproxyFailure::from_chain(&classified).map(SysproxyFailure::code),
|
||||
Some("SYSPROXY_PRIVILEGE_REQUIRED")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn classifying_keeps_the_original_failure_underneath() {
|
||||
let classified = classified_privilege_failure();
|
||||
|
||||
assert!(
|
||||
classified.chain().any(|cause| matches!(
|
||||
cause.downcast_ref::<sysproxy::Error>(),
|
||||
Some(sysproxy::Error::RequiresAdminPrivileges)
|
||||
)),
|
||||
"the original error must survive classification: {classified:#}"
|
||||
);
|
||||
assert!(format!("{classified:#}").contains("failed to apply the system proxy"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unrelated_failure_is_left_alone() {
|
||||
let untouched = classify_local_failure(anyhow::anyhow!("port already in use"));
|
||||
@@ -1097,14 +1052,6 @@ mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_failure_that_can_be_explained_is_filed_here_as_a_restore() {
|
||||
assert!(matches!(
|
||||
super::table_effect(&Err(mappable()), true),
|
||||
super::TableEffect::File(FailedOperation::SystemProxyRestore, _)
|
||||
));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn a_failure_is_filed_as_whatever_the_caller_declared() {
|
||||
let (applied, cleared) = super::notification::asking_for(FailedOperation::SystemProxyEnable, async {
|
||||
|
||||
@@ -176,25 +176,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_usable_requires_no_operation_in_flight() {
|
||||
assert!(state(ServiceHealth::Ready, false, false).service_usable());
|
||||
assert!(!state(ServiceHealth::Ready, false, true).service_usable());
|
||||
assert!(!state(ServiceHealth::NotInstalled, false, false).service_usable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_ready_ignores_operations_in_flight() {
|
||||
assert!(state(ServiceHealth::Ready, false, true).service_ready());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tun_capable_when_elevated_even_without_service() {
|
||||
assert!(state(ServiceHealth::NotInstalled, true, false).tun_capable());
|
||||
assert!(state(ServiceHealth::Unavailable("boom".into()), true, true).tun_capable());
|
||||
assert!(!state(ServiceHealth::NotInstalled, false, false).tun_capable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_service_that_is_merely_absent_needs_no_decision() {
|
||||
assert!(!state(ServiceHealth::NotInstalled, false, false).service_needs_attention());
|
||||
@@ -244,14 +225,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_session_that_settled_on_sidecar_needs_nothing() {
|
||||
let mut run_state = state(ServiceHealth::VersionMismatch, false, false);
|
||||
run_state.sidecar_allowed = true;
|
||||
|
||||
assert!(!run_state.service_needs_attention());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_view_carries_the_derived_answers_and_the_unavailable_reason() {
|
||||
let mut run_state = state(ServiceHealth::Unavailable("socket refused".into()), true, false);
|
||||
@@ -347,17 +320,6 @@ mod tests {
|
||||
assert!(!state(ServiceHealth::NotInstalled, false, true).tun_should_be_disabled(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_tun_reconciliation_waits_until_the_core_is_running() {
|
||||
let stopped = state(ServiceHealth::NotInstalled, false, false);
|
||||
|
||||
assert_eq!(stopped.mode, RunningMode::NotRunning);
|
||||
assert!(
|
||||
stopped.tun_should_be_disabled(true),
|
||||
"the predicate alone says yes, which is why the caller must gate on the mode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tun_is_never_disabled_on_an_unprobed_service() {
|
||||
assert!(!state(ServiceHealth::Unknown, false, false).tun_should_be_disabled(true));
|
||||
@@ -387,30 +349,4 @@ mod tests {
|
||||
|
||||
assert!(settled.tun_should_be_disabled(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn observing_clears_a_pending_request() {
|
||||
let mut stored = StoredService::default();
|
||||
stored.request(PendingAction::Install);
|
||||
stored.observe(ServiceHealth::Ready);
|
||||
|
||||
assert_eq!(stored.pending, None);
|
||||
assert!(!stored.sidecar_allowed);
|
||||
assert_eq!(stored.health, ServiceHealth::Ready);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requesting_and_allowing_sidecar_are_mutually_exclusive() {
|
||||
let mut stored = StoredService::default();
|
||||
stored.observe(ServiceHealth::NotInstalled);
|
||||
|
||||
stored.request(PendingAction::Install);
|
||||
assert!(!stored.sidecar_allowed);
|
||||
|
||||
stored.allow_sidecar();
|
||||
assert_eq!(stored.pending, None);
|
||||
assert!(stored.sidecar_allowed);
|
||||
|
||||
assert_eq!(stored.health, ServiceHealth::NotInstalled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,22 +209,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_without_evidence_is_not_installed() {
|
||||
assert_eq!(
|
||||
classify_service_health(CurrentServiceProbe::Missing, false, ""),
|
||||
ServiceHealth::NotInstalled
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_with_evidence_means_broken_not_absent() {
|
||||
assert_eq!(
|
||||
classify_service_health(CurrentServiceProbe::Missing, true, ""),
|
||||
ServiceHealth::VersionMismatch
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unreachable_service_keeps_the_reason() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -818,37 +818,6 @@ mod tests {
|
||||
assert_eq!(authoritative_state(&refused, false), AuthoritativeState::Unchanged);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[test]
|
||||
fn only_evidence_that_a_write_landed_stands_the_guard_down() {
|
||||
let nothing_written = anyhow::Error::new(sysproxy::Error::ProxyWrite {
|
||||
progress: sysproxy::WriteProgress::new(0, 7),
|
||||
source: Box::new(sysproxy::Error::RequiresAdminPrivileges),
|
||||
});
|
||||
let something_written = anyhow::Error::new(sysproxy::Error::ProxyWrite {
|
||||
progress: sysproxy::WriteProgress::new(3, 7),
|
||||
source: Box::new(sysproxy::Error::RequiresAdminPrivileges),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
authoritative_state(¬hing_written, false),
|
||||
AuthoritativeState::Unchanged
|
||||
);
|
||||
assert_eq!(
|
||||
authoritative_state(&something_written, false),
|
||||
AuthoritativeState::Unknown
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[test]
|
||||
fn a_step_that_already_finished_stands_the_guard_down_whatever_the_error_says() {
|
||||
let refused = anyhow::Error::new(sysproxy::Error::RequiresAdminPrivileges);
|
||||
|
||||
assert_eq!(authoritative_state(&refused, false), AuthoritativeState::Unchanged);
|
||||
assert_eq!(authoritative_state(&refused, true), AuthoritativeState::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn where_a_successful_setter_proves_nothing_no_failure_is_destructive() {
|
||||
let strongest_evidence = anyhow::Error::new(sysproxy::Error::ProxyWrite {
|
||||
@@ -865,31 +834,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn where_it_proves_something_both_kinds_of_evidence_count() {
|
||||
let nothing_written = anyhow::Error::new(sysproxy::Error::ProxyWrite {
|
||||
progress: sysproxy::WriteProgress::new(0, 7),
|
||||
source: Box::new(sysproxy::Error::RequiresAdminPrivileges),
|
||||
});
|
||||
let partly_written = anyhow::Error::new(sysproxy::Error::ProxyWrite {
|
||||
progress: sysproxy::WriteProgress::new(3, 7),
|
||||
source: Box::new(sysproxy::Error::RequiresAdminPrivileges),
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
authoritative_state_from(true, ¬hing_written, false),
|
||||
AuthoritativeState::Unchanged
|
||||
);
|
||||
assert_eq!(
|
||||
authoritative_state_from(true, &partly_written, false),
|
||||
AuthoritativeState::Unknown
|
||||
);
|
||||
assert_eq!(
|
||||
authoritative_state_from(true, ¬hing_written, true),
|
||||
AuthoritativeState::Unknown
|
||||
);
|
||||
}
|
||||
|
||||
fn holding_global(port: u16) -> sysproxy::ProxySnapshot {
|
||||
let endpoint = || sysproxy::ProxyEndpoint {
|
||||
host: "127.0.0.1".to_owned(),
|
||||
|
||||
@@ -396,15 +396,7 @@ fn read_core_version(path: &Path) -> Result<std::string::String> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{asset_base_name, is_usable_version, package_url};
|
||||
|
||||
#[test]
|
||||
fn asset_names_match_the_prebuild_map() {
|
||||
let release = asset_base_name(false).unwrap_or_default();
|
||||
let alpha = asset_base_name(true).unwrap_or_default();
|
||||
assert!(release.starts_with("mihomo-"));
|
||||
assert!(alpha.starts_with("mihomo-"));
|
||||
}
|
||||
use super::{is_usable_version, package_url};
|
||||
|
||||
#[test]
|
||||
fn only_plain_version_tokens_reach_the_package_url() {
|
||||
|
||||
Reference in New Issue
Block a user