ant_node_manager/
lib.rs

1// Copyright (C) 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9#[macro_use]
10extern crate tracing;
11
12pub mod add_services;
13pub mod cmd;
14pub mod config;
15pub mod error;
16pub mod helpers;
17pub mod local;
18pub mod rpc;
19pub mod rpc_client;
20
21pub const DEFAULT_NODE_STARTUP_CONNECTION_TIMEOUT_S: u64 = 300;
22
23#[derive(Clone, Copy, PartialEq, Debug)]
24pub enum VerbosityLevel {
25    Minimal,
26    Normal,
27    Full,
28}
29
30impl From<u8> for VerbosityLevel {
31    fn from(verbosity: u8) -> Self {
32        match verbosity {
33            1 => VerbosityLevel::Minimal,
34            2 => VerbosityLevel::Normal,
35            3 => VerbosityLevel::Full,
36            _ => VerbosityLevel::Normal,
37        }
38    }
39}
40
41use crate::error::{Error, Result};
42use ant_service_management::rpc::RpcActions;
43use ant_service_management::{
44    control::ServiceControl, error::Error as ServiceError, rpc::RpcClient, NodeRegistry,
45    NodeService, NodeServiceData, ServiceStateActions, ServiceStatus, UpgradeOptions,
46    UpgradeResult,
47};
48use colored::Colorize;
49use semver::Version;
50use tracing::debug;
51
52pub const DAEMON_DEFAULT_PORT: u16 = 12500;
53pub const DAEMON_SERVICE_NAME: &str = "antctld";
54
55const RPC_START_UP_DELAY_MS: u64 = 3000;
56
57pub struct ServiceManager<T: ServiceStateActions + Send> {
58    pub service: T,
59    pub service_control: Box<dyn ServiceControl + Send>,
60    pub verbosity: VerbosityLevel,
61}
62
63impl<T: ServiceStateActions + Send> ServiceManager<T> {
64    pub fn new(
65        service: T,
66        service_control: Box<dyn ServiceControl + Send>,
67        verbosity: VerbosityLevel,
68    ) -> Self {
69        ServiceManager {
70            service,
71            service_control,
72            verbosity,
73        }
74    }
75
76    pub async fn start(&mut self) -> Result<()> {
77        info!("Starting the {} service", self.service.name());
78        if ServiceStatus::Running == self.service.status() {
79            // The last time we checked the service was running, but it doesn't mean it's actually
80            // running now. If it is running, we don't need to do anything. If it stopped because
81            // of a fault, we will drop to the code below and attempt to start it again.
82            // We use `get_process_pid` because it searches for the process with the service binary
83            // path, and this path is unique to each service.
84            if self
85                .service_control
86                .get_process_pid(&self.service.bin_path())
87                .is_ok()
88            {
89                debug!("The {} service is already running", self.service.name());
90                if self.verbosity != VerbosityLevel::Minimal {
91                    println!("The {} service is already running", self.service.name());
92                }
93                return Ok(());
94            }
95        }
96
97        // At this point the service either hasn't been started for the first time or it has been
98        // stopped. If it was stopped, it was either intentional or because it crashed.
99        if self.verbosity != VerbosityLevel::Minimal {
100            println!("Attempting to start {}...", self.service.name());
101        }
102        self.service_control
103            .start(&self.service.name(), self.service.is_user_mode())?;
104        self.service_control.wait(RPC_START_UP_DELAY_MS);
105
106        // This is an attempt to see whether the service process has actually launched. You don't
107        // always get an error from the service infrastructure.
108        //
109        // There might be many different `antnode` processes running, but since each service has
110        // its own isolated binary, we use the binary path to uniquely identify it.
111        match self
112            .service_control
113            .get_process_pid(&self.service.bin_path())
114        {
115            Ok(pid) => {
116                debug!(
117                    "Service process started for {} with PID {}",
118                    self.service.name(),
119                    pid
120                );
121                self.service.on_start(Some(pid), true).await?;
122
123                info!(
124                    "Service {} has been started successfully",
125                    self.service.name()
126                );
127            }
128            Err(ant_service_management::error::Error::ServiceProcessNotFound(_)) => {
129                error!("The '{}' service has failed to start because ServiceProcessNotFound when fetching PID", self.service.name());
130                return Err(Error::PidNotFoundAfterStarting);
131            }
132            Err(err) => {
133                error!("Failed to start service, because PID could not be obtained: {err}");
134                return Err(err.into());
135            }
136        };
137
138        if self.verbosity != VerbosityLevel::Minimal {
139            println!("{} Started {} service", "✓".green(), self.service.name());
140            println!(
141                "  - PID: {}",
142                self.service
143                    .pid()
144                    .map_or("-".to_string(), |p| p.to_string())
145            );
146            println!(
147                "  - Bin path: {}",
148                self.service.bin_path().to_string_lossy()
149            );
150            println!(
151                "  - Data path: {}",
152                self.service.data_dir_path().to_string_lossy()
153            );
154            println!(
155                "  - Logs path: {}",
156                self.service.log_dir_path().to_string_lossy()
157            );
158        }
159        Ok(())
160    }
161
162    pub async fn stop(&mut self) -> Result<()> {
163        info!("Stopping the {} service", self.service.name());
164        match self.service.status() {
165            ServiceStatus::Added => {
166                debug!(
167                    "The {} service has not been started since it was installed",
168                    self.service.name()
169                );
170                if self.verbosity != VerbosityLevel::Minimal {
171                    println!(
172                        "Service {} has not been started since it was installed",
173                        self.service.name()
174                    );
175                }
176                Ok(())
177            }
178            ServiceStatus::Removed => {
179                debug!("The {} service has been removed", self.service.name());
180                if self.verbosity != VerbosityLevel::Minimal {
181                    println!("Service {} has been removed", self.service.name());
182                }
183                Ok(())
184            }
185            ServiceStatus::Running => {
186                let pid = self.service.pid().ok_or(Error::PidNotSet)?;
187                let name = self.service.name();
188
189                if self
190                    .service_control
191                    .get_process_pid(&self.service.bin_path())
192                    .is_ok()
193                {
194                    if self.verbosity != VerbosityLevel::Minimal {
195                        println!("Attempting to stop {}...", name);
196                    }
197                    self.service_control
198                        .stop(&name, self.service.is_user_mode())?;
199                    if self.verbosity != VerbosityLevel::Minimal {
200                        println!(
201                            "{} Service {} with PID {} was stopped",
202                            "✓".green(),
203                            name,
204                            pid
205                        );
206                    }
207                } else if self.verbosity != VerbosityLevel::Minimal {
208                    debug!("Service {name} was already stopped");
209                    println!("{} Service {} was already stopped", "✓".green(), name);
210                }
211
212                self.service.on_stop().await?;
213                info!("Service {name} has been stopped successfully.");
214                Ok(())
215            }
216            ServiceStatus::Stopped => {
217                debug!("Service {} was already stopped", self.service.name());
218                if self.verbosity != VerbosityLevel::Minimal {
219                    println!(
220                        "{} Service {} was already stopped",
221                        "✓".green(),
222                        self.service.name()
223                    );
224                }
225                Ok(())
226            }
227        }
228    }
229
230    pub async fn remove(&mut self, keep_directories: bool) -> Result<()> {
231        if let ServiceStatus::Running = self.service.status() {
232            if self
233                .service_control
234                .get_process_pid(&self.service.bin_path())
235                .is_ok()
236            {
237                error!(
238                    "Service {} is already running. Stop it before removing it",
239                    self.service.name()
240                );
241                return Err(Error::ServiceAlreadyRunning(vec![self.service.name()]));
242            } else {
243                // If the node wasn't actually running, we should give the user an opportunity to
244                // check why it may have failed before removing everything.
245                self.service.on_stop().await?;
246                error!(
247                "The service: {} was marked as running but it had actually stopped. You may want to check the logs for errors before removing it. To remove the service, run the command again.",
248                self.service.name()
249            );
250                return Err(Error::ServiceStatusMismatch {
251                    expected: ServiceStatus::Running,
252                });
253            }
254        }
255
256        match self
257            .service_control
258            .uninstall(&self.service.name(), self.service.is_user_mode())
259        {
260            Ok(()) => {
261                debug!("Service {} has been uninstalled", self.service.name());
262            }
263            Err(err) => match err {
264                ServiceError::ServiceRemovedManually(name) => {
265                    warn!("The user appears to have removed the {name} service manually. Skipping the error.",);
266                    // The user has deleted the service definition file, which the service manager
267                    // crate treats as an error. We then return our own error type, which allows us
268                    // to handle it here and just proceed with removing the service from the
269                    // registry.
270                    println!("The user appears to have removed the {name} service manually");
271                }
272                ServiceError::ServiceDoesNotExists(name) => {
273                    warn!("The service {name} has most probably been removed already, it does not exists. Skipping the error.");
274                }
275                _ => {
276                    error!("Error uninstalling the service: {err}");
277                    return Err(err.into());
278                }
279            },
280        }
281
282        if !keep_directories {
283            debug!(
284                "Removing data and log directories for {}",
285                self.service.name()
286            );
287            // It's possible the user deleted either of these directories manually.
288            // We can just proceed with removing the service from the registry.
289            if self.service.data_dir_path().exists() {
290                debug!("Removing data directory {:?}", self.service.data_dir_path());
291                std::fs::remove_dir_all(self.service.data_dir_path())?;
292            }
293            if self.service.log_dir_path().exists() {
294                debug!("Removing log directory {:?}", self.service.log_dir_path());
295                std::fs::remove_dir_all(self.service.log_dir_path())?;
296            }
297        }
298
299        self.service.on_remove();
300        info!(
301            "Service {} has been removed successfully.",
302            self.service.name()
303        );
304
305        if self.verbosity != VerbosityLevel::Minimal {
306            println!(
307                "{} Service {} was removed",
308                "✓".green(),
309                self.service.name()
310            );
311        }
312
313        Ok(())
314    }
315
316    pub async fn upgrade(&mut self, options: UpgradeOptions) -> Result<UpgradeResult> {
317        let current_version = Version::parse(&self.service.version())?;
318        if !options.force
319            && (current_version == options.target_version
320                || options.target_version < current_version)
321        {
322            info!(
323                "The service {} is already at the latest version. No upgrade is required.",
324                self.service.name()
325            );
326            return Ok(UpgradeResult::NotRequired);
327        }
328
329        debug!("Stopping the service and copying the binary");
330        self.stop().await?;
331        std::fs::copy(options.clone().target_bin_path, self.service.bin_path())?;
332
333        self.service_control
334            .uninstall(&self.service.name(), self.service.is_user_mode())?;
335        self.service_control.install(
336            self.service
337                .build_upgrade_install_context(options.clone())?,
338            self.service.is_user_mode(),
339        )?;
340
341        if options.start_service {
342            match self.start().await {
343                Ok(start_duration) => start_duration,
344                Err(err) => {
345                    self.service
346                        .set_version(&options.target_version.to_string());
347                    info!("The service has been upgraded but could not be started: {err}");
348                    return Ok(UpgradeResult::UpgradedButNotStarted(
349                        current_version.to_string(),
350                        options.target_version.to_string(),
351                        err.to_string(),
352                    ));
353                }
354            }
355        }
356        self.service
357            .set_version(&options.target_version.to_string());
358
359        if options.force {
360            Ok(UpgradeResult::Forced(
361                current_version.to_string(),
362                options.target_version.to_string(),
363            ))
364        } else {
365            Ok(UpgradeResult::Upgraded(
366                current_version.to_string(),
367                options.target_version.to_string(),
368            ))
369        }
370    }
371}
372
373pub async fn status_report(
374    node_registry: &mut NodeRegistry,
375    service_control: &dyn ServiceControl,
376    detailed_view: bool,
377    output_json: bool,
378    fail: bool,
379    is_local_network: bool,
380) -> Result<()> {
381    refresh_node_registry(
382        node_registry,
383        service_control,
384        !output_json,
385        true,
386        is_local_network,
387    )
388    .await?;
389
390    if output_json {
391        let json = serde_json::to_string_pretty(&node_registry.to_status_summary())?;
392        println!("{json}");
393    } else if detailed_view {
394        for node in &node_registry.nodes {
395            print_banner(&format!(
396                "{} - {}",
397                &node.service_name,
398                format_status_without_colour(&node.status)
399            ));
400            println!("Version: {}", node.version);
401            println!(
402                "Peer ID: {}",
403                node.peer_id.map_or("-".to_string(), |p| p.to_string())
404            );
405            println!("RPC Socket: {}", node.rpc_socket_addr);
406            println!("Listen Addresses: {:?}", node.listen_addr);
407            println!(
408                "PID: {}",
409                node.pid.map_or("-".to_string(), |p| p.to_string())
410            );
411            if node.status == ServiceStatus::Stopped {
412                if let Some(failure_reason) = node.get_critical_failure() {
413                    println!(
414                        "Failure reason: [{}] {}",
415                        failure_reason.0, failure_reason.1
416                    );
417                }
418            }
419            println!("Data path: {}", node.data_dir_path.to_string_lossy());
420            println!("Log path: {}", node.log_dir_path.to_string_lossy());
421            println!("Bin path: {}", node.antnode_path.to_string_lossy());
422            println!(
423                "Connected peers: {}",
424                node.connected_peers
425                    .as_ref()
426                    .map_or("-".to_string(), |p| p.len().to_string())
427            );
428            println!(
429                "Reward balance: {}",
430                node.reward_balance
431                    .map_or("-".to_string(), |b| b.to_string())
432            );
433            println!("Rewards address: {}", node.rewards_address);
434            println!();
435        }
436
437        if let Some(daemon) = &node_registry.daemon {
438            print_banner(&format!(
439                "{} - {}",
440                &daemon.service_name,
441                format_status(&daemon.status)
442            ));
443            println!("Version: {}", daemon.version);
444            println!("Bin path: {}", daemon.daemon_path.to_string_lossy());
445        }
446
447        if let Some(faucet) = &node_registry.faucet {
448            print_banner(&format!(
449                "{} - {}",
450                &faucet.service_name,
451                format_status(&faucet.status)
452            ));
453            println!("Version: {}", faucet.version);
454            println!("Bin path: {}", faucet.faucet_path.to_string_lossy());
455            println!("Log path: {}", faucet.log_dir_path.to_string_lossy());
456        }
457    } else {
458        println!(
459            "{:<18} {:<52} {:<7} {:>15} {:<}",
460            "Service Name", "Peer ID", "Status", "Connected Peers", "Failure"
461        );
462        let nodes = node_registry
463            .nodes
464            .iter()
465            .filter(|n| n.status != ServiceStatus::Removed)
466            .collect::<Vec<&NodeServiceData>>();
467        for node in nodes {
468            let peer_id = node.peer_id.map_or("-".to_string(), |p| p.to_string());
469            let connected_peers = node
470                .connected_peers
471                .clone()
472                .map_or("-".to_string(), |p| p.len().to_string());
473            let failure_reason = if node.status == ServiceStatus::Stopped {
474                node.get_critical_failure()
475                    .map_or("-".to_string(), |(_time, reason)| reason)
476            } else {
477                "-".to_string()
478            };
479            println!(
480                "{:<18} {:<52} {:<7} {:>15} {:<}",
481                node.service_name,
482                peer_id,
483                format_status(&node.status),
484                connected_peers,
485                failure_reason
486            );
487        }
488        if let Some(daemon) = &node_registry.daemon {
489            println!(
490                "{:<18} {:<52} {:<7} {:>15} {:>15}",
491                daemon.service_name,
492                "-",
493                format_status(&daemon.status),
494                "-",
495                "-"
496            );
497        }
498        if let Some(faucet) = &node_registry.faucet {
499            println!(
500                "{:<18} {:<52} {:<7} {:>15} {:>15}",
501                faucet.service_name,
502                "-",
503                format_status(&faucet.status),
504                "-",
505                "-"
506            );
507        }
508    }
509
510    if fail {
511        let non_running_services = node_registry
512            .nodes
513            .iter()
514            .filter_map(|n| {
515                if n.status != ServiceStatus::Running {
516                    Some(n.service_name.clone())
517                } else {
518                    None
519                }
520            })
521            .collect::<Vec<String>>();
522        if non_running_services.is_empty() {
523            info!("Fail is set to true, but all services are running.");
524        } else {
525            error!(
526                "One or more nodes are not in a running state: {non_running_services:?}
527            "
528            );
529
530            return Err(Error::ServiceNotRunning(non_running_services));
531        }
532    }
533
534    Ok(())
535}
536
537/// Refreshes the status of the node registry's services.
538///
539/// The mechanism is different, depending on whether it's a service-based network or a local
540/// network.
541///
542/// For a service-based network, at a minimum, the refresh determines if each service is running.
543/// It does that by trying to find a process whose binary path matches the path of the binary for
544/// the service. Since each service uses its own binary, the path is a unique identifer. So you can
545/// know if any *particular* service is running or not. A full refresh uses the RPC client to
546/// connect to the node's RPC service to determine things like the number of connected peers.
547///
548/// For a local network, the node paths are not unique, so we can't use that. We consider the node
549/// running if we can connect to its RPC service; otherwise it is considered stopped.
550pub async fn refresh_node_registry(
551    node_registry: &mut NodeRegistry,
552    service_control: &dyn ServiceControl,
553    print_refresh_message: bool,
554    full_refresh: bool,
555    is_local_network: bool,
556) -> Result<()> {
557    // This message is useful for users, but needs to be suppressed when a JSON output is
558    // requested.
559    if print_refresh_message {
560        println!("Refreshing the node registry...");
561    }
562    info!("Refreshing the node registry");
563
564    for node in &mut node_registry.nodes {
565        // The `status` command can run before a node is started and therefore before its wallet
566        // exists.
567        // TODO: remove this as we have no way to know the reward balance of nodes since EVM payments!
568        node.reward_balance = None;
569
570        let mut rpc_client = RpcClient::from_socket_addr(node.rpc_socket_addr);
571        rpc_client.set_max_attempts(1);
572        let mut service = NodeService::new(node, Box::new(rpc_client.clone()));
573
574        if is_local_network {
575            // For a local network, retrieving the process by its path does not work, because the
576            // paths are not unique: they are all launched from the same binary. Instead we will
577            // just determine whether the node is running by connecting to its RPC service. We
578            // only need to distinguish between `RUNNING` and `STOPPED` for a local network.
579            match rpc_client.node_info().await {
580                Ok(info) => {
581                    let pid = info.pid;
582                    debug!(
583                        "local node {} is running with PID {pid}",
584                        service.service_data.service_name
585                    );
586                    service.on_start(Some(pid), full_refresh).await?;
587                }
588                Err(_) => {
589                    debug!(
590                        "Failed to retrieve PID for local node {}",
591                        service.service_data.service_name
592                    );
593                    service.on_stop().await?;
594                }
595            }
596        } else {
597            match service_control.get_process_pid(&service.bin_path()) {
598                Ok(pid) => {
599                    debug!(
600                        "{} is running with PID {pid}",
601                        service.service_data.service_name
602                    );
603                    service.on_start(Some(pid), full_refresh).await?;
604                }
605                Err(_) => {
606                    match service.status() {
607                        ServiceStatus::Added => {
608                            // If the service is still at `Added` status, there hasn't been an attempt
609                            // to start it since it was installed. It's useful to keep this status
610                            // rather than setting it to `STOPPED`, so that the user can differentiate.
611                            debug!(
612                                "{} has not been started since it was installed",
613                                service.service_data.service_name
614                            );
615                        }
616                        ServiceStatus::Removed => {
617                            // In the case of the service being removed, we want to retain that state
618                            // and not have it marked `STOPPED`.
619                            debug!("{} has been removed", service.service_data.service_name);
620                        }
621                        _ => {
622                            debug!(
623                                "Failed to retrieve PID for {}",
624                                service.service_data.service_name
625                            );
626                            service.on_stop().await?;
627                        }
628                    }
629                }
630            }
631        }
632    }
633    Ok(())
634}
635
636pub fn print_banner(text: &str) {
637    let padding = 2;
638    let text_width = text.len() + padding * 2;
639    let border_chars = 2;
640    let total_width = text_width + border_chars;
641    let top_bottom = "═".repeat(total_width);
642
643    println!("╔{}╗", top_bottom);
644    println!("║ {:^width$} ║", text, width = text_width);
645    println!("╚{}╝", top_bottom);
646}
647
648fn format_status(status: &ServiceStatus) -> String {
649    match status {
650        ServiceStatus::Running => "RUNNING".green().to_string(),
651        ServiceStatus::Stopped => "STOPPED".red().to_string(),
652        ServiceStatus::Added => "ADDED".yellow().to_string(),
653        ServiceStatus::Removed => "REMOVED".red().to_string(),
654    }
655}
656
657fn format_status_without_colour(status: &ServiceStatus) -> String {
658    match status {
659        ServiceStatus::Running => "RUNNING".to_string(),
660        ServiceStatus::Stopped => "STOPPED".to_string(),
661        ServiceStatus::Added => "ADDED".to_string(),
662        ServiceStatus::Removed => "REMOVED".to_string(),
663    }
664}
665
666#[cfg(test)]
667mod tests {
668    use super::*;
669    use ant_bootstrap::InitialPeersConfig;
670    use ant_evm::{AttoTokens, CustomNetwork, EvmNetwork, RewardsAddress};
671    use ant_logging::LogFormat;
672    use ant_service_management::{
673        error::{Error as ServiceControlError, Result as ServiceControlResult},
674        node::{NodeService, NodeServiceData},
675        rpc::{NetworkInfo, NodeInfo, RecordAddress, RpcActions},
676        UpgradeOptions, UpgradeResult,
677    };
678    use assert_fs::prelude::*;
679    use assert_matches::assert_matches;
680    use async_trait::async_trait;
681    use color_eyre::eyre::Result;
682    use libp2p_identity::PeerId;
683    use mockall::{mock, predicate::*};
684    use predicates::prelude::*;
685    use service_manager::ServiceInstallCtx;
686    use std::{
687        ffi::OsString,
688        net::{IpAddr, Ipv4Addr, SocketAddr},
689        path::{Path, PathBuf},
690        str::FromStr,
691        time::Duration,
692    };
693
694    mock! {
695        pub RpcClient {}
696        #[async_trait]
697        impl RpcActions for RpcClient {
698            async fn node_info(&self) -> ServiceControlResult<NodeInfo>;
699            async fn network_info(&self) -> ServiceControlResult<NetworkInfo>;
700            async fn record_addresses(&self) -> ServiceControlResult<Vec<RecordAddress>>;
701            async fn node_restart(&self, delay_millis: u64, retain_peer_id: bool) -> ServiceControlResult<()>;
702            async fn node_stop(&self, delay_millis: u64) -> ServiceControlResult<()>;
703            async fn node_update(&self, delay_millis: u64) -> ServiceControlResult<()>;
704            async fn is_node_connected_to_network(&self, timeout: std::time::Duration) -> ServiceControlResult<()>;
705            async fn update_log_level(&self, log_levels: String) -> ServiceControlResult<()>;
706        }
707    }
708
709    mock! {
710        pub ServiceControl {}
711        impl ServiceControl for ServiceControl {
712            fn create_service_user(&self, username: &str) -> ServiceControlResult<()>;
713            fn get_available_port(&self) -> ServiceControlResult<u16>;
714            fn install(&self, install_ctx: ServiceInstallCtx, user_mode: bool) -> ServiceControlResult<()>;
715            fn get_process_pid(&self, bin_path: &Path) -> ServiceControlResult<u32>;
716            fn start(&self, service_name: &str, user_mode: bool) -> ServiceControlResult<()>;
717            fn stop(&self, service_name: &str, user_mode: bool) -> ServiceControlResult<()>;
718            fn uninstall(&self, service_name: &str, user_mode: bool) -> ServiceControlResult<()>;
719            fn wait(&self, delay: u64);
720        }
721    }
722
723    #[tokio::test]
724    async fn start_should_start_a_newly_installed_service() -> Result<()> {
725        let mut mock_service_control = MockServiceControl::new();
726        let mut mock_rpc_client = MockRpcClient::new();
727
728        mock_service_control
729            .expect_start()
730            .with(eq("antnode1"), eq(false))
731            .times(1)
732            .returning(|_, _| Ok(()));
733        mock_service_control
734            .expect_wait()
735            .with(eq(3000))
736            .times(1)
737            .returning(|_| ());
738        mock_service_control
739            .expect_get_process_pid()
740            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
741            .times(1)
742            .returning(|_| Ok(1000));
743
744        mock_rpc_client.expect_node_info().times(1).returning(|| {
745            Ok(NodeInfo {
746                pid: 1000,
747                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
748                data_path: PathBuf::from("/var/antctl/services/antnode1"),
749                log_path: PathBuf::from("/var/log/antnode/antnode1"),
750                version: "0.98.1".to_string(),
751                uptime: std::time::Duration::from_secs(1), // the service was just started
752                wallet_balance: 0,
753            })
754        });
755        mock_rpc_client
756            .expect_network_info()
757            .times(1)
758            .returning(|| {
759                Ok(NetworkInfo {
760                    connected_peers: vec![PeerId::from_str(
761                        "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
762                    )?],
763                    listeners: Vec::new(),
764                })
765            });
766
767        let mut service_data = NodeServiceData {
768            auto_restart: false,
769            connected_peers: None,
770            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
771            evm_network: EvmNetwork::Custom(CustomNetwork {
772                rpc_url_http: "http://localhost:8545".parse()?,
773                payment_token_address: RewardsAddress::from_str(
774                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
775                )?,
776                data_payments_address: RewardsAddress::from_str(
777                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
778                )?,
779            }),
780            home_network: false,
781            listen_addr: None,
782            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
783            log_format: None,
784            max_archived_log_files: None,
785            max_log_files: None,
786            metrics_port: None,
787            network_id: None,
788            node_ip: None,
789            node_port: None,
790            number: 1,
791            peer_id: None,
792            peers_args: InitialPeersConfig::default(),
793            pid: None,
794            rewards_address: RewardsAddress::from_str(
795                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
796            )?,
797            reward_balance: Some(AttoTokens::zero()),
798            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
799            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
800            service_name: "antnode1".to_string(),
801            status: ServiceStatus::Added,
802            upnp: false,
803            user: Some("ant".to_string()),
804            user_mode: false,
805            version: "0.98.1".to_string(),
806        };
807        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
808
809        let mut service_manager = ServiceManager::new(
810            service,
811            Box::new(mock_service_control),
812            VerbosityLevel::Normal,
813        );
814
815        service_manager.start().await?;
816
817        assert_eq!(
818            service_manager.service.service_data.connected_peers,
819            Some(vec![PeerId::from_str(
820                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
821            )?,])
822        );
823        assert_eq!(service_manager.service.service_data.pid, Some(1000));
824        assert_eq!(
825            service_manager.service.service_data.peer_id,
826            Some(PeerId::from_str(
827                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
828            )?)
829        );
830        assert_matches!(
831            service_manager.service.service_data.status,
832            ServiceStatus::Running
833        );
834
835        Ok(())
836    }
837
838    #[tokio::test]
839    async fn start_should_start_a_stopped_service() -> Result<()> {
840        let mut mock_service_control = MockServiceControl::new();
841        let mut mock_rpc_client = MockRpcClient::new();
842
843        mock_service_control
844            .expect_start()
845            .with(eq("antnode1"), eq(false))
846            .times(1)
847            .returning(|_, _| Ok(()));
848        mock_service_control
849            .expect_wait()
850            .with(eq(3000))
851            .times(1)
852            .returning(|_| ());
853        mock_service_control
854            .expect_get_process_pid()
855            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
856            .times(1)
857            .returning(|_| Ok(1000));
858
859        mock_rpc_client.expect_node_info().times(1).returning(|| {
860            Ok(NodeInfo {
861                pid: 1000,
862                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
863                data_path: PathBuf::from("/var/antctl/services/antnode1"),
864                log_path: PathBuf::from("/var/log/antnode/antnode1"),
865                version: "0.98.1".to_string(),
866                uptime: std::time::Duration::from_secs(1), // the service was just started
867                wallet_balance: 0,
868            })
869        });
870        mock_rpc_client
871            .expect_network_info()
872            .times(1)
873            .returning(|| {
874                Ok(NetworkInfo {
875                    connected_peers: Vec::new(),
876                    listeners: Vec::new(),
877                })
878            });
879
880        let mut service_data = NodeServiceData {
881            auto_restart: false,
882            connected_peers: None,
883            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
884            evm_network: EvmNetwork::Custom(CustomNetwork {
885                rpc_url_http: "http://localhost:8545".parse()?,
886                payment_token_address: RewardsAddress::from_str(
887                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
888                )?,
889                data_payments_address: RewardsAddress::from_str(
890                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
891                )?,
892            }),
893            home_network: false,
894            listen_addr: None,
895            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
896            log_format: None,
897            max_archived_log_files: None,
898            max_log_files: None,
899            metrics_port: None,
900            network_id: None,
901            node_ip: None,
902            node_port: None,
903            number: 1,
904            peer_id: Some(PeerId::from_str(
905                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
906            )?),
907            peers_args: InitialPeersConfig::default(),
908            pid: None,
909            rewards_address: RewardsAddress::from_str(
910                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
911            )?,
912            reward_balance: Some(AttoTokens::zero()),
913            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
914            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
915            service_name: "antnode1".to_string(),
916            status: ServiceStatus::Stopped,
917            upnp: false,
918            user: Some("ant".to_string()),
919            user_mode: false,
920            version: "0.98.1".to_string(),
921        };
922        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
923
924        let mut service_manager = ServiceManager::new(
925            service,
926            Box::new(mock_service_control),
927            VerbosityLevel::Normal,
928        );
929
930        service_manager.start().await?;
931
932        assert_eq!(service_manager.service.service_data.pid, Some(1000));
933        assert_eq!(
934            service_manager.service.service_data.peer_id,
935            Some(PeerId::from_str(
936                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
937            )?)
938        );
939        assert_matches!(
940            service_manager.service.service_data.status,
941            ServiceStatus::Running
942        );
943
944        Ok(())
945    }
946
947    #[tokio::test]
948    async fn start_should_not_attempt_to_start_a_running_service() -> Result<()> {
949        let mut mock_service_control = MockServiceControl::new();
950        let mock_rpc_client = MockRpcClient::new();
951
952        mock_service_control
953            .expect_get_process_pid()
954            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
955            .times(1)
956            .returning(|_| Ok(100));
957
958        let mut service_data = NodeServiceData {
959            auto_restart: false,
960            connected_peers: None,
961            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
962            evm_network: EvmNetwork::Custom(CustomNetwork {
963                rpc_url_http: "http://localhost:8545".parse()?,
964                payment_token_address: RewardsAddress::from_str(
965                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
966                )?,
967                data_payments_address: RewardsAddress::from_str(
968                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
969                )?,
970            }),
971            home_network: false,
972            listen_addr: None,
973            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
974            log_format: None,
975            max_archived_log_files: None,
976            max_log_files: None,
977            metrics_port: None,
978            network_id: None,
979            node_ip: None,
980            node_port: None,
981            number: 1,
982            peer_id: Some(PeerId::from_str(
983                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
984            )?),
985            peers_args: InitialPeersConfig::default(),
986            pid: Some(1000),
987            rewards_address: RewardsAddress::from_str(
988                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
989            )?,
990            reward_balance: Some(AttoTokens::zero()),
991            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
992            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
993            service_name: "antnode1".to_string(),
994            status: ServiceStatus::Running,
995            upnp: false,
996            user: Some("ant".to_string()),
997            user_mode: false,
998            version: "0.98.1".to_string(),
999        };
1000        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1001
1002        let mut service_manager = ServiceManager::new(
1003            service,
1004            Box::new(mock_service_control),
1005            VerbosityLevel::Normal,
1006        );
1007
1008        service_manager.start().await?;
1009
1010        assert_eq!(service_manager.service.service_data.pid, Some(1000));
1011        assert_eq!(
1012            service_manager.service.service_data.peer_id,
1013            Some(PeerId::from_str(
1014                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
1015            )?)
1016        );
1017        assert_matches!(
1018            service_manager.service.service_data.status,
1019            ServiceStatus::Running
1020        );
1021
1022        Ok(())
1023    }
1024
1025    #[tokio::test]
1026    async fn start_should_start_a_service_marked_as_running_but_had_since_stopped() -> Result<()> {
1027        let mut mock_service_control = MockServiceControl::new();
1028        let mut mock_rpc_client = MockRpcClient::new();
1029
1030        mock_service_control
1031            .expect_get_process_pid()
1032            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
1033            .times(1)
1034            .returning(|_| {
1035                Err(ServiceError::ServiceProcessNotFound(
1036                    "Could not find process at '/var/antctl/services/antnode1/antnode'".to_string(),
1037                ))
1038            });
1039        mock_service_control
1040            .expect_start()
1041            .with(eq("antnode1"), eq(false))
1042            .times(1)
1043            .returning(|_, _| Ok(()));
1044        mock_service_control
1045            .expect_wait()
1046            .with(eq(3000))
1047            .times(1)
1048            .returning(|_| ());
1049        mock_service_control
1050            .expect_get_process_pid()
1051            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
1052            .times(1)
1053            .returning(|_| Ok(1000));
1054
1055        mock_rpc_client.expect_node_info().times(1).returning(|| {
1056            Ok(NodeInfo {
1057                pid: 1000,
1058                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1059                data_path: PathBuf::from("/var/antctl/services/antnode1"),
1060                log_path: PathBuf::from("/var/log/antnode/antnode1"),
1061                version: "0.98.1".to_string(),
1062                uptime: std::time::Duration::from_secs(1), // the service was just started
1063                wallet_balance: 0,
1064            })
1065        });
1066        mock_rpc_client
1067            .expect_network_info()
1068            .times(1)
1069            .returning(|| {
1070                Ok(NetworkInfo {
1071                    connected_peers: Vec::new(),
1072                    listeners: Vec::new(),
1073                })
1074            });
1075
1076        let mut service_data = NodeServiceData {
1077            auto_restart: false,
1078            connected_peers: None,
1079            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1080            evm_network: EvmNetwork::Custom(CustomNetwork {
1081                rpc_url_http: "http://localhost:8545".parse()?,
1082                payment_token_address: RewardsAddress::from_str(
1083                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1084                )?,
1085                data_payments_address: RewardsAddress::from_str(
1086                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1087                )?,
1088            }),
1089            home_network: false,
1090            listen_addr: None,
1091            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1092            log_format: None,
1093            max_archived_log_files: None,
1094            max_log_files: None,
1095            metrics_port: None,
1096            network_id: None,
1097            node_ip: None,
1098            node_port: None,
1099            number: 1,
1100            peer_id: Some(PeerId::from_str(
1101                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1102            )?),
1103            peers_args: InitialPeersConfig::default(),
1104            pid: Some(1000),
1105            rewards_address: RewardsAddress::from_str(
1106                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1107            )?,
1108            reward_balance: Some(AttoTokens::zero()),
1109            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1110            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1111            service_name: "antnode1".to_string(),
1112            status: ServiceStatus::Running,
1113            upnp: false,
1114            user: Some("ant".to_string()),
1115            user_mode: false,
1116            version: "0.98.1".to_string(),
1117        };
1118        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1119
1120        let mut service_manager = ServiceManager::new(
1121            service,
1122            Box::new(mock_service_control),
1123            VerbosityLevel::Normal,
1124        );
1125
1126        service_manager.start().await?;
1127
1128        assert_eq!(service_manager.service.service_data.pid, Some(1000));
1129        assert_eq!(
1130            service_manager.service.service_data.peer_id,
1131            Some(PeerId::from_str(
1132                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR"
1133            )?)
1134        );
1135        assert_matches!(
1136            service_manager.service.service_data.status,
1137            ServiceStatus::Running
1138        );
1139
1140        Ok(())
1141    }
1142
1143    #[tokio::test]
1144    async fn start_should_return_an_error_if_the_process_was_not_found() -> Result<()> {
1145        let mut mock_service_control = MockServiceControl::new();
1146
1147        mock_service_control
1148            .expect_start()
1149            .with(eq("antnode1"), eq(false))
1150            .times(1)
1151            .returning(|_, _| Ok(()));
1152        mock_service_control
1153            .expect_wait()
1154            .with(eq(3000))
1155            .times(1)
1156            .returning(|_| ());
1157        mock_service_control
1158            .expect_get_process_pid()
1159            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
1160            .times(1)
1161            .returning(|_| {
1162                Err(ServiceControlError::ServiceProcessNotFound(
1163                    "/var/antctl/services/antnode1/antnode".to_string(),
1164                ))
1165            });
1166
1167        let mut service_data = NodeServiceData {
1168            auto_restart: false,
1169            connected_peers: None,
1170            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1171            evm_network: EvmNetwork::Custom(CustomNetwork {
1172                rpc_url_http: "http://localhost:8545".parse()?,
1173                payment_token_address: RewardsAddress::from_str(
1174                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1175                )?,
1176                data_payments_address: RewardsAddress::from_str(
1177                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1178                )?,
1179            }),
1180            home_network: false,
1181            listen_addr: None,
1182            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1183            log_format: None,
1184            max_archived_log_files: None,
1185            max_log_files: None,
1186            metrics_port: None,
1187            network_id: None,
1188            node_ip: None,
1189            node_port: None,
1190            number: 1,
1191            peer_id: None,
1192            peers_args: InitialPeersConfig::default(),
1193            pid: None,
1194            rewards_address: RewardsAddress::from_str(
1195                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1196            )?,
1197            reward_balance: Some(AttoTokens::zero()),
1198            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1199            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1200            service_name: "antnode1".to_string(),
1201            status: ServiceStatus::Added,
1202            upnp: false,
1203            user: Some("ant".to_string()),
1204            user_mode: false,
1205            version: "0.98.1".to_string(),
1206        };
1207        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1208        let mut service_manager = ServiceManager::new(
1209            service,
1210            Box::new(mock_service_control),
1211            VerbosityLevel::Normal,
1212        );
1213
1214        let result = service_manager.start().await;
1215        match result {
1216            Ok(_) => panic!("This test should have resulted in an error"),
1217            Err(e) => assert_eq!(
1218                "The PID of the process was not found after starting it.",
1219                e.to_string()
1220            ),
1221        }
1222
1223        Ok(())
1224    }
1225
1226    #[tokio::test]
1227    async fn start_should_start_a_user_mode_service() -> Result<()> {
1228        let mut mock_service_control = MockServiceControl::new();
1229        let mut mock_rpc_client = MockRpcClient::new();
1230
1231        mock_service_control
1232            .expect_start()
1233            .with(eq("antnode1"), eq(true))
1234            .times(1)
1235            .returning(|_, _| Ok(()));
1236        mock_service_control
1237            .expect_wait()
1238            .with(eq(3000))
1239            .times(1)
1240            .returning(|_| ());
1241        mock_service_control
1242            .expect_get_process_pid()
1243            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
1244            .times(1)
1245            .returning(|_| Ok(100));
1246
1247        mock_rpc_client.expect_node_info().times(1).returning(|| {
1248            Ok(NodeInfo {
1249                pid: 1000,
1250                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1251                data_path: PathBuf::from("/var/antctl/services/antnode1"),
1252                log_path: PathBuf::from("/var/log/antnode/antnode1"),
1253                version: "0.98.1".to_string(),
1254                uptime: std::time::Duration::from_secs(1), // the service was just started
1255                wallet_balance: 0,
1256            })
1257        });
1258        mock_rpc_client
1259            .expect_network_info()
1260            .times(1)
1261            .returning(|| {
1262                Ok(NetworkInfo {
1263                    connected_peers: Vec::new(),
1264                    listeners: Vec::new(),
1265                })
1266            });
1267
1268        let mut service_data = NodeServiceData {
1269            auto_restart: false,
1270            connected_peers: None,
1271            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1272            evm_network: EvmNetwork::Custom(CustomNetwork {
1273                rpc_url_http: "http://localhost:8545".parse()?,
1274                payment_token_address: RewardsAddress::from_str(
1275                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1276                )?,
1277                data_payments_address: RewardsAddress::from_str(
1278                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1279                )?,
1280            }),
1281            home_network: false,
1282            listen_addr: None,
1283            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1284            log_format: None,
1285            max_archived_log_files: None,
1286            max_log_files: None,
1287            metrics_port: None,
1288            network_id: None,
1289            node_ip: None,
1290            node_port: None,
1291            number: 1,
1292            peer_id: None,
1293            peers_args: InitialPeersConfig::default(),
1294            pid: None,
1295            rewards_address: RewardsAddress::from_str(
1296                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1297            )?,
1298            reward_balance: Some(AttoTokens::zero()),
1299            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1300            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1301            service_name: "antnode1".to_string(),
1302            status: ServiceStatus::Added,
1303            upnp: false,
1304            user: Some("ant".to_string()),
1305            user_mode: true,
1306            version: "0.98.1".to_string(),
1307        };
1308        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1309
1310        let mut service_manager = ServiceManager::new(
1311            service,
1312            Box::new(mock_service_control),
1313            VerbosityLevel::Normal,
1314        );
1315
1316        service_manager.start().await?;
1317
1318        Ok(())
1319    }
1320
1321    #[tokio::test]
1322    async fn start_should_use_dynamic_startup_delay_if_set() -> Result<()> {
1323        let mut mock_service_control = MockServiceControl::new();
1324        let mut mock_rpc_client = MockRpcClient::new();
1325
1326        mock_service_control
1327            .expect_start()
1328            .with(eq("antnode1"), eq(false))
1329            .times(1)
1330            .returning(|_, _| Ok(()));
1331        mock_service_control
1332            .expect_wait()
1333            .with(eq(3000))
1334            .times(1)
1335            .returning(|_| ());
1336        mock_service_control
1337            .expect_get_process_pid()
1338            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
1339            .times(1)
1340            .returning(|_| Ok(1000));
1341        mock_rpc_client
1342            .expect_is_node_connected_to_network()
1343            .times(1)
1344            .returning(|_| Ok(()));
1345        mock_rpc_client.expect_node_info().times(1).returning(|| {
1346            Ok(NodeInfo {
1347                pid: 1000,
1348                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1349                data_path: PathBuf::from("/var/antctl/services/antnode1"),
1350                log_path: PathBuf::from("/var/log/antnode/antnode1"),
1351                version: "0.98.1".to_string(),
1352                uptime: std::time::Duration::from_secs(1), // the service was just started
1353                wallet_balance: 0,
1354            })
1355        });
1356        mock_rpc_client
1357            .expect_network_info()
1358            .times(1)
1359            .returning(|| {
1360                Ok(NetworkInfo {
1361                    connected_peers: vec![PeerId::from_str(
1362                        "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1363                    )?],
1364                    listeners: Vec::new(),
1365                })
1366            });
1367
1368        let mut service_data = NodeServiceData {
1369            auto_restart: false,
1370            connected_peers: None,
1371            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1372            evm_network: EvmNetwork::Custom(CustomNetwork {
1373                rpc_url_http: "http://localhost:8545".parse()?,
1374                payment_token_address: RewardsAddress::from_str(
1375                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1376                )?,
1377                data_payments_address: RewardsAddress::from_str(
1378                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1379                )?,
1380            }),
1381            home_network: false,
1382            listen_addr: None,
1383            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1384            log_format: None,
1385            max_archived_log_files: None,
1386            max_log_files: None,
1387            metrics_port: None,
1388            network_id: None,
1389            node_ip: None,
1390            node_port: None,
1391            number: 1,
1392            peer_id: None,
1393            peers_args: InitialPeersConfig::default(),
1394            pid: None,
1395            rewards_address: RewardsAddress::from_str(
1396                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1397            )?,
1398            reward_balance: Some(AttoTokens::zero()),
1399            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1400            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1401            service_name: "antnode1".to_string(),
1402            status: ServiceStatus::Added,
1403            upnp: false,
1404            user: Some("ant".to_string()),
1405            user_mode: false,
1406            version: "0.98.1".to_string(),
1407        };
1408        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client))
1409            .with_connection_timeout(Duration::from_secs(
1410                DEFAULT_NODE_STARTUP_CONNECTION_TIMEOUT_S,
1411            ));
1412        let mut service_manager = ServiceManager::new(
1413            service,
1414            Box::new(mock_service_control),
1415            VerbosityLevel::Normal,
1416        );
1417
1418        service_manager.start().await?;
1419
1420        Ok(())
1421    }
1422
1423    #[tokio::test]
1424    async fn stop_should_stop_a_running_service() -> Result<()> {
1425        let mut mock_service_control = MockServiceControl::new();
1426
1427        mock_service_control
1428            .expect_stop()
1429            .with(eq("antnode1"), eq(false))
1430            .times(1)
1431            .returning(|_, _| Ok(()));
1432        mock_service_control
1433            .expect_get_process_pid()
1434            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
1435            .times(1)
1436            .returning(|_| Ok(100));
1437
1438        let mut service_data = NodeServiceData {
1439            auto_restart: false,
1440            connected_peers: None,
1441            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1442            evm_network: EvmNetwork::Custom(CustomNetwork {
1443                rpc_url_http: "http://localhost:8545".parse()?,
1444                payment_token_address: RewardsAddress::from_str(
1445                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1446                )?,
1447                data_payments_address: RewardsAddress::from_str(
1448                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1449                )?,
1450            }),
1451            home_network: false,
1452            listen_addr: None,
1453            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1454            log_format: None,
1455            max_archived_log_files: None,
1456            max_log_files: None,
1457            metrics_port: None,
1458            network_id: None,
1459            node_ip: None,
1460            node_port: None,
1461            number: 1,
1462            peer_id: Some(PeerId::from_str(
1463                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1464            )?),
1465            peers_args: InitialPeersConfig::default(),
1466            pid: Some(1000),
1467            rewards_address: RewardsAddress::from_str(
1468                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1469            )?,
1470            reward_balance: Some(AttoTokens::zero()),
1471            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1472            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1473            service_name: "antnode1".to_string(),
1474            status: ServiceStatus::Running,
1475            upnp: false,
1476            user: Some("ant".to_string()),
1477            user_mode: false,
1478            version: "0.98.1".to_string(),
1479        };
1480        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1481        let mut service_manager = ServiceManager::new(
1482            service,
1483            Box::new(mock_service_control),
1484            VerbosityLevel::Normal,
1485        );
1486
1487        service_manager.stop().await?;
1488
1489        assert_eq!(service_manager.service.service_data.pid, None);
1490        assert_eq!(service_manager.service.service_data.connected_peers, None);
1491        assert_matches!(
1492            service_manager.service.service_data.status,
1493            ServiceStatus::Stopped
1494        );
1495        Ok(())
1496    }
1497
1498    #[tokio::test]
1499    async fn stop_should_not_return_error_for_attempt_to_stop_installed_service() -> Result<()> {
1500        let mut service_data = NodeServiceData {
1501            auto_restart: false,
1502            connected_peers: None,
1503            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1504            evm_network: EvmNetwork::Custom(CustomNetwork {
1505                rpc_url_http: "http://localhost:8545".parse()?,
1506                payment_token_address: RewardsAddress::from_str(
1507                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1508                )?,
1509                data_payments_address: RewardsAddress::from_str(
1510                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1511                )?,
1512            }),
1513            home_network: false,
1514            listen_addr: None,
1515            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1516            log_format: None,
1517            max_archived_log_files: None,
1518            max_log_files: None,
1519            metrics_port: None,
1520            network_id: None,
1521            node_ip: None,
1522            node_port: None,
1523            number: 1,
1524            peer_id: None,
1525            peers_args: InitialPeersConfig::default(),
1526            pid: None,
1527            rewards_address: RewardsAddress::from_str(
1528                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1529            )?,
1530            reward_balance: Some(AttoTokens::zero()),
1531            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1532            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1533            service_name: "antnode1".to_string(),
1534            status: ServiceStatus::Added,
1535            upnp: false,
1536            user: Some("ant".to_string()),
1537            user_mode: false,
1538            version: "0.98.1".to_string(),
1539        };
1540        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1541        let mut service_manager = ServiceManager::new(
1542            service,
1543            Box::new(MockServiceControl::new()),
1544            VerbosityLevel::Normal,
1545        );
1546
1547        let result = service_manager.stop().await;
1548
1549        match result {
1550            Ok(()) => Ok(()),
1551            Err(_) => {
1552                panic!("The stop command should be idempotent and do nothing for an added service");
1553            }
1554        }
1555    }
1556
1557    #[tokio::test]
1558    async fn stop_should_return_ok_when_attempting_to_stop_service_that_was_already_stopped(
1559    ) -> Result<()> {
1560        let mut service_data = NodeServiceData {
1561            auto_restart: false,
1562            connected_peers: None,
1563            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1564            evm_network: EvmNetwork::Custom(CustomNetwork {
1565                rpc_url_http: "http://localhost:8545".parse()?,
1566                payment_token_address: RewardsAddress::from_str(
1567                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1568                )?,
1569                data_payments_address: RewardsAddress::from_str(
1570                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1571                )?,
1572            }),
1573            home_network: false,
1574            listen_addr: None,
1575            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1576            log_format: None,
1577            max_archived_log_files: None,
1578            max_log_files: None,
1579            metrics_port: None,
1580            network_id: None,
1581            node_ip: None,
1582            node_port: None,
1583            number: 1,
1584            peer_id: Some(PeerId::from_str(
1585                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1586            )?),
1587            peers_args: InitialPeersConfig::default(),
1588            pid: None,
1589            rewards_address: RewardsAddress::from_str(
1590                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1591            )?,
1592            reward_balance: Some(AttoTokens::zero()),
1593            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1594            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1595            service_name: "antnode1".to_string(),
1596            status: ServiceStatus::Stopped,
1597            upnp: false,
1598            user: Some("ant".to_string()),
1599            user_mode: false,
1600            version: "0.98.1".to_string(),
1601        };
1602        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1603        let mut service_manager = ServiceManager::new(
1604            service,
1605            Box::new(MockServiceControl::new()),
1606            VerbosityLevel::Normal,
1607        );
1608
1609        let result = service_manager.stop().await;
1610
1611        match result {
1612            Ok(()) => Ok(()),
1613            Err(_) => {
1614                panic!(
1615                    "The stop command should be idempotent and do nothing for an stopped service"
1616                );
1617            }
1618        }
1619    }
1620
1621    #[tokio::test]
1622    async fn stop_should_return_ok_when_attempting_to_stop_a_removed_service() -> Result<()> {
1623        let mut service_data = NodeServiceData {
1624            auto_restart: false,
1625            connected_peers: None,
1626            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1627            evm_network: EvmNetwork::Custom(CustomNetwork {
1628                rpc_url_http: "http://localhost:8545".parse()?,
1629                payment_token_address: RewardsAddress::from_str(
1630                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1631                )?,
1632                data_payments_address: RewardsAddress::from_str(
1633                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1634                )?,
1635            }),
1636            home_network: false,
1637            listen_addr: None,
1638            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1639            log_format: None,
1640            max_archived_log_files: None,
1641            max_log_files: None,
1642            metrics_port: None,
1643            network_id: None,
1644            node_ip: None,
1645            node_port: None,
1646            number: 1,
1647            peer_id: None,
1648            peers_args: InitialPeersConfig::default(),
1649            pid: None,
1650            rewards_address: RewardsAddress::from_str(
1651                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1652            )?,
1653            reward_balance: Some(AttoTokens::zero()),
1654            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1655            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1656            service_name: "antnode1".to_string(),
1657            status: ServiceStatus::Removed,
1658            upnp: false,
1659            user: Some("ant".to_string()),
1660            user_mode: false,
1661            version: "0.98.1".to_string(),
1662        };
1663        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1664        let mut service_manager = ServiceManager::new(
1665            service,
1666            Box::new(MockServiceControl::new()),
1667            VerbosityLevel::Normal,
1668        );
1669
1670        let result = service_manager.stop().await;
1671
1672        match result {
1673            Ok(()) => Ok(()),
1674            Err(_) => {
1675                panic!(
1676                    "The stop command should be idempotent and do nothing for a removed service"
1677                );
1678            }
1679        }
1680    }
1681
1682    #[tokio::test]
1683    async fn stop_should_stop_a_user_mode_service() -> Result<()> {
1684        let mut mock_service_control = MockServiceControl::new();
1685
1686        mock_service_control
1687            .expect_stop()
1688            .with(eq("antnode1"), eq(true))
1689            .times(1)
1690            .returning(|_, _| Ok(()));
1691        mock_service_control
1692            .expect_get_process_pid()
1693            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
1694            .times(1)
1695            .returning(|_| Ok(100));
1696
1697        let mut service_data = NodeServiceData {
1698            auto_restart: false,
1699            connected_peers: None,
1700            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1701            evm_network: EvmNetwork::Custom(CustomNetwork {
1702                rpc_url_http: "http://localhost:8545".parse()?,
1703                payment_token_address: RewardsAddress::from_str(
1704                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1705                )?,
1706                data_payments_address: RewardsAddress::from_str(
1707                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1708                )?,
1709            }),
1710            home_network: false,
1711            listen_addr: None,
1712            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1713            log_format: None,
1714            max_archived_log_files: None,
1715            max_log_files: None,
1716            metrics_port: None,
1717            network_id: None,
1718            node_ip: None,
1719            node_port: None,
1720            number: 1,
1721            peer_id: Some(PeerId::from_str(
1722                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1723            )?),
1724            peers_args: InitialPeersConfig::default(),
1725            pid: Some(1000),
1726            rewards_address: RewardsAddress::from_str(
1727                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1728            )?,
1729            reward_balance: Some(AttoTokens::zero()),
1730            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1731            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
1732            service_name: "antnode1".to_string(),
1733            status: ServiceStatus::Running,
1734            upnp: false,
1735            user: None,
1736            user_mode: true,
1737            version: "0.98.1".to_string(),
1738        };
1739        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
1740        let mut service_manager = ServiceManager::new(
1741            service,
1742            Box::new(mock_service_control),
1743            VerbosityLevel::Normal,
1744        );
1745
1746        service_manager.stop().await?;
1747
1748        assert_eq!(service_manager.service.service_data.pid, None);
1749        assert_eq!(service_manager.service.service_data.connected_peers, None);
1750        assert_matches!(
1751            service_manager.service.service_data.status,
1752            ServiceStatus::Stopped
1753        );
1754        Ok(())
1755    }
1756
1757    #[tokio::test]
1758    async fn upgrade_should_upgrade_a_service_to_a_new_version() -> Result<()> {
1759        let current_version = "0.1.0";
1760        let target_version = "0.2.0";
1761
1762        let tmp_data_dir = assert_fs::TempDir::new()?;
1763        let current_install_dir = tmp_data_dir.child("antnode_install");
1764        current_install_dir.create_dir_all()?;
1765
1766        let current_node_bin = current_install_dir.child("antnode");
1767        current_node_bin.write_binary(b"fake antnode binary")?;
1768        let target_node_bin = tmp_data_dir.child("antnode");
1769        target_node_bin.write_binary(b"fake antnode binary")?;
1770
1771        let mut mock_service_control = MockServiceControl::new();
1772        let mut mock_rpc_client = MockRpcClient::new();
1773
1774        // before binary upgrade
1775        mock_service_control
1776            .expect_get_process_pid()
1777            .with(eq(current_node_bin.to_path_buf().clone()))
1778            .times(1)
1779            .returning(|_| Ok(1000));
1780        mock_service_control
1781            .expect_stop()
1782            .with(eq("antnode1"), eq(false))
1783            .times(1)
1784            .returning(|_, _| Ok(()));
1785
1786        // after binary upgrade
1787        mock_service_control
1788            .expect_uninstall()
1789            .with(eq("antnode1"), eq(false))
1790            .times(1)
1791            .returning(|_, _| Ok(()));
1792        mock_service_control
1793            .expect_install()
1794            .with(always(), always())
1795            .times(1)
1796            .returning(|_, _| Ok(()));
1797
1798        // after service restart
1799        mock_service_control
1800            .expect_start()
1801            .with(eq("antnode1"), eq(false))
1802            .times(1)
1803            .returning(|_, _| Ok(()));
1804        mock_service_control
1805            .expect_wait()
1806            .with(eq(3000))
1807            .times(1)
1808            .returning(|_| ());
1809        mock_service_control
1810            .expect_get_process_pid()
1811            .with(eq(current_node_bin.to_path_buf().clone()))
1812            .times(1)
1813            .returning(|_| Ok(2000));
1814
1815        mock_rpc_client.expect_node_info().times(1).returning(|| {
1816            Ok(NodeInfo {
1817                pid: 2000,
1818                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
1819                data_path: PathBuf::from("/var/antctl/services/antnode1"),
1820                log_path: PathBuf::from("/var/log/antnode/antnode1"),
1821                version: target_version.to_string(),
1822                uptime: std::time::Duration::from_secs(1), // the service was just started
1823                wallet_balance: 0,
1824            })
1825        });
1826        mock_rpc_client
1827            .expect_network_info()
1828            .times(1)
1829            .returning(|| {
1830                Ok(NetworkInfo {
1831                    connected_peers: Vec::new(),
1832                    listeners: Vec::new(),
1833                })
1834            });
1835
1836        let mut service_data = NodeServiceData {
1837            auto_restart: false,
1838            connected_peers: None,
1839            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1840            evm_network: EvmNetwork::Custom(CustomNetwork {
1841                rpc_url_http: "http://localhost:8545".parse()?,
1842                payment_token_address: RewardsAddress::from_str(
1843                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1844                )?,
1845                data_payments_address: RewardsAddress::from_str(
1846                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1847                )?,
1848            }),
1849            home_network: false,
1850            listen_addr: None,
1851            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1852            log_format: None,
1853            max_archived_log_files: None,
1854            max_log_files: None,
1855            metrics_port: None,
1856            network_id: None,
1857            node_ip: None,
1858            node_port: None,
1859            number: 1,
1860            peer_id: Some(PeerId::from_str(
1861                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1862            )?),
1863            peers_args: InitialPeersConfig::default(),
1864            pid: Some(1000),
1865            rewards_address: RewardsAddress::from_str(
1866                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1867            )?,
1868            reward_balance: Some(AttoTokens::zero()),
1869            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1870            antnode_path: current_node_bin.to_path_buf(),
1871            service_name: "antnode1".to_string(),
1872            status: ServiceStatus::Running,
1873            upnp: false,
1874            user: Some("ant".to_string()),
1875            user_mode: false,
1876            version: current_version.to_string(),
1877        };
1878        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1879        let mut service_manager = ServiceManager::new(
1880            service,
1881            Box::new(mock_service_control),
1882            VerbosityLevel::Normal,
1883        );
1884
1885        let upgrade_result = service_manager
1886            .upgrade(UpgradeOptions {
1887                auto_restart: false,
1888                env_variables: None,
1889                force: false,
1890                start_service: true,
1891                target_bin_path: target_node_bin.to_path_buf(),
1892                target_version: Version::parse(target_version).unwrap(),
1893            })
1894            .await?;
1895
1896        match upgrade_result {
1897            UpgradeResult::Upgraded(old_version, new_version) => {
1898                assert_eq!(old_version, current_version);
1899                assert_eq!(new_version, target_version);
1900            }
1901            _ => panic!(
1902                "Expected UpgradeResult::Upgraded but was {:#?}",
1903                upgrade_result
1904            ),
1905        }
1906
1907        assert_eq!(service_manager.service.service_data.pid, Some(2000));
1908        assert_eq!(
1909            service_manager.service.service_data.peer_id,
1910            Some(PeerId::from_str(
1911                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1912            )?)
1913        );
1914        assert_eq!(service_manager.service.service_data.version, target_version);
1915
1916        Ok(())
1917    }
1918
1919    #[tokio::test]
1920    async fn upgrade_should_not_be_required_if_target_is_less_than_current_version() -> Result<()> {
1921        let current_version = "0.2.0";
1922        let target_version = "0.1.0";
1923
1924        let tmp_data_dir = assert_fs::TempDir::new()?;
1925        let current_install_dir = tmp_data_dir.child("antnode_install");
1926        current_install_dir.create_dir_all()?;
1927
1928        let current_node_bin = current_install_dir.child("antnode");
1929        current_node_bin.write_binary(b"fake antnode binary")?;
1930        let target_node_bin = tmp_data_dir.child("antnode");
1931        target_node_bin.write_binary(b"fake antnode binary")?;
1932
1933        let mock_service_control = MockServiceControl::new();
1934        let mock_rpc_client = MockRpcClient::new();
1935
1936        let mut service_data = NodeServiceData {
1937            auto_restart: false,
1938            connected_peers: None,
1939            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
1940            evm_network: EvmNetwork::Custom(CustomNetwork {
1941                rpc_url_http: "http://localhost:8545".parse()?,
1942                payment_token_address: RewardsAddress::from_str(
1943                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
1944                )?,
1945                data_payments_address: RewardsAddress::from_str(
1946                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
1947                )?,
1948            }),
1949            home_network: false,
1950            listen_addr: None,
1951            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
1952            log_format: None,
1953            max_archived_log_files: None,
1954            max_log_files: None,
1955            metrics_port: None,
1956            network_id: None,
1957            node_ip: None,
1958            node_port: None,
1959            number: 1,
1960            peer_id: Some(PeerId::from_str(
1961                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
1962            )?),
1963            peers_args: InitialPeersConfig::default(),
1964            pid: Some(1000),
1965            rewards_address: RewardsAddress::from_str(
1966                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
1967            )?,
1968            reward_balance: Some(AttoTokens::zero()),
1969            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
1970            antnode_path: current_node_bin.to_path_buf(),
1971            service_name: "antnode1".to_string(),
1972            status: ServiceStatus::Running,
1973            upnp: false,
1974            user: Some("ant".to_string()),
1975            user_mode: false,
1976            version: current_version.to_string(),
1977        };
1978        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
1979
1980        let mut service_manager = ServiceManager::new(
1981            service,
1982            Box::new(mock_service_control),
1983            VerbosityLevel::Normal,
1984        );
1985
1986        let upgrade_result = service_manager
1987            .upgrade(UpgradeOptions {
1988                auto_restart: false,
1989                env_variables: None,
1990                force: false,
1991                start_service: true,
1992                target_bin_path: target_node_bin.to_path_buf(),
1993                target_version: Version::parse(target_version).unwrap(),
1994            })
1995            .await?;
1996
1997        assert_matches!(upgrade_result, UpgradeResult::NotRequired);
1998
1999        Ok(())
2000    }
2001
2002    #[tokio::test]
2003    async fn upgrade_should_downgrade_to_a_previous_version_if_force_is_used() -> Result<()> {
2004        let current_version = "0.1.0";
2005        let target_version = "0.2.0";
2006
2007        let tmp_data_dir = assert_fs::TempDir::new()?;
2008        let current_install_dir = tmp_data_dir.child("antnode_install");
2009        current_install_dir.create_dir_all()?;
2010
2011        let current_node_bin = current_install_dir.child("antnode");
2012        current_node_bin.write_binary(b"fake antnode binary")?;
2013        let target_node_bin = tmp_data_dir.child("antnode");
2014        target_node_bin.write_binary(b"fake antnode binary")?;
2015
2016        let mut mock_service_control = MockServiceControl::new();
2017        let mut mock_rpc_client = MockRpcClient::new();
2018
2019        // before binary upgrade
2020        mock_service_control
2021            .expect_get_process_pid()
2022            .with(eq(current_node_bin.to_path_buf().clone()))
2023            .times(1)
2024            .returning(|_| Ok(1000));
2025        mock_service_control
2026            .expect_stop()
2027            .with(eq("antnode1"), eq(false))
2028            .times(1)
2029            .returning(|_, _| Ok(()));
2030
2031        // after binary upgrade
2032        mock_service_control
2033            .expect_uninstall()
2034            .with(eq("antnode1"), eq(false))
2035            .times(1)
2036            .returning(|_, _| Ok(()));
2037        mock_service_control
2038            .expect_install()
2039            .with(always(), always())
2040            .times(1)
2041            .returning(|_, _| Ok(()));
2042
2043        // after service restart
2044        mock_service_control
2045            .expect_start()
2046            .with(eq("antnode1"), eq(false))
2047            .times(1)
2048            .returning(|_, _| Ok(()));
2049        mock_service_control
2050            .expect_wait()
2051            .with(eq(3000))
2052            .times(1)
2053            .returning(|_| ());
2054        mock_service_control
2055            .expect_get_process_pid()
2056            .with(eq(current_node_bin.to_path_buf().clone()))
2057            .times(1)
2058            .returning(|_| Ok(2000));
2059
2060        mock_rpc_client.expect_node_info().times(1).returning(|| {
2061            Ok(NodeInfo {
2062                pid: 2000,
2063                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2064                data_path: PathBuf::from("/var/antctl/services/antnode1"),
2065                log_path: PathBuf::from("/var/log/antnode/antnode1"),
2066                version: target_version.to_string(),
2067                uptime: std::time::Duration::from_secs(1), // the service was just started
2068                wallet_balance: 0,
2069            })
2070        });
2071        mock_rpc_client
2072            .expect_network_info()
2073            .times(1)
2074            .returning(|| {
2075                Ok(NetworkInfo {
2076                    connected_peers: Vec::new(),
2077                    listeners: Vec::new(),
2078                })
2079            });
2080
2081        let mut service_data = NodeServiceData {
2082            auto_restart: false,
2083            connected_peers: None,
2084            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
2085            evm_network: EvmNetwork::Custom(CustomNetwork {
2086                rpc_url_http: "http://localhost:8545".parse()?,
2087                payment_token_address: RewardsAddress::from_str(
2088                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2089                )?,
2090                data_payments_address: RewardsAddress::from_str(
2091                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2092                )?,
2093            }),
2094            home_network: false,
2095            listen_addr: None,
2096            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
2097            log_format: None,
2098            max_archived_log_files: None,
2099            max_log_files: None,
2100            metrics_port: None,
2101            network_id: None,
2102            node_ip: None,
2103            node_port: None,
2104            number: 1,
2105            peer_id: Some(PeerId::from_str(
2106                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2107            )?),
2108            peers_args: InitialPeersConfig::default(),
2109            pid: Some(1000),
2110            rewards_address: RewardsAddress::from_str(
2111                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2112            )?,
2113            reward_balance: Some(AttoTokens::zero()),
2114            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2115            antnode_path: current_node_bin.to_path_buf(),
2116            service_name: "antnode1".to_string(),
2117            status: ServiceStatus::Running,
2118            upnp: false,
2119            user: Some("ant".to_string()),
2120            user_mode: false,
2121            version: current_version.to_string(),
2122        };
2123        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2124
2125        let mut service_manager = ServiceManager::new(
2126            service,
2127            Box::new(mock_service_control),
2128            VerbosityLevel::Normal,
2129        );
2130
2131        let upgrade_result = service_manager
2132            .upgrade(UpgradeOptions {
2133                auto_restart: false,
2134                env_variables: None,
2135                force: true,
2136                start_service: true,
2137                target_bin_path: target_node_bin.to_path_buf(),
2138                target_version: Version::parse(target_version).unwrap(),
2139            })
2140            .await?;
2141
2142        match upgrade_result {
2143            UpgradeResult::Forced(old_version, new_version) => {
2144                assert_eq!(old_version, current_version);
2145                assert_eq!(new_version, target_version);
2146            }
2147            _ => panic!(
2148                "Expected UpgradeResult::Forced but was {:#?}",
2149                upgrade_result
2150            ),
2151        }
2152
2153        assert_eq!(service_manager.service.service_data.pid, Some(2000));
2154        assert_eq!(
2155            service_manager.service.service_data.peer_id,
2156            Some(PeerId::from_str(
2157                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2158            )?)
2159        );
2160        assert_eq!(service_manager.service.service_data.version, target_version);
2161
2162        Ok(())
2163    }
2164
2165    #[tokio::test]
2166    async fn upgrade_should_upgrade_and_not_start_the_service() -> Result<()> {
2167        let current_version = "0.1.0";
2168        let target_version = "0.2.0";
2169
2170        let tmp_data_dir = assert_fs::TempDir::new()?;
2171        let current_install_dir = tmp_data_dir.child("antnode_install");
2172        current_install_dir.create_dir_all()?;
2173
2174        let current_node_bin = current_install_dir.child("antnode");
2175        current_node_bin.write_binary(b"fake antnode binary")?;
2176        let target_node_bin = tmp_data_dir.child("antnode");
2177        target_node_bin.write_binary(b"fake antnode binary")?;
2178
2179        let mut mock_service_control = MockServiceControl::new();
2180        let mut mock_rpc_client = MockRpcClient::new();
2181
2182        // before binary upgrade
2183        mock_service_control
2184            .expect_get_process_pid()
2185            .with(eq(current_node_bin.to_path_buf().clone()))
2186            .times(1)
2187            .returning(|_| Ok(1000));
2188        mock_service_control
2189            .expect_stop()
2190            .with(eq("antnode1"), eq(false))
2191            .times(1)
2192            .returning(|_, _| Ok(()));
2193
2194        // after binary upgrade
2195        mock_service_control
2196            .expect_uninstall()
2197            .with(eq("antnode1"), eq(false))
2198            .times(1)
2199            .returning(|_, _| Ok(()));
2200        mock_service_control
2201            .expect_install()
2202            .with(always(), always())
2203            .times(1)
2204            .returning(|_, _| Ok(()));
2205
2206        // after service restart
2207        mock_service_control
2208            .expect_start()
2209            .with(eq("antnode1"), eq(false))
2210            .times(0)
2211            .returning(|_, _| Ok(()));
2212        mock_service_control
2213            .expect_wait()
2214            .with(eq(3000))
2215            .times(0)
2216            .returning(|_| ());
2217        mock_rpc_client.expect_node_info().times(0).returning(|| {
2218            Ok(NodeInfo {
2219                pid: 2000,
2220                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2221                data_path: PathBuf::from("/var/antctl/services/antnode1"),
2222                log_path: PathBuf::from("/var/log/antnode/antnode1"),
2223                version: target_version.to_string(),
2224                uptime: std::time::Duration::from_secs(1), // the service was just started
2225                wallet_balance: 0,
2226            })
2227        });
2228        mock_rpc_client
2229            .expect_network_info()
2230            .times(0)
2231            .returning(|| {
2232                Ok(NetworkInfo {
2233                    connected_peers: Vec::new(),
2234                    listeners: Vec::new(),
2235                })
2236            });
2237
2238        let mut service_data = NodeServiceData {
2239            auto_restart: false,
2240            connected_peers: None,
2241            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
2242            evm_network: EvmNetwork::Custom(CustomNetwork {
2243                rpc_url_http: "http://localhost:8545".parse()?,
2244                payment_token_address: RewardsAddress::from_str(
2245                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2246                )?,
2247                data_payments_address: RewardsAddress::from_str(
2248                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2249                )?,
2250            }),
2251            home_network: false,
2252            listen_addr: None,
2253            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
2254            log_format: None,
2255            max_archived_log_files: None,
2256            max_log_files: None,
2257            metrics_port: None,
2258            network_id: None,
2259            node_ip: None,
2260            node_port: None,
2261            number: 1,
2262            peer_id: Some(PeerId::from_str(
2263                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2264            )?),
2265            peers_args: InitialPeersConfig::default(),
2266            pid: Some(1000),
2267            rewards_address: RewardsAddress::from_str(
2268                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2269            )?,
2270            reward_balance: Some(AttoTokens::zero()),
2271            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2272            antnode_path: current_node_bin.to_path_buf(),
2273            service_name: "antnode1".to_string(),
2274            status: ServiceStatus::Running,
2275            upnp: false,
2276            user: Some("ant".to_string()),
2277            user_mode: false,
2278            version: current_version.to_string(),
2279        };
2280        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2281
2282        let mut service_manager = ServiceManager::new(
2283            service,
2284            Box::new(mock_service_control),
2285            VerbosityLevel::Normal,
2286        );
2287
2288        let upgrade_result = service_manager
2289            .upgrade(UpgradeOptions {
2290                auto_restart: false,
2291                env_variables: None,
2292                force: false,
2293                start_service: false,
2294                target_bin_path: target_node_bin.to_path_buf(),
2295                target_version: Version::parse(target_version).unwrap(),
2296            })
2297            .await?;
2298
2299        match upgrade_result {
2300            UpgradeResult::Upgraded(old_version, new_version) => {
2301                assert_eq!(old_version, current_version);
2302                assert_eq!(new_version, target_version);
2303            }
2304            _ => panic!(
2305                "Expected UpgradeResult::Upgraded but was {:#?}",
2306                upgrade_result
2307            ),
2308        }
2309
2310        assert_eq!(service_manager.service.service_data.pid, None);
2311        assert_eq!(
2312            service_manager.service.service_data.peer_id,
2313            Some(PeerId::from_str(
2314                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2315            )?)
2316        );
2317        assert_eq!(service_manager.service.service_data.version, target_version);
2318        assert_matches!(
2319            service_manager.service.service_data.status,
2320            ServiceStatus::Stopped
2321        );
2322
2323        Ok(())
2324    }
2325
2326    #[tokio::test]
2327    async fn upgrade_should_return_upgraded_but_not_started_if_service_did_not_start() -> Result<()>
2328    {
2329        let current_version = "0.1.0";
2330        let target_version = "0.2.0";
2331
2332        let tmp_data_dir = assert_fs::TempDir::new()?;
2333        let current_install_dir = tmp_data_dir.child("antnode_install");
2334        current_install_dir.create_dir_all()?;
2335
2336        let current_node_bin = current_install_dir.child("antnode");
2337        current_node_bin.write_binary(b"fake antnode binary")?;
2338        let target_node_bin = tmp_data_dir.child("antnode");
2339        target_node_bin.write_binary(b"fake antnode binary")?;
2340
2341        let current_node_bin_str = current_node_bin.to_path_buf().to_string_lossy().to_string();
2342
2343        let mut mock_service_control = MockServiceControl::new();
2344
2345        // before binary upgrade
2346        mock_service_control
2347            .expect_get_process_pid()
2348            .with(eq(current_node_bin.to_path_buf().clone()))
2349            .times(1)
2350            .returning(|_| Ok(1000));
2351        mock_service_control
2352            .expect_stop()
2353            .with(eq("antnode1"), eq(false))
2354            .times(1)
2355            .returning(|_, _| Ok(()));
2356
2357        // after binary upgrade
2358        mock_service_control
2359            .expect_uninstall()
2360            .with(eq("antnode1"), eq(false))
2361            .times(1)
2362            .returning(|_, _| Ok(()));
2363        mock_service_control
2364            .expect_install()
2365            .with(always(), always())
2366            .times(1)
2367            .returning(|_, _| Ok(()));
2368
2369        // after service restart
2370        mock_service_control
2371            .expect_start()
2372            .with(eq("antnode1"), eq(false))
2373            .times(1)
2374            .returning(|_, _| Ok(()));
2375        mock_service_control
2376            .expect_wait()
2377            .with(eq(3000))
2378            .times(1)
2379            .returning(|_| ());
2380        mock_service_control
2381            .expect_get_process_pid()
2382            .with(eq(current_node_bin.to_path_buf().clone()))
2383            .times(1)
2384            .returning(move |_| {
2385                Err(ServiceControlError::ServiceProcessNotFound(
2386                    current_node_bin_str.clone(),
2387                ))
2388            });
2389
2390        let mut service_data = NodeServiceData {
2391            auto_restart: false,
2392            connected_peers: None,
2393            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
2394            evm_network: EvmNetwork::Custom(CustomNetwork {
2395                rpc_url_http: "http://localhost:8545".parse()?,
2396                payment_token_address: RewardsAddress::from_str(
2397                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2398                )?,
2399                data_payments_address: RewardsAddress::from_str(
2400                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2401                )?,
2402            }),
2403            home_network: false,
2404            listen_addr: None,
2405            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
2406            log_format: None,
2407            max_archived_log_files: None,
2408            max_log_files: None,
2409            metrics_port: None,
2410            network_id: None,
2411            node_ip: None,
2412            node_port: None,
2413            number: 1,
2414            peer_id: Some(PeerId::from_str(
2415                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2416            )?),
2417            peers_args: InitialPeersConfig::default(),
2418            pid: Some(1000),
2419            rewards_address: RewardsAddress::from_str(
2420                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2421            )?,
2422            reward_balance: Some(AttoTokens::zero()),
2423            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2424            antnode_path: current_node_bin.to_path_buf(),
2425            service_name: "antnode1".to_string(),
2426            status: ServiceStatus::Running,
2427            upnp: false,
2428            user: Some("ant".to_string()),
2429            user_mode: false,
2430            version: current_version.to_string(),
2431        };
2432        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
2433        let mut service_manager = ServiceManager::new(
2434            service,
2435            Box::new(mock_service_control),
2436            VerbosityLevel::Normal,
2437        );
2438
2439        let upgrade_result = service_manager
2440            .upgrade(UpgradeOptions {
2441                auto_restart: false,
2442                env_variables: None,
2443                force: false,
2444                start_service: true,
2445                target_bin_path: target_node_bin.to_path_buf(),
2446                target_version: Version::parse(target_version).unwrap(),
2447            })
2448            .await?;
2449
2450        match upgrade_result {
2451            UpgradeResult::UpgradedButNotStarted(old_version, new_version, _) => {
2452                assert_eq!(old_version, current_version);
2453                assert_eq!(new_version, target_version);
2454            }
2455            _ => panic!(
2456                "Expected UpgradeResult::UpgradedButNotStarted but was {:#?}",
2457                upgrade_result
2458            ),
2459        }
2460
2461        Ok(())
2462    }
2463
2464    #[tokio::test]
2465    async fn upgrade_should_upgrade_a_service_in_user_mode() -> Result<()> {
2466        let current_version = "0.1.0";
2467        let target_version = "0.2.0";
2468
2469        let tmp_data_dir = assert_fs::TempDir::new()?;
2470        let current_install_dir = tmp_data_dir.child("antnode_install");
2471        current_install_dir.create_dir_all()?;
2472
2473        let current_node_bin = current_install_dir.child("antnode");
2474        current_node_bin.write_binary(b"fake antnode binary")?;
2475        let target_node_bin = tmp_data_dir.child("antnode");
2476        target_node_bin.write_binary(b"fake antnode binary")?;
2477
2478        let mut mock_service_control = MockServiceControl::new();
2479        let mut mock_rpc_client = MockRpcClient::new();
2480
2481        // before binary upgrade
2482        mock_service_control
2483            .expect_get_process_pid()
2484            .with(eq(current_node_bin.to_path_buf().clone()))
2485            .times(1)
2486            .returning(|_| Ok(1000));
2487        mock_service_control
2488            .expect_stop()
2489            .with(eq("antnode1"), eq(true))
2490            .times(1)
2491            .returning(|_, _| Ok(()));
2492
2493        // after binary upgrade
2494        mock_service_control
2495            .expect_uninstall()
2496            .with(eq("antnode1"), eq(true))
2497            .times(1)
2498            .returning(|_, _| Ok(()));
2499        mock_service_control
2500            .expect_install()
2501            .with(always(), eq(true))
2502            .times(1)
2503            .returning(|_, _| Ok(()));
2504
2505        // after service restart
2506        mock_service_control
2507            .expect_start()
2508            .with(eq("antnode1"), eq(true))
2509            .times(1)
2510            .returning(|_, _| Ok(()));
2511        mock_service_control
2512            .expect_wait()
2513            .with(eq(3000))
2514            .times(1)
2515            .returning(|_| ());
2516        mock_service_control
2517            .expect_get_process_pid()
2518            .with(eq(current_node_bin.to_path_buf().clone()))
2519            .times(1)
2520            .returning(|_| Ok(2000));
2521
2522        mock_rpc_client.expect_node_info().times(1).returning(|| {
2523            Ok(NodeInfo {
2524                pid: 2000,
2525                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2526                data_path: PathBuf::from("/var/antctl/services/antnode1"),
2527                log_path: PathBuf::from("/var/log/antnode/antnode1"),
2528                version: target_version.to_string(),
2529                uptime: std::time::Duration::from_secs(1), // the service was just started
2530                wallet_balance: 0,
2531            })
2532        });
2533        mock_rpc_client
2534            .expect_network_info()
2535            .times(1)
2536            .returning(|| {
2537                Ok(NetworkInfo {
2538                    connected_peers: Vec::new(),
2539                    listeners: Vec::new(),
2540                })
2541            });
2542
2543        let mut service_data = NodeServiceData {
2544            auto_restart: false,
2545            connected_peers: None,
2546            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
2547            evm_network: EvmNetwork::Custom(CustomNetwork {
2548                rpc_url_http: "http://localhost:8545".parse()?,
2549                payment_token_address: RewardsAddress::from_str(
2550                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
2551                )?,
2552                data_payments_address: RewardsAddress::from_str(
2553                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
2554                )?,
2555            }),
2556            home_network: false,
2557            listen_addr: None,
2558            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
2559            log_format: None,
2560            max_archived_log_files: None,
2561            max_log_files: None,
2562            metrics_port: None,
2563            network_id: None,
2564            node_ip: None,
2565            node_port: None,
2566            number: 1,
2567            peer_id: Some(PeerId::from_str(
2568                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2569            )?),
2570            peers_args: InitialPeersConfig::default(),
2571            pid: Some(1000),
2572            rewards_address: RewardsAddress::from_str(
2573                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2574            )?,
2575            reward_balance: Some(AttoTokens::zero()),
2576            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2577            antnode_path: current_node_bin.to_path_buf(),
2578            service_name: "antnode1".to_string(),
2579            status: ServiceStatus::Running,
2580            upnp: false,
2581            user: None,
2582            user_mode: true,
2583            version: current_version.to_string(),
2584        };
2585        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2586
2587        let mut service_manager = ServiceManager::new(
2588            service,
2589            Box::new(mock_service_control),
2590            VerbosityLevel::Normal,
2591        );
2592
2593        let upgrade_result = service_manager
2594            .upgrade(UpgradeOptions {
2595                auto_restart: false,
2596                env_variables: None,
2597                force: false,
2598                start_service: true,
2599                target_bin_path: target_node_bin.to_path_buf(),
2600                target_version: Version::parse(target_version).unwrap(),
2601            })
2602            .await?;
2603
2604        match upgrade_result {
2605            UpgradeResult::Upgraded(old_version, new_version) => {
2606                assert_eq!(old_version, current_version);
2607                assert_eq!(new_version, target_version);
2608            }
2609            _ => panic!(
2610                "Expected UpgradeResult::Upgraded but was {:#?}",
2611                upgrade_result
2612            ),
2613        }
2614
2615        assert_eq!(service_manager.service.service_data.pid, Some(2000));
2616        assert_eq!(
2617            service_manager.service.service_data.peer_id,
2618            Some(PeerId::from_str(
2619                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2620            )?)
2621        );
2622        assert_eq!(service_manager.service.service_data.version, target_version);
2623
2624        Ok(())
2625    }
2626
2627    #[tokio::test]
2628    async fn upgrade_should_retain_the_first_flag() -> Result<()> {
2629        let current_version = "0.1.0";
2630        let target_version = "0.2.0";
2631
2632        let tmp_data_dir = assert_fs::TempDir::new()?;
2633        let current_install_dir = tmp_data_dir.child("antnode_install");
2634        current_install_dir.create_dir_all()?;
2635
2636        let current_node_bin = current_install_dir.child("antnode");
2637        current_node_bin.write_binary(b"fake antnode binary")?;
2638        let target_node_bin = tmp_data_dir.child("antnode");
2639        target_node_bin.write_binary(b"fake antnode binary")?;
2640
2641        let mut mock_service_control = MockServiceControl::new();
2642        let mut mock_rpc_client = MockRpcClient::new();
2643
2644        // before binary upgrade
2645        mock_service_control
2646            .expect_get_process_pid()
2647            .with(eq(current_node_bin.to_path_buf().clone()))
2648            .times(1)
2649            .returning(|_| Ok(1000));
2650        mock_service_control
2651            .expect_stop()
2652            .with(eq("antnode1"), eq(false))
2653            .times(1)
2654            .returning(|_, _| Ok(()));
2655
2656        // after binary upgrade
2657        mock_service_control
2658            .expect_uninstall()
2659            .with(eq("antnode1"), eq(false))
2660            .times(1)
2661            .returning(|_, _| Ok(()));
2662        mock_service_control
2663            .expect_install()
2664            .with(
2665                eq(ServiceInstallCtx {
2666                    args: vec![
2667                        OsString::from("--rpc"),
2668                        OsString::from("127.0.0.1:8081"),
2669                        OsString::from("--root-dir"),
2670                        OsString::from("/var/antctl/services/antnode1"),
2671                        OsString::from("--log-output-dest"),
2672                        OsString::from("/var/log/antnode/antnode1"),
2673                        OsString::from("--first"),
2674                        OsString::from("--rewards-address"),
2675                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
2676                        OsString::from("evm-arbitrum-one"),
2677                    ],
2678                    autostart: false,
2679                    contents: None,
2680                    environment: None,
2681                    label: "antnode1".parse()?,
2682                    program: current_node_bin.to_path_buf(),
2683                    username: Some("ant".to_string()),
2684                    working_directory: None,
2685                    disable_restart_on_failure: true,
2686                }),
2687                eq(false),
2688            )
2689            .times(1)
2690            .returning(|_, _| Ok(()));
2691
2692        // after service restart
2693        mock_service_control
2694            .expect_start()
2695            .with(eq("antnode1"), eq(false))
2696            .times(1)
2697            .returning(|_, _| Ok(()));
2698        mock_service_control
2699            .expect_wait()
2700            .with(eq(3000))
2701            .times(1)
2702            .returning(|_| ());
2703        mock_service_control
2704            .expect_get_process_pid()
2705            .with(eq(current_node_bin.to_path_buf().clone()))
2706            .times(1)
2707            .returning(|_| Ok(100));
2708
2709        mock_rpc_client.expect_node_info().times(1).returning(|| {
2710            Ok(NodeInfo {
2711                pid: 2000,
2712                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2713                data_path: PathBuf::from("/var/antctl/services/antnode1"),
2714                log_path: PathBuf::from("/var/log/antnode/antnode1"),
2715                version: target_version.to_string(),
2716                uptime: std::time::Duration::from_secs(1), // the service was just started
2717                wallet_balance: 0,
2718            })
2719        });
2720        mock_rpc_client
2721            .expect_network_info()
2722            .times(1)
2723            .returning(|| {
2724                Ok(NetworkInfo {
2725                    connected_peers: Vec::new(),
2726                    listeners: Vec::new(),
2727                })
2728            });
2729
2730        let mut service_data = NodeServiceData {
2731            auto_restart: false,
2732            connected_peers: None,
2733            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
2734            evm_network: EvmNetwork::ArbitrumOne,
2735            home_network: false,
2736            listen_addr: None,
2737            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
2738            log_format: None,
2739            max_archived_log_files: None,
2740            max_log_files: None,
2741            metrics_port: None,
2742            network_id: None,
2743            node_ip: None,
2744            node_port: None,
2745            number: 1,
2746            peer_id: Some(PeerId::from_str(
2747                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2748            )?),
2749            peers_args: InitialPeersConfig {
2750                first: true,
2751                addrs: vec![],
2752                network_contacts_url: vec![],
2753                local: false,
2754                disable_mainnet_contacts: false,
2755                ignore_cache: false,
2756                bootstrap_cache_dir: None,
2757            },
2758            pid: Some(1000),
2759            rewards_address: RewardsAddress::from_str(
2760                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2761            )?,
2762            reward_balance: Some(AttoTokens::zero()),
2763            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2764            antnode_path: current_node_bin.to_path_buf(),
2765            service_name: "antnode1".to_string(),
2766            status: ServiceStatus::Running,
2767            upnp: false,
2768            user: Some("ant".to_string()),
2769            user_mode: false,
2770            version: current_version.to_string(),
2771        };
2772        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2773
2774        let mut service_manager = ServiceManager::new(
2775            service,
2776            Box::new(mock_service_control),
2777            VerbosityLevel::Normal,
2778        );
2779
2780        service_manager
2781            .upgrade(UpgradeOptions {
2782                auto_restart: false,
2783                env_variables: None,
2784                force: false,
2785                start_service: true,
2786                target_bin_path: target_node_bin.to_path_buf(),
2787                target_version: Version::parse(target_version).unwrap(),
2788            })
2789            .await?;
2790
2791        assert!(service_manager.service.service_data.peers_args.first);
2792
2793        Ok(())
2794    }
2795
2796    #[tokio::test]
2797    async fn upgrade_should_retain_the_peers_arg() -> Result<()> {
2798        let current_version = "0.1.0";
2799        let target_version = "0.2.0";
2800
2801        let tmp_data_dir = assert_fs::TempDir::new()?;
2802        let current_install_dir = tmp_data_dir.child("antnode_install");
2803        current_install_dir.create_dir_all()?;
2804
2805        let current_node_bin = current_install_dir.child("antnode");
2806        current_node_bin.write_binary(b"fake antnode binary")?;
2807        let target_node_bin = tmp_data_dir.child("antnode");
2808        target_node_bin.write_binary(b"fake antnode binary")?;
2809
2810        let mut mock_service_control = MockServiceControl::new();
2811        let mut mock_rpc_client = MockRpcClient::new();
2812
2813        // before binary upgrade
2814        mock_service_control
2815            .expect_get_process_pid()
2816            .with(eq(current_node_bin.to_path_buf().clone()))
2817            .times(1)
2818            .returning(|_| Ok(1000));
2819        mock_service_control
2820            .expect_stop()
2821            .with(eq("antnode1"), eq(false))
2822            .times(1)
2823            .returning(|_, _| Ok(()));
2824
2825        // after binary upgrade
2826        mock_service_control
2827            .expect_uninstall()
2828            .with(eq("antnode1"), eq(false))
2829            .times(1)
2830            .returning(|_, _| Ok(()));
2831        mock_service_control
2832            .expect_install()
2833            .with(
2834                eq(ServiceInstallCtx {
2835                    args: vec![
2836                        OsString::from("--rpc"),
2837                        OsString::from("127.0.0.1:8081"),
2838                        OsString::from("--root-dir"),
2839                        OsString::from("/var/antctl/services/antnode1"),
2840                        OsString::from("--log-output-dest"),
2841                        OsString::from("/var/log/antnode/antnode1"),
2842                        OsString::from("--peer"),
2843                        OsString::from(
2844                            "/ip4/127.0.0.1/tcp/8080/p2p/12D3KooWRBhwfeP2Y4TCx1SM6s9rUoHhR5STiGwxBhgFRcw3UERE"
2845                        ),
2846                        OsString::from("--rewards-address"),
2847                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
2848                        OsString::from("evm-arbitrum-one"),
2849                    ],
2850                    autostart: false,
2851                    contents: None,
2852                    environment: None,
2853                    label: "antnode1".parse()?,
2854                    program: current_node_bin.to_path_buf(),
2855                    username: Some("ant".to_string()),
2856                    working_directory: None,
2857                    disable_restart_on_failure: true,
2858                }),
2859                eq(false),
2860            )
2861            .times(1)
2862            .returning(|_, _| Ok(()));
2863
2864        // after service restart
2865        mock_service_control
2866            .expect_start()
2867            .with(eq("antnode1"), eq(false))
2868            .times(1)
2869            .returning(|_, _| Ok(()));
2870        mock_service_control
2871            .expect_wait()
2872            .with(eq(3000))
2873            .times(1)
2874            .returning(|_| ());
2875        mock_service_control
2876            .expect_get_process_pid()
2877            .with(eq(current_node_bin.to_path_buf().clone()))
2878            .times(1)
2879            .returning(|_| Ok(100));
2880
2881        mock_rpc_client.expect_node_info().times(1).returning(|| {
2882            Ok(NodeInfo {
2883                pid: 2000,
2884                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
2885                data_path: PathBuf::from("/var/antctl/services/antnode1"),
2886                log_path: PathBuf::from("/var/log/antnode/antnode1"),
2887                version: target_version.to_string(),
2888                uptime: std::time::Duration::from_secs(1), // the service was just started
2889                wallet_balance: 0,
2890            })
2891        });
2892        mock_rpc_client
2893            .expect_network_info()
2894            .times(1)
2895            .returning(|| {
2896                Ok(NetworkInfo {
2897                    connected_peers: Vec::new(),
2898                    listeners: Vec::new(),
2899                })
2900            });
2901
2902        let mut service_data = NodeServiceData {
2903            auto_restart: false,
2904            connected_peers: None,
2905            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
2906            evm_network: EvmNetwork::ArbitrumOne,
2907            home_network: false,
2908            listen_addr: None,
2909            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
2910            log_format: None,
2911            max_archived_log_files: None,
2912            max_log_files: None,
2913            metrics_port: None,
2914network_id: None,
2915            node_ip: None,
2916            node_port: None,
2917            number: 1,
2918            peer_id: Some(PeerId::from_str(
2919                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
2920            )?),
2921            peers_args:  InitialPeersConfig {
2922                first: false,
2923                addrs: vec![
2924                    "/ip4/127.0.0.1/tcp/8080/p2p/12D3KooWRBhwfeP2Y4TCx1SM6s9rUoHhR5STiGwxBhgFRcw3UERE"
2925                        .parse()?,
2926                ],
2927                network_contacts_url: vec![],
2928                local: false,
2929                disable_mainnet_contacts: false,
2930                ignore_cache: false,
2931        bootstrap_cache_dir: None,
2932    },
2933            pid: Some(1000),
2934            rewards_address: RewardsAddress::from_str(
2935                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
2936            )?,
2937            reward_balance: Some(AttoTokens::zero()),
2938            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
2939            antnode_path: current_node_bin.to_path_buf(),
2940            service_name: "antnode1".to_string(),
2941            status: ServiceStatus::Running,
2942            upnp: false,
2943            user: Some("ant".to_string()),
2944            user_mode: false,
2945            version: current_version.to_string(),
2946        };
2947        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
2948
2949        let mut service_manager = ServiceManager::new(
2950            service,
2951            Box::new(mock_service_control),
2952            VerbosityLevel::Normal,
2953        );
2954
2955        service_manager
2956            .upgrade(UpgradeOptions {
2957                auto_restart: false,
2958                env_variables: None,
2959                force: false,
2960                start_service: true,
2961                target_bin_path: target_node_bin.to_path_buf(),
2962                target_version: Version::parse(target_version).unwrap(),
2963            })
2964            .await?;
2965
2966        assert!(!service_manager
2967            .service
2968            .service_data
2969            .peers_args
2970            .addrs
2971            .is_empty());
2972
2973        Ok(())
2974    }
2975
2976    #[tokio::test]
2977    async fn upgrade_should_retain_the_network_id_arg() -> Result<()> {
2978        let current_version = "0.1.0";
2979        let target_version = "0.2.0";
2980
2981        let tmp_data_dir = assert_fs::TempDir::new()?;
2982        let current_install_dir = tmp_data_dir.child("antnode_install");
2983        current_install_dir.create_dir_all()?;
2984
2985        let current_node_bin = current_install_dir.child("antnode");
2986        current_node_bin.write_binary(b"fake antnode binary")?;
2987        let target_node_bin = tmp_data_dir.child("antnode");
2988        target_node_bin.write_binary(b"fake antnode binary")?;
2989
2990        let mut mock_service_control = MockServiceControl::new();
2991        let mut mock_rpc_client = MockRpcClient::new();
2992
2993        // before binary upgrade
2994        mock_service_control
2995            .expect_get_process_pid()
2996            .with(eq(current_node_bin.to_path_buf().clone()))
2997            .times(1)
2998            .returning(|_| Ok(1000));
2999        mock_service_control
3000            .expect_stop()
3001            .with(eq("antnode1"), eq(false))
3002            .times(1)
3003            .returning(|_, _| Ok(()));
3004
3005        // after binary upgrade
3006        mock_service_control
3007            .expect_uninstall()
3008            .with(eq("antnode1"), eq(false))
3009            .times(1)
3010            .returning(|_, _| Ok(()));
3011        mock_service_control
3012            .expect_install()
3013            .with(
3014                eq(ServiceInstallCtx {
3015                    args: vec![
3016                        OsString::from("--rpc"),
3017                        OsString::from("127.0.0.1:8081"),
3018                        OsString::from("--root-dir"),
3019                        OsString::from("/var/antctl/services/antnode1"),
3020                        OsString::from("--log-output-dest"),
3021                        OsString::from("/var/log/antnode/antnode1"),
3022                        OsString::from("--network-id"),
3023                        OsString::from("5"),
3024                        OsString::from("--rewards-address"),
3025                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3026                        OsString::from("evm-arbitrum-one"),
3027                    ],
3028                    autostart: false,
3029                    contents: None,
3030                    environment: None,
3031                    label: "antnode1".parse()?,
3032                    program: current_node_bin.to_path_buf(),
3033                    username: Some("ant".to_string()),
3034                    working_directory: None,
3035                    disable_restart_on_failure: true,
3036                }),
3037                eq(false),
3038            )
3039            .times(1)
3040            .returning(|_, _| Ok(()));
3041
3042        // after service restart
3043        mock_service_control
3044            .expect_start()
3045            .with(eq("antnode1"), eq(false))
3046            .times(1)
3047            .returning(|_, _| Ok(()));
3048        mock_service_control
3049            .expect_wait()
3050            .with(eq(3000))
3051            .times(1)
3052            .returning(|_| ());
3053        mock_service_control
3054            .expect_get_process_pid()
3055            .with(eq(current_node_bin.to_path_buf().clone()))
3056            .times(1)
3057            .returning(|_| Ok(100));
3058
3059        mock_rpc_client.expect_node_info().times(1).returning(|| {
3060            Ok(NodeInfo {
3061                pid: 2000,
3062                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3063                data_path: PathBuf::from("/var/antctl/services/antnode1"),
3064                log_path: PathBuf::from("/var/log/antnode/antnode1"),
3065                version: target_version.to_string(),
3066                uptime: std::time::Duration::from_secs(1), // the service was just started
3067                wallet_balance: 0,
3068            })
3069        });
3070        mock_rpc_client
3071            .expect_network_info()
3072            .times(1)
3073            .returning(|| {
3074                Ok(NetworkInfo {
3075                    connected_peers: Vec::new(),
3076                    listeners: Vec::new(),
3077                })
3078            });
3079
3080        let mut service_data = NodeServiceData {
3081            auto_restart: false,
3082            connected_peers: None,
3083            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
3084            evm_network: EvmNetwork::ArbitrumOne,
3085            home_network: false,
3086            listen_addr: None,
3087            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
3088            log_format: None,
3089            max_archived_log_files: None,
3090            max_log_files: None,
3091            metrics_port: None,
3092            network_id: Some(5),
3093            node_ip: None,
3094            node_port: None,
3095            number: 1,
3096            peer_id: Some(PeerId::from_str(
3097                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3098            )?),
3099            peers_args: Default::default(),
3100            pid: Some(1000),
3101            rewards_address: RewardsAddress::from_str(
3102                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3103            )?,
3104            reward_balance: Some(AttoTokens::zero()),
3105            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3106            antnode_path: current_node_bin.to_path_buf(),
3107            service_name: "antnode1".to_string(),
3108            status: ServiceStatus::Running,
3109            upnp: false,
3110            user: Some("ant".to_string()),
3111            user_mode: false,
3112            version: current_version.to_string(),
3113        };
3114        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3115
3116        let mut service_manager = ServiceManager::new(
3117            service,
3118            Box::new(mock_service_control),
3119            VerbosityLevel::Normal,
3120        );
3121
3122        service_manager
3123            .upgrade(UpgradeOptions {
3124                auto_restart: false,
3125                env_variables: None,
3126                force: false,
3127                start_service: true,
3128                target_bin_path: target_node_bin.to_path_buf(),
3129                target_version: Version::parse(target_version).unwrap(),
3130            })
3131            .await?;
3132
3133        assert_eq!(service_manager.service.service_data.network_id, Some(5));
3134
3135        Ok(())
3136    }
3137
3138    #[tokio::test]
3139    async fn upgrade_should_retain_the_local_flag() -> Result<()> {
3140        let current_version = "0.1.0";
3141        let target_version = "0.2.0";
3142
3143        let tmp_data_dir = assert_fs::TempDir::new()?;
3144        let current_install_dir = tmp_data_dir.child("antnode_install");
3145        current_install_dir.create_dir_all()?;
3146
3147        let current_node_bin = current_install_dir.child("antnode");
3148        current_node_bin.write_binary(b"fake antnode binary")?;
3149        let target_node_bin = tmp_data_dir.child("antnode");
3150        target_node_bin.write_binary(b"fake antnode binary")?;
3151
3152        let mut mock_service_control = MockServiceControl::new();
3153        let mut mock_rpc_client = MockRpcClient::new();
3154
3155        // before binary upgrade
3156        mock_service_control
3157            .expect_get_process_pid()
3158            .with(eq(current_node_bin.to_path_buf().clone()))
3159            .times(1)
3160            .returning(|_| Ok(1000));
3161        mock_service_control
3162            .expect_stop()
3163            .with(eq("antnode1"), eq(false))
3164            .times(1)
3165            .returning(|_, _| Ok(()));
3166
3167        // after binary upgrade
3168        mock_service_control
3169            .expect_uninstall()
3170            .with(eq("antnode1"), eq(false))
3171            .times(1)
3172            .returning(|_, _| Ok(()));
3173        mock_service_control
3174            .expect_install()
3175            .with(
3176                eq(ServiceInstallCtx {
3177                    args: vec![
3178                        OsString::from("--rpc"),
3179                        OsString::from("127.0.0.1:8081"),
3180                        OsString::from("--root-dir"),
3181                        OsString::from("/var/antctl/services/antnode1"),
3182                        OsString::from("--log-output-dest"),
3183                        OsString::from("/var/log/antnode/antnode1"),
3184                        OsString::from("--local"),
3185                        OsString::from("--rewards-address"),
3186                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3187                        OsString::from("evm-arbitrum-one"),
3188                    ],
3189                    autostart: false,
3190                    contents: None,
3191                    environment: None,
3192                    label: "antnode1".parse()?,
3193                    program: current_node_bin.to_path_buf(),
3194                    username: Some("ant".to_string()),
3195                    working_directory: None,
3196                    disable_restart_on_failure: true,
3197                }),
3198                eq(false),
3199            )
3200            .times(1)
3201            .returning(|_, _| Ok(()));
3202
3203        // after service restart
3204        mock_service_control
3205            .expect_start()
3206            .with(eq("antnode1"), eq(false))
3207            .times(1)
3208            .returning(|_, _| Ok(()));
3209        mock_service_control
3210            .expect_wait()
3211            .with(eq(3000))
3212            .times(1)
3213            .returning(|_| ());
3214        mock_service_control
3215            .expect_get_process_pid()
3216            .with(eq(current_node_bin.to_path_buf().clone()))
3217            .times(1)
3218            .returning(|_| Ok(100));
3219
3220        mock_rpc_client.expect_node_info().times(1).returning(|| {
3221            Ok(NodeInfo {
3222                pid: 2000,
3223                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3224                data_path: PathBuf::from("/var/antctl/services/antnode1"),
3225                log_path: PathBuf::from("/var/log/antnode/antnode1"),
3226                version: target_version.to_string(),
3227                uptime: std::time::Duration::from_secs(1), // the service was just started
3228                wallet_balance: 0,
3229            })
3230        });
3231        mock_rpc_client
3232            .expect_network_info()
3233            .times(1)
3234            .returning(|| {
3235                Ok(NetworkInfo {
3236                    connected_peers: Vec::new(),
3237                    listeners: Vec::new(),
3238                })
3239            });
3240
3241        let mut service_data = NodeServiceData {
3242            auto_restart: false,
3243            connected_peers: None,
3244            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
3245            evm_network: EvmNetwork::ArbitrumOne,
3246            home_network: false,
3247            listen_addr: None,
3248            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
3249            log_format: None,
3250            max_archived_log_files: None,
3251            max_log_files: None,
3252            metrics_port: None,
3253            network_id: None,
3254            node_ip: None,
3255            node_port: None,
3256            number: 1,
3257            peer_id: Some(PeerId::from_str(
3258                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3259            )?),
3260            peers_args: InitialPeersConfig {
3261                first: false,
3262                addrs: vec![],
3263                network_contacts_url: vec![],
3264                local: true,
3265                disable_mainnet_contacts: false,
3266                ignore_cache: false,
3267                bootstrap_cache_dir: None,
3268            },
3269            pid: Some(1000),
3270            rewards_address: RewardsAddress::from_str(
3271                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3272            )?,
3273            reward_balance: Some(AttoTokens::zero()),
3274            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3275            antnode_path: current_node_bin.to_path_buf(),
3276            service_name: "antnode1".to_string(),
3277            status: ServiceStatus::Running,
3278            upnp: false,
3279            user: Some("ant".to_string()),
3280            user_mode: false,
3281            version: current_version.to_string(),
3282        };
3283        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3284
3285        let mut service_manager = ServiceManager::new(
3286            service,
3287            Box::new(mock_service_control),
3288            VerbosityLevel::Normal,
3289        );
3290
3291        service_manager
3292            .upgrade(UpgradeOptions {
3293                auto_restart: false,
3294                env_variables: None,
3295                force: false,
3296                start_service: true,
3297                target_bin_path: target_node_bin.to_path_buf(),
3298                target_version: Version::parse(target_version).unwrap(),
3299            })
3300            .await?;
3301
3302        assert!(service_manager.service.service_data.peers_args.local);
3303
3304        Ok(())
3305    }
3306
3307    #[tokio::test]
3308    async fn upgrade_should_retain_the_network_contacts_url_arg() -> Result<()> {
3309        let current_version = "0.1.0";
3310        let target_version = "0.2.0";
3311
3312        let tmp_data_dir = assert_fs::TempDir::new()?;
3313        let current_install_dir = tmp_data_dir.child("antnode_install");
3314        current_install_dir.create_dir_all()?;
3315
3316        let current_node_bin = current_install_dir.child("antnode");
3317        current_node_bin.write_binary(b"fake antnode binary")?;
3318        let target_node_bin = tmp_data_dir.child("antnode");
3319        target_node_bin.write_binary(b"fake antnode binary")?;
3320
3321        let mut mock_service_control = MockServiceControl::new();
3322        let mut mock_rpc_client = MockRpcClient::new();
3323
3324        // before binary upgrade
3325        mock_service_control
3326            .expect_get_process_pid()
3327            .with(eq(current_node_bin.to_path_buf().clone()))
3328            .times(1)
3329            .returning(|_| Ok(1000));
3330        mock_service_control
3331            .expect_stop()
3332            .with(eq("antnode1"), eq(false))
3333            .times(1)
3334            .returning(|_, _| Ok(()));
3335
3336        // after binary upgrade
3337        mock_service_control
3338            .expect_uninstall()
3339            .with(eq("antnode1"), eq(false))
3340            .times(1)
3341            .returning(|_, _| Ok(()));
3342        mock_service_control
3343            .expect_install()
3344            .with(
3345                eq(ServiceInstallCtx {
3346                    args: vec![
3347                        OsString::from("--rpc"),
3348                        OsString::from("127.0.0.1:8081"),
3349                        OsString::from("--root-dir"),
3350                        OsString::from("/var/antctl/services/antnode1"),
3351                        OsString::from("--log-output-dest"),
3352                        OsString::from("/var/log/antnode/antnode1"),
3353                        OsString::from("--network-contacts-url"),
3354                        OsString::from("http://localhost:8080/contacts.json,http://localhost:8081/contacts.json"),
3355                        OsString::from("--rewards-address"),
3356                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3357                        OsString::from("evm-arbitrum-one"),
3358                    ],
3359                    autostart: false,
3360                    contents: None,
3361                    environment: None,
3362                    label: "antnode1".parse()?,
3363                    program: current_node_bin.to_path_buf(),
3364                    username: Some("ant".to_string()),
3365                    working_directory: None,
3366                    disable_restart_on_failure: true,
3367                }),
3368                eq(false),
3369            )
3370            .times(1)
3371            .returning(|_, _| Ok(()));
3372
3373        // after service restart
3374        mock_service_control
3375            .expect_start()
3376            .with(eq("antnode1"), eq(false))
3377            .times(1)
3378            .returning(|_, _| Ok(()));
3379        mock_service_control
3380            .expect_wait()
3381            .with(eq(3000))
3382            .times(1)
3383            .returning(|_| ());
3384        mock_service_control
3385            .expect_get_process_pid()
3386            .with(eq(current_node_bin.to_path_buf().clone()))
3387            .times(1)
3388            .returning(|_| Ok(100));
3389
3390        mock_rpc_client.expect_node_info().times(1).returning(|| {
3391            Ok(NodeInfo {
3392                pid: 2000,
3393                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3394                data_path: PathBuf::from("/var/antctl/services/antnode1"),
3395                log_path: PathBuf::from("/var/log/antnode/antnode1"),
3396                version: target_version.to_string(),
3397                uptime: std::time::Duration::from_secs(1), // the service was just started
3398                wallet_balance: 0,
3399            })
3400        });
3401        mock_rpc_client
3402            .expect_network_info()
3403            .times(1)
3404            .returning(|| {
3405                Ok(NetworkInfo {
3406                    connected_peers: Vec::new(),
3407                    listeners: Vec::new(),
3408                })
3409            });
3410
3411        let mut service_data = NodeServiceData {
3412            auto_restart: false,
3413            connected_peers: None,
3414            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
3415            evm_network: EvmNetwork::ArbitrumOne,
3416            home_network: false,
3417            listen_addr: None,
3418            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
3419            log_format: None,
3420            max_archived_log_files: None,
3421            max_log_files: None,
3422            metrics_port: None,
3423            network_id: None,
3424            node_ip: None,
3425            node_port: None,
3426            number: 1,
3427            peer_id: Some(PeerId::from_str(
3428                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3429            )?),
3430            peers_args: InitialPeersConfig {
3431                first: false,
3432                addrs: vec![],
3433                network_contacts_url: vec![
3434                    "http://localhost:8080/contacts.json".to_string(),
3435                    "http://localhost:8081/contacts.json".to_string(),
3436                ],
3437                local: false,
3438                disable_mainnet_contacts: false,
3439                ignore_cache: false,
3440                bootstrap_cache_dir: None,
3441            },
3442            pid: Some(1000),
3443            rewards_address: RewardsAddress::from_str(
3444                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3445            )?,
3446            reward_balance: Some(AttoTokens::zero()),
3447            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3448            antnode_path: current_node_bin.to_path_buf(),
3449            service_name: "antnode1".to_string(),
3450            status: ServiceStatus::Running,
3451            upnp: false,
3452            user: Some("ant".to_string()),
3453            user_mode: false,
3454            version: current_version.to_string(),
3455        };
3456        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3457
3458        let mut service_manager = ServiceManager::new(
3459            service,
3460            Box::new(mock_service_control),
3461            VerbosityLevel::Normal,
3462        );
3463
3464        service_manager
3465            .upgrade(UpgradeOptions {
3466                auto_restart: false,
3467                env_variables: None,
3468                force: false,
3469                start_service: true,
3470                target_bin_path: target_node_bin.to_path_buf(),
3471                target_version: Version::parse(target_version).unwrap(),
3472            })
3473            .await?;
3474
3475        assert_eq!(
3476            service_manager
3477                .service
3478                .service_data
3479                .peers_args
3480                .network_contacts_url
3481                .len(),
3482            2
3483        );
3484
3485        Ok(())
3486    }
3487
3488    #[tokio::test]
3489    async fn upgrade_should_retain_the_testnet_flag() -> Result<()> {
3490        let current_version = "0.1.0";
3491        let target_version = "0.2.0";
3492
3493        let tmp_data_dir = assert_fs::TempDir::new()?;
3494        let current_install_dir = tmp_data_dir.child("antnode_install");
3495        current_install_dir.create_dir_all()?;
3496
3497        let current_node_bin = current_install_dir.child("antnode");
3498        current_node_bin.write_binary(b"fake antnode binary")?;
3499        let target_node_bin = tmp_data_dir.child("antnode");
3500        target_node_bin.write_binary(b"fake antnode binary")?;
3501
3502        let mut mock_service_control = MockServiceControl::new();
3503        let mut mock_rpc_client = MockRpcClient::new();
3504
3505        // before binary upgrade
3506        mock_service_control
3507            .expect_get_process_pid()
3508            .with(eq(current_node_bin.to_path_buf().clone()))
3509            .times(1)
3510            .returning(|_| Ok(1000));
3511        mock_service_control
3512            .expect_stop()
3513            .with(eq("antnode1"), eq(false))
3514            .times(1)
3515            .returning(|_, _| Ok(()));
3516
3517        // after binary upgrade
3518        mock_service_control
3519            .expect_uninstall()
3520            .with(eq("antnode1"), eq(false))
3521            .times(1)
3522            .returning(|_, _| Ok(()));
3523        mock_service_control
3524            .expect_install()
3525            .with(
3526                eq(ServiceInstallCtx {
3527                    args: vec![
3528                        OsString::from("--rpc"),
3529                        OsString::from("127.0.0.1:8081"),
3530                        OsString::from("--root-dir"),
3531                        OsString::from("/var/antctl/services/antnode1"),
3532                        OsString::from("--log-output-dest"),
3533                        OsString::from("/var/log/antnode/antnode1"),
3534                        OsString::from("--testnet"),
3535                        OsString::from("--rewards-address"),
3536                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3537                        OsString::from("evm-arbitrum-one"),
3538                    ],
3539                    autostart: false,
3540                    contents: None,
3541                    environment: None,
3542                    label: "antnode1".parse()?,
3543                    program: current_node_bin.to_path_buf(),
3544                    username: Some("ant".to_string()),
3545                    working_directory: None,
3546                    disable_restart_on_failure: true,
3547                }),
3548                eq(false),
3549            )
3550            .times(1)
3551            .returning(|_, _| Ok(()));
3552
3553        // after service restart
3554        mock_service_control
3555            .expect_start()
3556            .with(eq("antnode1"), eq(false))
3557            .times(1)
3558            .returning(|_, _| Ok(()));
3559        mock_service_control
3560            .expect_wait()
3561            .with(eq(3000))
3562            .times(1)
3563            .returning(|_| ());
3564        mock_service_control
3565            .expect_get_process_pid()
3566            .with(eq(current_node_bin.to_path_buf().clone()))
3567            .times(1)
3568            .returning(|_| Ok(100));
3569
3570        mock_rpc_client.expect_node_info().times(1).returning(|| {
3571            Ok(NodeInfo {
3572                pid: 2000,
3573                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3574                data_path: PathBuf::from("/var/antctl/services/antnode1"),
3575                log_path: PathBuf::from("/var/log/antnode/antnode1"),
3576                version: target_version.to_string(),
3577                uptime: std::time::Duration::from_secs(1), // the service was just started
3578                wallet_balance: 0,
3579            })
3580        });
3581        mock_rpc_client
3582            .expect_network_info()
3583            .times(1)
3584            .returning(|| {
3585                Ok(NetworkInfo {
3586                    connected_peers: Vec::new(),
3587                    listeners: Vec::new(),
3588                })
3589            });
3590
3591        let mut service_data = NodeServiceData {
3592            auto_restart: false,
3593            connected_peers: None,
3594            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
3595            evm_network: EvmNetwork::ArbitrumOne,
3596            home_network: false,
3597            listen_addr: None,
3598            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
3599            log_format: None,
3600            max_archived_log_files: None,
3601            max_log_files: None,
3602            metrics_port: None,
3603            network_id: None,
3604            node_ip: None,
3605            node_port: None,
3606            number: 1,
3607            peer_id: Some(PeerId::from_str(
3608                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3609            )?),
3610            peers_args: InitialPeersConfig {
3611                first: false,
3612                addrs: vec![],
3613                network_contacts_url: vec![],
3614                local: false,
3615                disable_mainnet_contacts: true,
3616                ignore_cache: false,
3617                bootstrap_cache_dir: None,
3618            },
3619            pid: Some(1000),
3620            rewards_address: RewardsAddress::from_str(
3621                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3622            )?,
3623            reward_balance: Some(AttoTokens::zero()),
3624            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3625            antnode_path: current_node_bin.to_path_buf(),
3626            service_name: "antnode1".to_string(),
3627            status: ServiceStatus::Running,
3628            upnp: false,
3629            user: Some("ant".to_string()),
3630            user_mode: false,
3631            version: current_version.to_string(),
3632        };
3633        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3634
3635        let mut service_manager = ServiceManager::new(
3636            service,
3637            Box::new(mock_service_control),
3638            VerbosityLevel::Normal,
3639        );
3640
3641        service_manager
3642            .upgrade(UpgradeOptions {
3643                auto_restart: false,
3644                env_variables: None,
3645                force: false,
3646                start_service: true,
3647                target_bin_path: target_node_bin.to_path_buf(),
3648                target_version: Version::parse(target_version).unwrap(),
3649            })
3650            .await?;
3651
3652        assert!(
3653            service_manager
3654                .service
3655                .service_data
3656                .peers_args
3657                .disable_mainnet_contacts
3658        );
3659
3660        Ok(())
3661    }
3662
3663    #[tokio::test]
3664    async fn upgrade_should_retain_the_ignore_cache_flag() -> Result<()> {
3665        let current_version = "0.1.0";
3666        let target_version = "0.2.0";
3667
3668        let tmp_data_dir = assert_fs::TempDir::new()?;
3669        let current_install_dir = tmp_data_dir.child("antnode_install");
3670        current_install_dir.create_dir_all()?;
3671
3672        let current_node_bin = current_install_dir.child("antnode");
3673        current_node_bin.write_binary(b"fake antnode binary")?;
3674        let target_node_bin = tmp_data_dir.child("antnode");
3675        target_node_bin.write_binary(b"fake antnode binary")?;
3676
3677        let mut mock_service_control = MockServiceControl::new();
3678        let mut mock_rpc_client = MockRpcClient::new();
3679
3680        // before binary upgrade
3681        mock_service_control
3682            .expect_get_process_pid()
3683            .with(eq(current_node_bin.to_path_buf().clone()))
3684            .times(1)
3685            .returning(|_| Ok(1000));
3686        mock_service_control
3687            .expect_stop()
3688            .with(eq("antnode1"), eq(false))
3689            .times(1)
3690            .returning(|_, _| Ok(()));
3691
3692        // after binary upgrade
3693        mock_service_control
3694            .expect_uninstall()
3695            .with(eq("antnode1"), eq(false))
3696            .times(1)
3697            .returning(|_, _| Ok(()));
3698        mock_service_control
3699            .expect_install()
3700            .with(
3701                eq(ServiceInstallCtx {
3702                    args: vec![
3703                        OsString::from("--rpc"),
3704                        OsString::from("127.0.0.1:8081"),
3705                        OsString::from("--root-dir"),
3706                        OsString::from("/var/antctl/services/antnode1"),
3707                        OsString::from("--log-output-dest"),
3708                        OsString::from("/var/log/antnode/antnode1"),
3709                        OsString::from("--ignore-cache"),
3710                        OsString::from("--rewards-address"),
3711                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3712                        OsString::from("evm-arbitrum-one"),
3713                    ],
3714                    autostart: false,
3715                    contents: None,
3716                    environment: None,
3717                    label: "antnode1".parse()?,
3718                    program: current_node_bin.to_path_buf(),
3719                    username: Some("ant".to_string()),
3720                    working_directory: None,
3721                    disable_restart_on_failure: true,
3722                }),
3723                eq(false),
3724            )
3725            .times(1)
3726            .returning(|_, _| Ok(()));
3727
3728        // after service restart
3729        mock_service_control
3730            .expect_start()
3731            .with(eq("antnode1"), eq(false))
3732            .times(1)
3733            .returning(|_, _| Ok(()));
3734        mock_service_control
3735            .expect_wait()
3736            .with(eq(3000))
3737            .times(1)
3738            .returning(|_| ());
3739        mock_service_control
3740            .expect_get_process_pid()
3741            .with(eq(current_node_bin.to_path_buf().clone()))
3742            .times(1)
3743            .returning(|_| Ok(100));
3744
3745        mock_rpc_client.expect_node_info().times(1).returning(|| {
3746            Ok(NodeInfo {
3747                pid: 2000,
3748                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3749                data_path: PathBuf::from("/var/antctl/services/antnode1"),
3750                log_path: PathBuf::from("/var/log/antnode/antnode1"),
3751                version: target_version.to_string(),
3752                uptime: std::time::Duration::from_secs(1), // the service was just started
3753                wallet_balance: 0,
3754            })
3755        });
3756        mock_rpc_client
3757            .expect_network_info()
3758            .times(1)
3759            .returning(|| {
3760                Ok(NetworkInfo {
3761                    connected_peers: Vec::new(),
3762                    listeners: Vec::new(),
3763                })
3764            });
3765
3766        let mut service_data = NodeServiceData {
3767            auto_restart: false,
3768            connected_peers: None,
3769            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
3770            evm_network: EvmNetwork::ArbitrumOne,
3771            home_network: false,
3772            listen_addr: None,
3773            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
3774            log_format: None,
3775            max_archived_log_files: None,
3776            max_log_files: None,
3777            metrics_port: None,
3778            network_id: None,
3779            node_ip: None,
3780            node_port: None,
3781            number: 1,
3782            peer_id: Some(PeerId::from_str(
3783                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3784            )?),
3785            peers_args: InitialPeersConfig {
3786                first: false,
3787                addrs: vec![],
3788                network_contacts_url: vec![],
3789                local: false,
3790                disable_mainnet_contacts: false,
3791                ignore_cache: true,
3792                bootstrap_cache_dir: None,
3793            },
3794            pid: Some(1000),
3795            rewards_address: RewardsAddress::from_str(
3796                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3797            )?,
3798            reward_balance: Some(AttoTokens::zero()),
3799            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3800            antnode_path: current_node_bin.to_path_buf(),
3801            service_name: "antnode1".to_string(),
3802            status: ServiceStatus::Running,
3803            upnp: false,
3804            user: Some("ant".to_string()),
3805            user_mode: false,
3806            version: current_version.to_string(),
3807        };
3808        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3809
3810        let mut service_manager = ServiceManager::new(
3811            service,
3812            Box::new(mock_service_control),
3813            VerbosityLevel::Normal,
3814        );
3815
3816        service_manager
3817            .upgrade(UpgradeOptions {
3818                auto_restart: false,
3819                env_variables: None,
3820                force: false,
3821                start_service: true,
3822                target_bin_path: target_node_bin.to_path_buf(),
3823                target_version: Version::parse(target_version).unwrap(),
3824            })
3825            .await?;
3826
3827        assert!(service_manager.service.service_data.peers_args.ignore_cache);
3828
3829        Ok(())
3830    }
3831
3832    #[tokio::test]
3833    async fn upgrade_should_retain_the_custom_bootstrap_cache_path() -> Result<()> {
3834        let current_version = "0.1.0";
3835        let target_version = "0.2.0";
3836
3837        let tmp_data_dir = assert_fs::TempDir::new()?;
3838        let current_install_dir = tmp_data_dir.child("antnode_install");
3839        current_install_dir.create_dir_all()?;
3840
3841        let current_node_bin = current_install_dir.child("antnode");
3842        current_node_bin.write_binary(b"fake antnode binary")?;
3843        let target_node_bin = tmp_data_dir.child("antnode");
3844        target_node_bin.write_binary(b"fake antnode binary")?;
3845
3846        let mut mock_service_control = MockServiceControl::new();
3847        let mut mock_rpc_client = MockRpcClient::new();
3848
3849        // before binary upgrade
3850        mock_service_control
3851            .expect_get_process_pid()
3852            .with(eq(current_node_bin.to_path_buf().clone()))
3853            .times(1)
3854            .returning(|_| Ok(1000));
3855        mock_service_control
3856            .expect_stop()
3857            .with(eq("antnode1"), eq(false))
3858            .times(1)
3859            .returning(|_, _| Ok(()));
3860
3861        // after binary upgrade
3862        mock_service_control
3863            .expect_uninstall()
3864            .with(eq("antnode1"), eq(false))
3865            .times(1)
3866            .returning(|_, _| Ok(()));
3867        mock_service_control
3868            .expect_install()
3869            .with(
3870                eq(ServiceInstallCtx {
3871                    args: vec![
3872                        OsString::from("--rpc"),
3873                        OsString::from("127.0.0.1:8081"),
3874                        OsString::from("--root-dir"),
3875                        OsString::from("/var/antctl/services/antnode1"),
3876                        OsString::from("--log-output-dest"),
3877                        OsString::from("/var/log/antnode/antnode1"),
3878                        OsString::from("--bootstrap-cache-dir"),
3879                        OsString::from("/var/antctl/services/antnode1/bootstrap_cache"),
3880                        OsString::from("--rewards-address"),
3881                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
3882                        OsString::from("evm-arbitrum-one"),
3883                    ],
3884                    autostart: false,
3885                    contents: None,
3886                    environment: None,
3887                    label: "antnode1".parse()?,
3888                    program: current_node_bin.to_path_buf(),
3889                    username: Some("ant".to_string()),
3890                    working_directory: None,
3891                    disable_restart_on_failure: true,
3892                }),
3893                eq(false),
3894            )
3895            .times(1)
3896            .returning(|_, _| Ok(()));
3897
3898        // after service restart
3899        mock_service_control
3900            .expect_start()
3901            .with(eq("antnode1"), eq(false))
3902            .times(1)
3903            .returning(|_, _| Ok(()));
3904        mock_service_control
3905            .expect_wait()
3906            .with(eq(3000))
3907            .times(1)
3908            .returning(|_| ());
3909        mock_service_control
3910            .expect_get_process_pid()
3911            .with(eq(current_node_bin.to_path_buf().clone()))
3912            .times(1)
3913            .returning(|_| Ok(100));
3914
3915        mock_rpc_client.expect_node_info().times(1).returning(|| {
3916            Ok(NodeInfo {
3917                pid: 2000,
3918                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
3919                data_path: PathBuf::from("/var/antctl/services/antnode1"),
3920                log_path: PathBuf::from("/var/log/antnode/antnode1"),
3921                version: target_version.to_string(),
3922                uptime: std::time::Duration::from_secs(1), // the service was just started
3923                wallet_balance: 0,
3924            })
3925        });
3926        mock_rpc_client
3927            .expect_network_info()
3928            .times(1)
3929            .returning(|| {
3930                Ok(NetworkInfo {
3931                    connected_peers: Vec::new(),
3932                    listeners: Vec::new(),
3933                })
3934            });
3935
3936        let mut service_data = NodeServiceData {
3937            auto_restart: false,
3938            connected_peers: None,
3939            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
3940            evm_network: EvmNetwork::ArbitrumOne,
3941            home_network: false,
3942            listen_addr: None,
3943            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
3944            log_format: None,
3945            max_archived_log_files: None,
3946            max_log_files: None,
3947            metrics_port: None,
3948            network_id: None,
3949            node_ip: None,
3950            node_port: None,
3951            number: 1,
3952            peer_id: Some(PeerId::from_str(
3953                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
3954            )?),
3955            peers_args: InitialPeersConfig {
3956                first: false,
3957                addrs: vec![],
3958                network_contacts_url: vec![],
3959                local: false,
3960                disable_mainnet_contacts: false,
3961                ignore_cache: false,
3962                bootstrap_cache_dir: Some(PathBuf::from(
3963                    "/var/antctl/services/antnode1/bootstrap_cache",
3964                )),
3965            },
3966            pid: Some(1000),
3967            rewards_address: RewardsAddress::from_str(
3968                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
3969            )?,
3970            reward_balance: Some(AttoTokens::zero()),
3971            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
3972            antnode_path: current_node_bin.to_path_buf(),
3973            service_name: "antnode1".to_string(),
3974            status: ServiceStatus::Running,
3975            upnp: false,
3976            user: Some("ant".to_string()),
3977            user_mode: false,
3978            version: current_version.to_string(),
3979        };
3980        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
3981
3982        let mut service_manager = ServiceManager::new(
3983            service,
3984            Box::new(mock_service_control),
3985            VerbosityLevel::Normal,
3986        );
3987
3988        service_manager
3989            .upgrade(UpgradeOptions {
3990                auto_restart: false,
3991                env_variables: None,
3992                force: false,
3993                start_service: true,
3994                target_bin_path: target_node_bin.to_path_buf(),
3995                target_version: Version::parse(target_version).unwrap(),
3996            })
3997            .await?;
3998
3999        assert_eq!(
4000            service_manager
4001                .service
4002                .service_data
4003                .peers_args
4004                .bootstrap_cache_dir,
4005            Some(PathBuf::from(
4006                "/var/antctl/services/antnode1/bootstrap_cache"
4007            ))
4008        );
4009
4010        Ok(())
4011    }
4012
4013    #[tokio::test]
4014    async fn upgrade_should_retain_the_upnp_flag() -> Result<()> {
4015        let current_version = "0.1.0";
4016        let target_version = "0.2.0";
4017
4018        let tmp_data_dir = assert_fs::TempDir::new()?;
4019        let current_install_dir = tmp_data_dir.child("antnode_install");
4020        current_install_dir.create_dir_all()?;
4021
4022        let current_node_bin = current_install_dir.child("antnode");
4023        current_node_bin.write_binary(b"fake antnode binary")?;
4024        let target_node_bin = tmp_data_dir.child("antnode");
4025        target_node_bin.write_binary(b"fake antnode binary")?;
4026
4027        let mut mock_service_control = MockServiceControl::new();
4028        let mut mock_rpc_client = MockRpcClient::new();
4029
4030        // before binary upgrade
4031        mock_service_control
4032            .expect_get_process_pid()
4033            .with(eq(current_node_bin.to_path_buf().clone()))
4034            .times(1)
4035            .returning(|_| Ok(1000));
4036        mock_service_control
4037            .expect_stop()
4038            .with(eq("antnode1"), eq(false))
4039            .times(1)
4040            .returning(|_, _| Ok(()));
4041
4042        // after binary upgrade
4043        mock_service_control
4044            .expect_uninstall()
4045            .with(eq("antnode1"), eq(false))
4046            .times(1)
4047            .returning(|_, _| Ok(()));
4048        mock_service_control
4049            .expect_install()
4050            .with(
4051                eq(ServiceInstallCtx {
4052                    args: vec![
4053                        OsString::from("--rpc"),
4054                        OsString::from("127.0.0.1:8081"),
4055                        OsString::from("--root-dir"),
4056                        OsString::from("/var/antctl/services/antnode1"),
4057                        OsString::from("--log-output-dest"),
4058                        OsString::from("/var/log/antnode/antnode1"),
4059                        OsString::from("--upnp"),
4060                        OsString::from("--rewards-address"),
4061                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4062                        OsString::from("evm-arbitrum-one"),
4063                    ],
4064                    autostart: false,
4065                    contents: None,
4066                    environment: None,
4067                    label: "antnode1".parse()?,
4068                    program: current_node_bin.to_path_buf(),
4069                    username: Some("ant".to_string()),
4070                    working_directory: None,
4071                    disable_restart_on_failure: true,
4072                }),
4073                eq(false),
4074            )
4075            .times(1)
4076            .returning(|_, _| Ok(()));
4077
4078        // after service restart
4079        mock_service_control
4080            .expect_start()
4081            .with(eq("antnode1"), eq(false))
4082            .times(1)
4083            .returning(|_, _| Ok(()));
4084        mock_service_control
4085            .expect_wait()
4086            .with(eq(3000))
4087            .times(1)
4088            .returning(|_| ());
4089        mock_service_control
4090            .expect_get_process_pid()
4091            .with(eq(current_node_bin.to_path_buf().clone()))
4092            .times(1)
4093            .returning(|_| Ok(100));
4094
4095        mock_rpc_client.expect_node_info().times(1).returning(|| {
4096            Ok(NodeInfo {
4097                pid: 2000,
4098                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4099                data_path: PathBuf::from("/var/antctl/services/antnode1"),
4100                log_path: PathBuf::from("/var/log/antnode/antnode1"),
4101                version: target_version.to_string(),
4102                uptime: std::time::Duration::from_secs(1), // the service was just started
4103                wallet_balance: 0,
4104            })
4105        });
4106        mock_rpc_client
4107            .expect_network_info()
4108            .times(1)
4109            .returning(|| {
4110                Ok(NetworkInfo {
4111                    connected_peers: Vec::new(),
4112                    listeners: Vec::new(),
4113                })
4114            });
4115
4116        let mut service_data = NodeServiceData {
4117            auto_restart: false,
4118            connected_peers: None,
4119            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
4120            evm_network: EvmNetwork::ArbitrumOne,
4121            home_network: false,
4122            listen_addr: None,
4123            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
4124            log_format: None,
4125            max_archived_log_files: None,
4126            max_log_files: None,
4127            metrics_port: None,
4128            network_id: None,
4129            node_ip: None,
4130            node_port: None,
4131            number: 1,
4132            peer_id: Some(PeerId::from_str(
4133                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4134            )?),
4135            peers_args: InitialPeersConfig::default(),
4136            pid: Some(1000),
4137            rewards_address: RewardsAddress::from_str(
4138                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4139            )?,
4140            reward_balance: Some(AttoTokens::zero()),
4141            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4142            antnode_path: current_node_bin.to_path_buf(),
4143            service_name: "antnode1".to_string(),
4144            status: ServiceStatus::Running,
4145            upnp: true,
4146            user: Some("ant".to_string()),
4147            user_mode: false,
4148            version: current_version.to_string(),
4149        };
4150        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4151
4152        let mut service_manager = ServiceManager::new(
4153            service,
4154            Box::new(mock_service_control),
4155            VerbosityLevel::Normal,
4156        );
4157
4158        service_manager
4159            .upgrade(UpgradeOptions {
4160                auto_restart: false,
4161                env_variables: None,
4162                force: false,
4163                start_service: true,
4164                target_bin_path: target_node_bin.to_path_buf(),
4165                target_version: Version::parse(target_version).unwrap(),
4166            })
4167            .await?;
4168
4169        assert!(service_manager.service.service_data.upnp);
4170
4171        Ok(())
4172    }
4173
4174    #[tokio::test]
4175    async fn upgrade_should_retain_the_log_format_flag() -> Result<()> {
4176        let current_version = "0.1.0";
4177        let target_version = "0.2.0";
4178
4179        let tmp_data_dir = assert_fs::TempDir::new()?;
4180        let current_install_dir = tmp_data_dir.child("antnode_install");
4181        current_install_dir.create_dir_all()?;
4182
4183        let current_node_bin = current_install_dir.child("antnode");
4184        current_node_bin.write_binary(b"fake antnode binary")?;
4185        let target_node_bin = tmp_data_dir.child("antnode");
4186        target_node_bin.write_binary(b"fake antnode binary")?;
4187
4188        let mut mock_service_control = MockServiceControl::new();
4189        let mut mock_rpc_client = MockRpcClient::new();
4190
4191        // before binary upgrade
4192        mock_service_control
4193            .expect_get_process_pid()
4194            .with(eq(current_node_bin.to_path_buf().clone()))
4195            .times(1)
4196            .returning(|_| Ok(1000));
4197        mock_service_control
4198            .expect_stop()
4199            .with(eq("antnode1"), eq(false))
4200            .times(1)
4201            .returning(|_, _| Ok(()));
4202
4203        // after binary upgrade
4204        mock_service_control
4205            .expect_uninstall()
4206            .with(eq("antnode1"), eq(false))
4207            .times(1)
4208            .returning(|_, _| Ok(()));
4209        mock_service_control
4210            .expect_install()
4211            .with(
4212                eq(ServiceInstallCtx {
4213                    args: vec![
4214                        OsString::from("--rpc"),
4215                        OsString::from("127.0.0.1:8081"),
4216                        OsString::from("--root-dir"),
4217                        OsString::from("/var/antctl/services/antnode1"),
4218                        OsString::from("--log-output-dest"),
4219                        OsString::from("/var/log/antnode/antnode1"),
4220                        OsString::from("--log-format"),
4221                        OsString::from("json"),
4222                        OsString::from("--rewards-address"),
4223                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4224                        OsString::from("evm-arbitrum-one"),
4225                    ],
4226                    autostart: false,
4227                    contents: None,
4228                    environment: None,
4229                    label: "antnode1".parse()?,
4230                    program: current_node_bin.to_path_buf(),
4231                    username: Some("ant".to_string()),
4232                    working_directory: None,
4233                    disable_restart_on_failure: true,
4234                }),
4235                eq(false),
4236            )
4237            .times(1)
4238            .returning(|_, _| Ok(()));
4239
4240        // after service restart
4241        mock_service_control
4242            .expect_start()
4243            .with(eq("antnode1"), eq(false))
4244            .times(1)
4245            .returning(|_, _| Ok(()));
4246        mock_service_control
4247            .expect_wait()
4248            .with(eq(3000))
4249            .times(1)
4250            .returning(|_| ());
4251        mock_service_control
4252            .expect_get_process_pid()
4253            .with(eq(current_node_bin.to_path_buf().clone()))
4254            .times(1)
4255            .returning(|_| Ok(100));
4256
4257        mock_rpc_client.expect_node_info().times(1).returning(|| {
4258            Ok(NodeInfo {
4259                pid: 2000,
4260                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4261                data_path: PathBuf::from("/var/antctl/services/antnode1"),
4262                log_path: PathBuf::from("/var/log/antnode/antnode1"),
4263                version: target_version.to_string(),
4264                uptime: std::time::Duration::from_secs(1), // the service was just started
4265                wallet_balance: 0,
4266            })
4267        });
4268        mock_rpc_client
4269            .expect_network_info()
4270            .times(1)
4271            .returning(|| {
4272                Ok(NetworkInfo {
4273                    connected_peers: Vec::new(),
4274                    listeners: Vec::new(),
4275                })
4276            });
4277
4278        let mut service_data = NodeServiceData {
4279            auto_restart: false,
4280            connected_peers: None,
4281            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
4282            evm_network: EvmNetwork::ArbitrumOne,
4283            home_network: false,
4284            listen_addr: None,
4285            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
4286            log_format: Some(LogFormat::Json),
4287            max_archived_log_files: None,
4288            max_log_files: None,
4289            metrics_port: None,
4290            network_id: None,
4291            node_ip: None,
4292            node_port: None,
4293            number: 1,
4294            peer_id: Some(PeerId::from_str(
4295                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4296            )?),
4297            peers_args: InitialPeersConfig::default(),
4298            pid: Some(1000),
4299            rewards_address: RewardsAddress::from_str(
4300                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4301            )?,
4302            reward_balance: Some(AttoTokens::zero()),
4303            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4304            antnode_path: current_node_bin.to_path_buf(),
4305            service_name: "antnode1".to_string(),
4306            status: ServiceStatus::Running,
4307            upnp: false,
4308            user: Some("ant".to_string()),
4309            user_mode: false,
4310            version: current_version.to_string(),
4311        };
4312        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4313
4314        let mut service_manager = ServiceManager::new(
4315            service,
4316            Box::new(mock_service_control),
4317            VerbosityLevel::Normal,
4318        );
4319
4320        service_manager
4321            .upgrade(UpgradeOptions {
4322                auto_restart: false,
4323                env_variables: None,
4324                force: false,
4325                start_service: true,
4326                target_bin_path: target_node_bin.to_path_buf(),
4327                target_version: Version::parse(target_version).unwrap(),
4328            })
4329            .await?;
4330
4331        assert!(service_manager.service.service_data.log_format.is_some());
4332        assert_eq!(
4333            service_manager.service.service_data.log_format,
4334            Some(LogFormat::Json)
4335        );
4336
4337        Ok(())
4338    }
4339
4340    #[tokio::test]
4341    async fn upgrade_should_retain_the_home_network_flag() -> Result<()> {
4342        let current_version = "0.1.0";
4343        let target_version = "0.2.0";
4344
4345        let tmp_data_dir = assert_fs::TempDir::new()?;
4346        let current_install_dir = tmp_data_dir.child("antnode_install");
4347        current_install_dir.create_dir_all()?;
4348
4349        let current_node_bin = current_install_dir.child("antnode");
4350        current_node_bin.write_binary(b"fake antnode binary")?;
4351        let target_node_bin = tmp_data_dir.child("antnode");
4352        target_node_bin.write_binary(b"fake antnode binary")?;
4353
4354        let mut mock_service_control = MockServiceControl::new();
4355        let mut mock_rpc_client = MockRpcClient::new();
4356
4357        // before binary upgrade
4358        mock_service_control
4359            .expect_get_process_pid()
4360            .with(eq(current_node_bin.to_path_buf().clone()))
4361            .times(1)
4362            .returning(|_| Ok(1000));
4363        mock_service_control
4364            .expect_stop()
4365            .with(eq("antnode1"), eq(false))
4366            .times(1)
4367            .returning(|_, _| Ok(()));
4368
4369        // after binary upgrade
4370        mock_service_control
4371            .expect_uninstall()
4372            .with(eq("antnode1"), eq(false))
4373            .times(1)
4374            .returning(|_, _| Ok(()));
4375        mock_service_control
4376            .expect_install()
4377            .with(
4378                eq(ServiceInstallCtx {
4379                    args: vec![
4380                        OsString::from("--rpc"),
4381                        OsString::from("127.0.0.1:8081"),
4382                        OsString::from("--root-dir"),
4383                        OsString::from("/var/antctl/services/antnode1"),
4384                        OsString::from("--log-output-dest"),
4385                        OsString::from("/var/log/antnode/antnode1"),
4386                        OsString::from("--home-network"),
4387                        OsString::from("--rewards-address"),
4388                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4389                        OsString::from("evm-arbitrum-one"),
4390                    ],
4391                    autostart: false,
4392                    contents: None,
4393                    environment: None,
4394                    label: "antnode1".parse()?,
4395                    program: current_node_bin.to_path_buf(),
4396                    username: Some("ant".to_string()),
4397                    working_directory: None,
4398                    disable_restart_on_failure: true,
4399                }),
4400                eq(false),
4401            )
4402            .times(1)
4403            .returning(|_, _| Ok(()));
4404
4405        // after service restart
4406        mock_service_control
4407            .expect_start()
4408            .with(eq("antnode1"), eq(false))
4409            .times(1)
4410            .returning(|_, _| Ok(()));
4411        mock_service_control
4412            .expect_wait()
4413            .with(eq(3000))
4414            .times(1)
4415            .returning(|_| ());
4416        mock_service_control
4417            .expect_get_process_pid()
4418            .with(eq(current_node_bin.to_path_buf().clone()))
4419            .times(1)
4420            .returning(|_| Ok(100));
4421
4422        mock_rpc_client.expect_node_info().times(1).returning(|| {
4423            Ok(NodeInfo {
4424                pid: 2000,
4425                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4426                data_path: PathBuf::from("/var/antctl/services/antnode1"),
4427                log_path: PathBuf::from("/var/log/antnode/antnode1"),
4428                version: target_version.to_string(),
4429                uptime: std::time::Duration::from_secs(1), // the service was just started
4430                wallet_balance: 0,
4431            })
4432        });
4433        mock_rpc_client
4434            .expect_network_info()
4435            .times(1)
4436            .returning(|| {
4437                Ok(NetworkInfo {
4438                    connected_peers: Vec::new(),
4439                    listeners: Vec::new(),
4440                })
4441            });
4442
4443        let mut service_data = NodeServiceData {
4444            auto_restart: false,
4445            connected_peers: None,
4446            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
4447            evm_network: EvmNetwork::ArbitrumOne,
4448            home_network: true,
4449            listen_addr: None,
4450            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
4451            log_format: None,
4452            max_archived_log_files: None,
4453            max_log_files: None,
4454            metrics_port: None,
4455            network_id: None,
4456            node_ip: None,
4457            node_port: None,
4458            number: 1,
4459            peer_id: Some(PeerId::from_str(
4460                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4461            )?),
4462            peers_args: InitialPeersConfig::default(),
4463            pid: Some(1000),
4464            rewards_address: RewardsAddress::from_str(
4465                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4466            )?,
4467            reward_balance: Some(AttoTokens::zero()),
4468            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4469            antnode_path: current_node_bin.to_path_buf(),
4470            service_name: "antnode1".to_string(),
4471            status: ServiceStatus::Running,
4472            upnp: false,
4473            user: Some("ant".to_string()),
4474            user_mode: false,
4475            version: current_version.to_string(),
4476        };
4477        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4478
4479        let mut service_manager = ServiceManager::new(
4480            service,
4481            Box::new(mock_service_control),
4482            VerbosityLevel::Normal,
4483        );
4484
4485        service_manager
4486            .upgrade(UpgradeOptions {
4487                auto_restart: false,
4488                env_variables: None,
4489                force: false,
4490                start_service: true,
4491                target_bin_path: target_node_bin.to_path_buf(),
4492                target_version: Version::parse(target_version).unwrap(),
4493            })
4494            .await?;
4495
4496        assert!(service_manager.service.service_data.home_network);
4497
4498        Ok(())
4499    }
4500
4501    #[tokio::test]
4502    async fn upgrade_should_retain_custom_node_ip() -> Result<()> {
4503        let current_version = "0.1.0";
4504        let target_version = "0.2.0";
4505
4506        let tmp_data_dir = assert_fs::TempDir::new()?;
4507        let current_install_dir = tmp_data_dir.child("antnode_install");
4508        current_install_dir.create_dir_all()?;
4509
4510        let current_node_bin = current_install_dir.child("antnode");
4511        current_node_bin.write_binary(b"fake antnode binary")?;
4512        let target_node_bin = tmp_data_dir.child("antnode");
4513        target_node_bin.write_binary(b"fake antnode binary")?;
4514
4515        let mut mock_service_control = MockServiceControl::new();
4516        let mut mock_rpc_client = MockRpcClient::new();
4517
4518        // before binary upgrade
4519        mock_service_control
4520            .expect_get_process_pid()
4521            .with(eq(current_node_bin.to_path_buf().clone()))
4522            .times(1)
4523            .returning(|_| Ok(1000));
4524        mock_service_control
4525            .expect_stop()
4526            .with(eq("antnode1"), eq(false))
4527            .times(1)
4528            .returning(|_, _| Ok(()));
4529
4530        // after binary upgrade
4531        mock_service_control
4532            .expect_uninstall()
4533            .with(eq("antnode1"), eq(false))
4534            .times(1)
4535            .returning(|_, _| Ok(()));
4536        mock_service_control
4537            .expect_install()
4538            .with(
4539                eq(ServiceInstallCtx {
4540                    args: vec![
4541                        OsString::from("--rpc"),
4542                        OsString::from("127.0.0.1:8081"),
4543                        OsString::from("--root-dir"),
4544                        OsString::from("/var/antctl/services/antnode1"),
4545                        OsString::from("--log-output-dest"),
4546                        OsString::from("/var/log/antnode/antnode1"),
4547                        OsString::from("--ip"),
4548                        OsString::from("192.168.1.1"),
4549                        OsString::from("--rewards-address"),
4550                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4551                        OsString::from("evm-arbitrum-one"),
4552                    ],
4553                    autostart: false,
4554                    contents: None,
4555                    environment: None,
4556                    label: "antnode1".parse()?,
4557                    program: current_node_bin.to_path_buf(),
4558                    username: Some("ant".to_string()),
4559                    working_directory: None,
4560                    disable_restart_on_failure: true,
4561                }),
4562                eq(false),
4563            )
4564            .times(1)
4565            .returning(|_, _| Ok(()));
4566
4567        // after service restart
4568        mock_service_control
4569            .expect_start()
4570            .with(eq("antnode1"), eq(false))
4571            .times(1)
4572            .returning(|_, _| Ok(()));
4573        mock_service_control
4574            .expect_wait()
4575            .with(eq(3000))
4576            .times(1)
4577            .returning(|_| ());
4578        mock_service_control
4579            .expect_get_process_pid()
4580            .with(eq(current_node_bin.to_path_buf().clone()))
4581            .times(1)
4582            .returning(|_| Ok(100));
4583
4584        mock_rpc_client.expect_node_info().times(1).returning(|| {
4585            Ok(NodeInfo {
4586                pid: 2000,
4587                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4588                data_path: PathBuf::from("/var/antctl/services/antnode1"),
4589                log_path: PathBuf::from("/var/log/antnode/antnode1"),
4590                version: target_version.to_string(),
4591                uptime: std::time::Duration::from_secs(1), // the service was just started
4592                wallet_balance: 0,
4593            })
4594        });
4595        mock_rpc_client
4596            .expect_network_info()
4597            .times(1)
4598            .returning(|| {
4599                Ok(NetworkInfo {
4600                    connected_peers: Vec::new(),
4601                    listeners: Vec::new(),
4602                })
4603            });
4604
4605        let mut service_data = NodeServiceData {
4606            auto_restart: false,
4607            connected_peers: None,
4608            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
4609            evm_network: EvmNetwork::ArbitrumOne,
4610            home_network: false,
4611            listen_addr: None,
4612            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
4613            log_format: None,
4614            max_archived_log_files: None,
4615            max_log_files: None,
4616            metrics_port: None,
4617            network_id: None,
4618            number: 1,
4619            node_ip: Some(Ipv4Addr::new(192, 168, 1, 1)),
4620            node_port: None,
4621            peer_id: Some(PeerId::from_str(
4622                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4623            )?),
4624            peers_args: InitialPeersConfig::default(),
4625            pid: Some(1000),
4626            rewards_address: RewardsAddress::from_str(
4627                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4628            )?,
4629            reward_balance: Some(AttoTokens::zero()),
4630            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4631            antnode_path: current_node_bin.to_path_buf(),
4632            service_name: "antnode1".to_string(),
4633            status: ServiceStatus::Running,
4634            upnp: false,
4635            user: Some("ant".to_string()),
4636            user_mode: false,
4637            version: current_version.to_string(),
4638        };
4639        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4640
4641        let mut service_manager = ServiceManager::new(
4642            service,
4643            Box::new(mock_service_control),
4644            VerbosityLevel::Normal,
4645        );
4646
4647        service_manager
4648            .upgrade(UpgradeOptions {
4649                auto_restart: false,
4650                env_variables: None,
4651                force: false,
4652                start_service: true,
4653                target_bin_path: target_node_bin.to_path_buf(),
4654                target_version: Version::parse(target_version).unwrap(),
4655            })
4656            .await?;
4657
4658        assert_eq!(
4659            service_manager.service.service_data.node_ip,
4660            Some(Ipv4Addr::new(192, 168, 1, 1))
4661        );
4662
4663        Ok(())
4664    }
4665
4666    #[tokio::test]
4667    async fn upgrade_should_retain_custom_node_ports() -> Result<()> {
4668        let current_version = "0.1.0";
4669        let target_version = "0.2.0";
4670
4671        let tmp_data_dir = assert_fs::TempDir::new()?;
4672        let current_install_dir = tmp_data_dir.child("antnode_install");
4673        current_install_dir.create_dir_all()?;
4674
4675        let current_node_bin = current_install_dir.child("antnode");
4676        current_node_bin.write_binary(b"fake antnode binary")?;
4677        let target_node_bin = tmp_data_dir.child("antnode");
4678        target_node_bin.write_binary(b"fake antnode binary")?;
4679
4680        let mut mock_service_control = MockServiceControl::new();
4681        let mut mock_rpc_client = MockRpcClient::new();
4682
4683        // before binary upgrade
4684        mock_service_control
4685            .expect_get_process_pid()
4686            .with(eq(current_node_bin.to_path_buf().clone()))
4687            .times(1)
4688            .returning(|_| Ok(1000));
4689        mock_service_control
4690            .expect_stop()
4691            .with(eq("antnode1"), eq(false))
4692            .times(1)
4693            .returning(|_, _| Ok(()));
4694
4695        // after binary upgrade
4696        mock_service_control
4697            .expect_uninstall()
4698            .with(eq("antnode1"), eq(false))
4699            .times(1)
4700            .returning(|_, _| Ok(()));
4701        mock_service_control
4702            .expect_install()
4703            .with(
4704                eq(ServiceInstallCtx {
4705                    args: vec![
4706                        OsString::from("--rpc"),
4707                        OsString::from("127.0.0.1:8081"),
4708                        OsString::from("--root-dir"),
4709                        OsString::from("/var/antctl/services/antnode1"),
4710                        OsString::from("--log-output-dest"),
4711                        OsString::from("/var/log/antnode/antnode1"),
4712                        OsString::from("--port"),
4713                        OsString::from("12000"),
4714                        OsString::from("--rewards-address"),
4715                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4716                        OsString::from("evm-arbitrum-one"),
4717                    ],
4718                    autostart: false,
4719                    contents: None,
4720                    environment: None,
4721                    label: "antnode1".parse()?,
4722                    program: current_node_bin.to_path_buf(),
4723                    username: Some("ant".to_string()),
4724                    working_directory: None,
4725                    disable_restart_on_failure: true,
4726                }),
4727                eq(false),
4728            )
4729            .times(1)
4730            .returning(|_, _| Ok(()));
4731
4732        // after service restart
4733        mock_service_control
4734            .expect_start()
4735            .with(eq("antnode1"), eq(false))
4736            .times(1)
4737            .returning(|_, _| Ok(()));
4738        mock_service_control
4739            .expect_wait()
4740            .with(eq(3000))
4741            .times(1)
4742            .returning(|_| ());
4743        mock_service_control
4744            .expect_get_process_pid()
4745            .with(eq(current_node_bin.to_path_buf().clone()))
4746            .times(1)
4747            .returning(|_| Ok(100));
4748
4749        mock_rpc_client.expect_node_info().times(1).returning(|| {
4750            Ok(NodeInfo {
4751                pid: 2000,
4752                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4753                data_path: PathBuf::from("/var/antctl/services/antnode1"),
4754                log_path: PathBuf::from("/var/log/antnode/antnode1"),
4755                version: target_version.to_string(),
4756                uptime: std::time::Duration::from_secs(1), // the service was just started
4757                wallet_balance: 0,
4758            })
4759        });
4760        mock_rpc_client
4761            .expect_network_info()
4762            .times(1)
4763            .returning(|| {
4764                Ok(NetworkInfo {
4765                    connected_peers: Vec::new(),
4766                    listeners: Vec::new(),
4767                })
4768            });
4769
4770        let mut service_data = NodeServiceData {
4771            auto_restart: false,
4772            connected_peers: None,
4773            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
4774            evm_network: EvmNetwork::ArbitrumOne,
4775            home_network: false,
4776            listen_addr: None,
4777            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
4778            log_format: None,
4779            max_archived_log_files: None,
4780            max_log_files: None,
4781            metrics_port: None,
4782            network_id: None,
4783            number: 1,
4784            node_ip: None,
4785            node_port: Some(12000),
4786            peer_id: Some(PeerId::from_str(
4787                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4788            )?),
4789            peers_args: InitialPeersConfig::default(),
4790            pid: Some(1000),
4791            rewards_address: RewardsAddress::from_str(
4792                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4793            )?,
4794            reward_balance: Some(AttoTokens::zero()),
4795            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4796            antnode_path: current_node_bin.to_path_buf(),
4797            service_name: "antnode1".to_string(),
4798            status: ServiceStatus::Running,
4799            upnp: false,
4800            user: Some("ant".to_string()),
4801            user_mode: false,
4802            version: current_version.to_string(),
4803        };
4804        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4805
4806        let mut service_manager = ServiceManager::new(
4807            service,
4808            Box::new(mock_service_control),
4809            VerbosityLevel::Normal,
4810        );
4811
4812        service_manager
4813            .upgrade(UpgradeOptions {
4814                auto_restart: false,
4815                env_variables: None,
4816                force: false,
4817                start_service: true,
4818                target_bin_path: target_node_bin.to_path_buf(),
4819                target_version: Version::parse(target_version).unwrap(),
4820            })
4821            .await?;
4822
4823        assert_eq!(service_manager.service.service_data.node_port, Some(12000));
4824
4825        Ok(())
4826    }
4827
4828    #[tokio::test]
4829    async fn upgrade_should_retain_max_archived_log_files() -> Result<()> {
4830        let current_version = "0.1.0";
4831        let target_version = "0.2.0";
4832
4833        let tmp_data_dir = assert_fs::TempDir::new()?;
4834        let current_install_dir = tmp_data_dir.child("antnode_install");
4835        current_install_dir.create_dir_all()?;
4836
4837        let current_node_bin = current_install_dir.child("antnode");
4838        current_node_bin.write_binary(b"fake antnode binary")?;
4839        let target_node_bin = tmp_data_dir.child("antnode");
4840        target_node_bin.write_binary(b"fake antnode binary")?;
4841
4842        let mut mock_service_control = MockServiceControl::new();
4843        let mut mock_rpc_client = MockRpcClient::new();
4844
4845        // before binary upgrade
4846        mock_service_control
4847            .expect_get_process_pid()
4848            .with(eq(current_node_bin.to_path_buf().clone()))
4849            .times(1)
4850            .returning(|_| Ok(1000));
4851        mock_service_control
4852            .expect_stop()
4853            .with(eq("antnode1"), eq(false))
4854            .times(1)
4855            .returning(|_, _| Ok(()));
4856
4857        // after binary upgrade
4858        mock_service_control
4859            .expect_uninstall()
4860            .with(eq("antnode1"), eq(false))
4861            .times(1)
4862            .returning(|_, _| Ok(()));
4863        mock_service_control
4864            .expect_install()
4865            .with(
4866                eq(ServiceInstallCtx {
4867                    args: vec![
4868                        OsString::from("--rpc"),
4869                        OsString::from("127.0.0.1:8081"),
4870                        OsString::from("--root-dir"),
4871                        OsString::from("/var/antctl/services/antnode1"),
4872                        OsString::from("--log-output-dest"),
4873                        OsString::from("/var/log/antnode/antnode1"),
4874                        OsString::from("--max-archived-log-files"),
4875                        OsString::from("20"),
4876                        OsString::from("--rewards-address"),
4877                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
4878                        OsString::from("evm-arbitrum-one"),
4879                    ],
4880                    autostart: false,
4881                    contents: None,
4882                    environment: None,
4883                    label: "antnode1".parse()?,
4884                    program: current_node_bin.to_path_buf(),
4885                    username: Some("ant".to_string()),
4886                    working_directory: None,
4887                    disable_restart_on_failure: true,
4888                }),
4889                eq(false),
4890            )
4891            .times(1)
4892            .returning(|_, _| Ok(()));
4893
4894        // after service restart
4895        mock_service_control
4896            .expect_start()
4897            .with(eq("antnode1"), eq(false))
4898            .times(1)
4899            .returning(|_, _| Ok(()));
4900        mock_service_control
4901            .expect_wait()
4902            .with(eq(3000))
4903            .times(1)
4904            .returning(|_| ());
4905        mock_service_control
4906            .expect_get_process_pid()
4907            .with(eq(current_node_bin.to_path_buf().clone()))
4908            .times(1)
4909            .returning(|_| Ok(100));
4910
4911        mock_rpc_client.expect_node_info().times(1).returning(|| {
4912            Ok(NodeInfo {
4913                pid: 2000,
4914                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
4915                data_path: PathBuf::from("/var/antctl/services/antnode1"),
4916                log_path: PathBuf::from("/var/log/antnode/antnode1"),
4917                version: target_version.to_string(),
4918                uptime: std::time::Duration::from_secs(1), // the service was just started
4919                wallet_balance: 0,
4920            })
4921        });
4922        mock_rpc_client
4923            .expect_network_info()
4924            .times(1)
4925            .returning(|| {
4926                Ok(NetworkInfo {
4927                    connected_peers: Vec::new(),
4928                    listeners: Vec::new(),
4929                })
4930            });
4931
4932        let mut service_data = NodeServiceData {
4933            auto_restart: false,
4934            connected_peers: None,
4935            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
4936            home_network: false,
4937            listen_addr: None,
4938            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
4939            log_format: None,
4940            max_archived_log_files: Some(20),
4941            max_log_files: None,
4942            metrics_port: None,
4943            network_id: None,
4944            node_ip: None,
4945            node_port: None,
4946            number: 1,
4947            peer_id: Some(PeerId::from_str(
4948                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
4949            )?),
4950            peers_args: InitialPeersConfig::default(),
4951            pid: Some(1000),
4952            reward_balance: Some(AttoTokens::zero()),
4953            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
4954            antnode_path: current_node_bin.to_path_buf(),
4955            service_name: "antnode1".to_string(),
4956            status: ServiceStatus::Running,
4957            upnp: false,
4958            user: Some("ant".to_string()),
4959            user_mode: false,
4960            version: current_version.to_string(),
4961            evm_network: EvmNetwork::ArbitrumOne,
4962            rewards_address: RewardsAddress::from_str(
4963                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
4964            )?,
4965        };
4966        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
4967
4968        let mut service_manager = ServiceManager::new(
4969            service,
4970            Box::new(mock_service_control),
4971            VerbosityLevel::Normal,
4972        );
4973
4974        service_manager
4975            .upgrade(UpgradeOptions {
4976                auto_restart: false,
4977                env_variables: None,
4978                force: false,
4979                start_service: true,
4980                target_bin_path: target_node_bin.to_path_buf(),
4981                target_version: Version::parse(target_version).unwrap(),
4982            })
4983            .await?;
4984
4985        assert_matches!(
4986            service_manager.service.service_data.max_archived_log_files,
4987            Some(20)
4988        );
4989
4990        Ok(())
4991    }
4992
4993    #[tokio::test]
4994    async fn upgrade_should_retain_max_log_files() -> Result<()> {
4995        let current_version = "0.1.0";
4996        let target_version = "0.2.0";
4997
4998        let tmp_data_dir = assert_fs::TempDir::new()?;
4999        let current_install_dir = tmp_data_dir.child("antnode_install");
5000        current_install_dir.create_dir_all()?;
5001
5002        let current_node_bin = current_install_dir.child("antnode");
5003        current_node_bin.write_binary(b"fake antnode binary")?;
5004        let target_node_bin = tmp_data_dir.child("antnode");
5005        target_node_bin.write_binary(b"fake antnode binary")?;
5006
5007        let mut mock_service_control = MockServiceControl::new();
5008        let mut mock_rpc_client = MockRpcClient::new();
5009
5010        // before binary upgrade
5011        mock_service_control
5012            .expect_get_process_pid()
5013            .with(eq(current_node_bin.to_path_buf().clone()))
5014            .times(1)
5015            .returning(|_| Ok(1000));
5016        mock_service_control
5017            .expect_stop()
5018            .with(eq("antnode1"), eq(false))
5019            .times(1)
5020            .returning(|_, _| Ok(()));
5021
5022        // after binary upgrade
5023        mock_service_control
5024            .expect_uninstall()
5025            .with(eq("antnode1"), eq(false))
5026            .times(1)
5027            .returning(|_, _| Ok(()));
5028        mock_service_control
5029            .expect_install()
5030            .with(
5031                eq(ServiceInstallCtx {
5032                    args: vec![
5033                        OsString::from("--rpc"),
5034                        OsString::from("127.0.0.1:8081"),
5035                        OsString::from("--root-dir"),
5036                        OsString::from("/var/antctl/services/antnode1"),
5037                        OsString::from("--log-output-dest"),
5038                        OsString::from("/var/log/antnode/antnode1"),
5039                        OsString::from("--max-log-files"),
5040                        OsString::from("20"),
5041                        OsString::from("--rewards-address"),
5042                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
5043                        OsString::from("evm-arbitrum-one"),
5044                    ],
5045                    autostart: false,
5046                    contents: None,
5047                    environment: None,
5048                    label: "antnode1".parse()?,
5049                    program: current_node_bin.to_path_buf(),
5050                    username: Some("ant".to_string()),
5051                    working_directory: None,
5052                    disable_restart_on_failure: true,
5053                }),
5054                eq(false),
5055            )
5056            .times(1)
5057            .returning(|_, _| Ok(()));
5058
5059        // after service restart
5060        mock_service_control
5061            .expect_start()
5062            .with(eq("antnode1"), eq(false))
5063            .times(1)
5064            .returning(|_, _| Ok(()));
5065        mock_service_control
5066            .expect_wait()
5067            .with(eq(3000))
5068            .times(1)
5069            .returning(|_| ());
5070        mock_service_control
5071            .expect_get_process_pid()
5072            .with(eq(current_node_bin.to_path_buf().clone()))
5073            .times(1)
5074            .returning(|_| Ok(100));
5075
5076        mock_rpc_client.expect_node_info().times(1).returning(|| {
5077            Ok(NodeInfo {
5078                pid: 2000,
5079                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
5080                data_path: PathBuf::from("/var/antctl/services/antnode1"),
5081                log_path: PathBuf::from("/var/log/antnode/antnode1"),
5082                version: target_version.to_string(),
5083                uptime: std::time::Duration::from_secs(1), // the service was just started
5084                wallet_balance: 0,
5085            })
5086        });
5087        mock_rpc_client
5088            .expect_network_info()
5089            .times(1)
5090            .returning(|| {
5091                Ok(NetworkInfo {
5092                    connected_peers: Vec::new(),
5093                    listeners: Vec::new(),
5094                })
5095            });
5096
5097        let mut service_data = NodeServiceData {
5098            auto_restart: false,
5099            connected_peers: None,
5100            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
5101            home_network: false,
5102            listen_addr: None,
5103            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
5104            log_format: None,
5105            max_archived_log_files: None,
5106            max_log_files: Some(20),
5107            metrics_port: None,
5108            network_id: None,
5109            node_ip: None,
5110            node_port: None,
5111            number: 1,
5112            peer_id: Some(PeerId::from_str(
5113                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5114            )?),
5115            peers_args: InitialPeersConfig::default(),
5116            pid: Some(1000),
5117            reward_balance: Some(AttoTokens::zero()),
5118            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5119            antnode_path: current_node_bin.to_path_buf(),
5120            service_name: "antnode1".to_string(),
5121            status: ServiceStatus::Running,
5122            upnp: false,
5123            user: Some("ant".to_string()),
5124            user_mode: false,
5125            version: current_version.to_string(),
5126            evm_network: EvmNetwork::ArbitrumOne,
5127            rewards_address: RewardsAddress::from_str(
5128                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5129            )?,
5130        };
5131        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
5132
5133        let mut service_manager = ServiceManager::new(
5134            service,
5135            Box::new(mock_service_control),
5136            VerbosityLevel::Normal,
5137        );
5138
5139        service_manager
5140            .upgrade(UpgradeOptions {
5141                auto_restart: false,
5142                env_variables: None,
5143                force: false,
5144                start_service: true,
5145                target_bin_path: target_node_bin.to_path_buf(),
5146                target_version: Version::parse(target_version).unwrap(),
5147            })
5148            .await?;
5149
5150        assert_matches!(service_manager.service.service_data.max_log_files, Some(20));
5151
5152        Ok(())
5153    }
5154
5155    #[tokio::test]
5156    async fn upgrade_should_retain_custom_metrics_ports() -> Result<()> {
5157        let current_version = "0.1.0";
5158        let target_version = "0.2.0";
5159
5160        let tmp_data_dir = assert_fs::TempDir::new()?;
5161        let current_install_dir = tmp_data_dir.child("antnode_install");
5162        current_install_dir.create_dir_all()?;
5163
5164        let current_node_bin = current_install_dir.child("antnode");
5165        current_node_bin.write_binary(b"fake antnode binary")?;
5166        let target_node_bin = tmp_data_dir.child("antnode");
5167        target_node_bin.write_binary(b"fake antnode binary")?;
5168
5169        let mut mock_service_control = MockServiceControl::new();
5170        let mut mock_rpc_client = MockRpcClient::new();
5171
5172        // before binary upgrade
5173        mock_service_control
5174            .expect_get_process_pid()
5175            .with(eq(current_node_bin.to_path_buf().clone()))
5176            .times(1)
5177            .returning(|_| Ok(1000));
5178        mock_service_control
5179            .expect_stop()
5180            .with(eq("antnode1"), eq(false))
5181            .times(1)
5182            .returning(|_, _| Ok(()));
5183
5184        // after binary upgrade
5185        mock_service_control
5186            .expect_uninstall()
5187            .with(eq("antnode1"), eq(false))
5188            .times(1)
5189            .returning(|_, _| Ok(()));
5190        mock_service_control
5191            .expect_install()
5192            .with(
5193                eq(ServiceInstallCtx {
5194                    args: vec![
5195                        OsString::from("--rpc"),
5196                        OsString::from("127.0.0.1:8081"),
5197                        OsString::from("--root-dir"),
5198                        OsString::from("/var/antctl/services/antnode1"),
5199                        OsString::from("--log-output-dest"),
5200                        OsString::from("/var/log/antnode/antnode1"),
5201                        OsString::from("--metrics-server-port"),
5202                        OsString::from("12000"),
5203                        OsString::from("--rewards-address"),
5204                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
5205                        OsString::from("evm-arbitrum-one"),
5206                    ],
5207                    autostart: false,
5208                    contents: None,
5209                    environment: None,
5210                    label: "antnode1".parse()?,
5211                    program: current_node_bin.to_path_buf(),
5212                    username: Some("ant".to_string()),
5213                    working_directory: None,
5214                    disable_restart_on_failure: true,
5215                }),
5216                eq(false),
5217            )
5218            .times(1)
5219            .returning(|_, _| Ok(()));
5220
5221        // after service restart
5222        mock_service_control
5223            .expect_start()
5224            .with(eq("antnode1"), eq(false))
5225            .times(1)
5226            .returning(|_, _| Ok(()));
5227        mock_service_control
5228            .expect_wait()
5229            .with(eq(3000))
5230            .times(1)
5231            .returning(|_| ());
5232        mock_service_control
5233            .expect_get_process_pid()
5234            .with(eq(current_node_bin.to_path_buf().clone()))
5235            .times(1)
5236            .returning(|_| Ok(100));
5237
5238        mock_rpc_client.expect_node_info().times(1).returning(|| {
5239            Ok(NodeInfo {
5240                pid: 2000,
5241                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
5242                data_path: PathBuf::from("/var/antctl/services/antnode1"),
5243                log_path: PathBuf::from("/var/log/antnode/antnode1"),
5244                version: target_version.to_string(),
5245                uptime: std::time::Duration::from_secs(1), // the service was just started
5246                wallet_balance: 0,
5247            })
5248        });
5249        mock_rpc_client
5250            .expect_network_info()
5251            .times(1)
5252            .returning(|| {
5253                Ok(NetworkInfo {
5254                    connected_peers: Vec::new(),
5255                    listeners: Vec::new(),
5256                })
5257            });
5258
5259        let mut service_data = NodeServiceData {
5260            auto_restart: false,
5261            connected_peers: None,
5262            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
5263            evm_network: EvmNetwork::ArbitrumOne,
5264            home_network: false,
5265            listen_addr: None,
5266            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
5267            log_format: None,
5268            max_archived_log_files: None,
5269            max_log_files: None,
5270            metrics_port: Some(12000),
5271            network_id: None,
5272            node_ip: None,
5273            node_port: None,
5274            number: 1,
5275            peer_id: Some(PeerId::from_str(
5276                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5277            )?),
5278            peers_args: InitialPeersConfig::default(),
5279            pid: Some(1000),
5280            rewards_address: RewardsAddress::from_str(
5281                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5282            )?,
5283            reward_balance: Some(AttoTokens::zero()),
5284            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5285            antnode_path: current_node_bin.to_path_buf(),
5286            service_name: "antnode1".to_string(),
5287            status: ServiceStatus::Running,
5288            upnp: false,
5289            user: Some("ant".to_string()),
5290            user_mode: false,
5291            version: current_version.to_string(),
5292        };
5293        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
5294
5295        let mut service_manager = ServiceManager::new(
5296            service,
5297            Box::new(mock_service_control),
5298            VerbosityLevel::Normal,
5299        );
5300
5301        service_manager
5302            .upgrade(UpgradeOptions {
5303                auto_restart: false,
5304                env_variables: None,
5305                force: false,
5306                start_service: true,
5307                target_bin_path: target_node_bin.to_path_buf(),
5308                target_version: Version::parse(target_version).unwrap(),
5309            })
5310            .await?;
5311
5312        assert_eq!(
5313            service_manager.service.service_data.metrics_port,
5314            Some(12000)
5315        );
5316
5317        Ok(())
5318    }
5319
5320    #[tokio::test]
5321    async fn upgrade_should_retain_custom_rpc_ports() -> Result<()> {
5322        let current_version = "0.1.0";
5323        let target_version = "0.2.0";
5324
5325        let tmp_data_dir = assert_fs::TempDir::new()?;
5326        let current_install_dir = tmp_data_dir.child("antnode_install");
5327        current_install_dir.create_dir_all()?;
5328
5329        let current_node_bin = current_install_dir.child("antnode");
5330        current_node_bin.write_binary(b"fake antnode binary")?;
5331        let target_node_bin = tmp_data_dir.child("antnode");
5332        target_node_bin.write_binary(b"fake antnode binary")?;
5333
5334        let mut mock_service_control = MockServiceControl::new();
5335        let mut mock_rpc_client = MockRpcClient::new();
5336
5337        // before binary upgrade
5338        mock_service_control
5339            .expect_get_process_pid()
5340            .with(eq(current_node_bin.to_path_buf().clone()))
5341            .times(1)
5342            .returning(|_| Ok(1000));
5343        mock_service_control
5344            .expect_stop()
5345            .with(eq("antnode1"), eq(false))
5346            .times(1)
5347            .returning(|_, _| Ok(()));
5348
5349        // after binary upgrade
5350        mock_service_control
5351            .expect_uninstall()
5352            .with(eq("antnode1"), eq(false))
5353            .times(1)
5354            .returning(|_, _| Ok(()));
5355        mock_service_control
5356            .expect_install()
5357            .with(
5358                eq(ServiceInstallCtx {
5359                    args: vec![
5360                        OsString::from("--rpc"),
5361                        OsString::from("127.0.0.1:8081"),
5362                        OsString::from("--root-dir"),
5363                        OsString::from("/var/antctl/services/antnode1"),
5364                        OsString::from("--log-output-dest"),
5365                        OsString::from("/var/log/antnode/antnode1"),
5366                        OsString::from("--metrics-server-port"),
5367                        OsString::from("12000"),
5368                        OsString::from("--rewards-address"),
5369                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
5370                        OsString::from("evm-arbitrum-one"),
5371                    ],
5372                    autostart: false,
5373                    contents: None,
5374                    environment: None,
5375                    label: "antnode1".parse()?,
5376                    program: current_node_bin.to_path_buf(),
5377                    username: Some("ant".to_string()),
5378                    working_directory: None,
5379                    disable_restart_on_failure: true,
5380                }),
5381                eq(false),
5382            )
5383            .times(1)
5384            .returning(|_, _| Ok(()));
5385
5386        // after service restart
5387        mock_service_control
5388            .expect_start()
5389            .with(eq("antnode1"), eq(false))
5390            .times(1)
5391            .returning(|_, _| Ok(()));
5392        mock_service_control
5393            .expect_wait()
5394            .with(eq(3000))
5395            .times(1)
5396            .returning(|_| ());
5397        mock_service_control
5398            .expect_get_process_pid()
5399            .with(eq(current_node_bin.to_path_buf().clone()))
5400            .times(1)
5401            .returning(|_| Ok(100));
5402
5403        mock_rpc_client.expect_node_info().times(1).returning(|| {
5404            Ok(NodeInfo {
5405                pid: 2000,
5406                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
5407                data_path: PathBuf::from("/var/antctl/services/antnode1"),
5408                log_path: PathBuf::from("/var/log/antnode/antnode1"),
5409                version: target_version.to_string(),
5410                uptime: std::time::Duration::from_secs(1), // the service was just started
5411                wallet_balance: 0,
5412            })
5413        });
5414        mock_rpc_client
5415            .expect_network_info()
5416            .times(1)
5417            .returning(|| {
5418                Ok(NetworkInfo {
5419                    connected_peers: Vec::new(),
5420                    listeners: Vec::new(),
5421                })
5422            });
5423
5424        let mut service_data = NodeServiceData {
5425            auto_restart: false,
5426            connected_peers: None,
5427            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
5428            evm_network: EvmNetwork::ArbitrumOne,
5429            home_network: false,
5430            listen_addr: None,
5431            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
5432            log_format: None,
5433            max_archived_log_files: None,
5434            max_log_files: None,
5435            metrics_port: Some(12000),
5436            network_id: None,
5437            node_ip: None,
5438            node_port: None,
5439            number: 1,
5440            peer_id: Some(PeerId::from_str(
5441                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5442            )?),
5443            peers_args: InitialPeersConfig::default(),
5444            pid: Some(1000),
5445            rewards_address: RewardsAddress::from_str(
5446                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5447            )?,
5448            reward_balance: Some(AttoTokens::zero()),
5449            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5450            antnode_path: current_node_bin.to_path_buf(),
5451            service_name: "antnode1".to_string(),
5452            status: ServiceStatus::Running,
5453            upnp: false,
5454            user: Some("ant".to_string()),
5455            user_mode: false,
5456            version: current_version.to_string(),
5457        };
5458        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
5459
5460        let mut service_manager = ServiceManager::new(
5461            service,
5462            Box::new(mock_service_control),
5463            VerbosityLevel::Normal,
5464        );
5465
5466        service_manager
5467            .upgrade(UpgradeOptions {
5468                auto_restart: false,
5469                env_variables: None,
5470                force: false,
5471                start_service: true,
5472                target_bin_path: target_node_bin.to_path_buf(),
5473                target_version: Version::parse(target_version).unwrap(),
5474            })
5475            .await?;
5476
5477        assert_eq!(
5478            service_manager.service.service_data.rpc_socket_addr,
5479            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081)
5480        );
5481
5482        Ok(())
5483    }
5484
5485    #[tokio::test]
5486    async fn upgrade_should_retain_auto_restart() -> Result<()> {
5487        let current_version = "0.1.0";
5488        let target_version = "0.2.0";
5489
5490        let tmp_data_dir = assert_fs::TempDir::new()?;
5491        let current_install_dir = tmp_data_dir.child("antnode_install");
5492        current_install_dir.create_dir_all()?;
5493
5494        let current_node_bin = current_install_dir.child("antnode");
5495        current_node_bin.write_binary(b"fake antnode binary")?;
5496        let target_node_bin = tmp_data_dir.child("antnode");
5497        target_node_bin.write_binary(b"fake antnode binary")?;
5498
5499        let mut mock_service_control = MockServiceControl::new();
5500        let mut mock_rpc_client = MockRpcClient::new();
5501
5502        // before binary upgrade
5503        mock_service_control
5504            .expect_get_process_pid()
5505            .with(eq(current_node_bin.to_path_buf().clone()))
5506            .times(1)
5507            .returning(|_| Ok(1000));
5508        mock_service_control
5509            .expect_stop()
5510            .with(eq("antnode1"), eq(false))
5511            .times(1)
5512            .returning(|_, _| Ok(()));
5513
5514        // after binary upgrade
5515        mock_service_control
5516            .expect_uninstall()
5517            .with(eq("antnode1"), eq(false))
5518            .times(1)
5519            .returning(|_, _| Ok(()));
5520        mock_service_control
5521            .expect_install()
5522            .with(
5523                eq(ServiceInstallCtx {
5524                    args: vec![
5525                        OsString::from("--rpc"),
5526                        OsString::from("127.0.0.1:8081"),
5527                        OsString::from("--root-dir"),
5528                        OsString::from("/var/antctl/services/antnode1"),
5529                        OsString::from("--log-output-dest"),
5530                        OsString::from("/var/log/antnode/antnode1"),
5531                        OsString::from("--rewards-address"),
5532                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
5533                        OsString::from("evm-arbitrum-one"),
5534                    ],
5535                    autostart: true,
5536                    contents: None,
5537                    environment: None,
5538                    label: "antnode1".parse()?,
5539                    program: current_node_bin.to_path_buf(),
5540                    username: Some("ant".to_string()),
5541                    working_directory: None,
5542                    disable_restart_on_failure: true,
5543                }),
5544                eq(false),
5545            )
5546            .times(1)
5547            .returning(|_, _| Ok(()));
5548
5549        // after service restart
5550        mock_service_control
5551            .expect_start()
5552            .with(eq("antnode1"), eq(false))
5553            .times(1)
5554            .returning(|_, _| Ok(()));
5555        mock_service_control
5556            .expect_wait()
5557            .with(eq(3000))
5558            .times(1)
5559            .returning(|_| ());
5560        mock_service_control
5561            .expect_get_process_pid()
5562            .with(eq(current_node_bin.to_path_buf().clone()))
5563            .times(1)
5564            .returning(|_| Ok(100));
5565
5566        mock_rpc_client.expect_node_info().times(1).returning(|| {
5567            Ok(NodeInfo {
5568                pid: 2000,
5569                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
5570                data_path: PathBuf::from("/var/antctl/services/antnode1"),
5571                log_path: PathBuf::from("/var/log/antnode/antnode1"),
5572                version: target_version.to_string(),
5573                uptime: std::time::Duration::from_secs(1), // the service was just started
5574                wallet_balance: 0,
5575            })
5576        });
5577        mock_rpc_client
5578            .expect_network_info()
5579            .times(1)
5580            .returning(|| {
5581                Ok(NetworkInfo {
5582                    connected_peers: Vec::new(),
5583                    listeners: Vec::new(),
5584                })
5585            });
5586
5587        let mut service_data = NodeServiceData {
5588            auto_restart: true,
5589            connected_peers: None,
5590            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
5591            evm_network: EvmNetwork::ArbitrumOne,
5592            home_network: false,
5593            listen_addr: None,
5594            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
5595            log_format: None,
5596            max_archived_log_files: None,
5597            max_log_files: None,
5598            metrics_port: None,
5599            network_id: None,
5600            node_ip: None,
5601            node_port: None,
5602            number: 1,
5603            peer_id: Some(PeerId::from_str(
5604                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5605            )?),
5606            peers_args: InitialPeersConfig::default(),
5607            pid: Some(1000),
5608            rewards_address: RewardsAddress::from_str(
5609                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5610            )?,
5611            reward_balance: Some(AttoTokens::zero()),
5612            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5613            antnode_path: current_node_bin.to_path_buf(),
5614            service_name: "antnode1".to_string(),
5615            status: ServiceStatus::Running,
5616            upnp: false,
5617            user: Some("ant".to_string()),
5618            user_mode: false,
5619            version: current_version.to_string(),
5620        };
5621        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
5622
5623        let mut service_manager = ServiceManager::new(
5624            service,
5625            Box::new(mock_service_control),
5626            VerbosityLevel::Normal,
5627        );
5628
5629        service_manager
5630            .upgrade(UpgradeOptions {
5631                auto_restart: true,
5632                env_variables: None,
5633                force: false,
5634                start_service: true,
5635                target_bin_path: target_node_bin.to_path_buf(),
5636                target_version: Version::parse(target_version).unwrap(),
5637            })
5638            .await?;
5639
5640        assert!(service_manager.service.service_data.auto_restart,);
5641
5642        Ok(())
5643    }
5644
5645    #[tokio::test]
5646    async fn upgrade_should_retain_evm_network_settings() -> Result<()> {
5647        let current_version = "0.1.0";
5648        let target_version = "0.2.0";
5649
5650        let tmp_data_dir = assert_fs::TempDir::new()?;
5651        let current_install_dir = tmp_data_dir.child("antnode_install");
5652        current_install_dir.create_dir_all()?;
5653
5654        let current_node_bin = current_install_dir.child("antnode");
5655        current_node_bin.write_binary(b"fake antnode binary")?;
5656        let target_node_bin = tmp_data_dir.child("antnode");
5657        target_node_bin.write_binary(b"fake antnode binary")?;
5658
5659        let mut mock_service_control = MockServiceControl::new();
5660        let mut mock_rpc_client = MockRpcClient::new();
5661
5662        // before binary upgrade
5663        mock_service_control
5664            .expect_get_process_pid()
5665            .with(eq(current_node_bin.to_path_buf().clone()))
5666            .times(1)
5667            .returning(|_| Ok(1000));
5668        mock_service_control
5669            .expect_stop()
5670            .with(eq("antnode1"), eq(false))
5671            .times(1)
5672            .returning(|_, _| Ok(()));
5673
5674        // after binary upgrade
5675        mock_service_control
5676            .expect_uninstall()
5677            .with(eq("antnode1"), eq(false))
5678            .times(1)
5679            .returning(|_, _| Ok(()));
5680        mock_service_control
5681            .expect_install()
5682            .with(
5683                eq(ServiceInstallCtx {
5684                    args: vec![
5685                        OsString::from("--rpc"),
5686                        OsString::from("127.0.0.1:8081"),
5687                        OsString::from("--root-dir"),
5688                        OsString::from("/var/antctl/services/antnode1"),
5689                        OsString::from("--log-output-dest"),
5690                        OsString::from("/var/log/antnode/antnode1"),
5691                        OsString::from("--rewards-address"),
5692                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
5693                        OsString::from("evm-custom"),
5694                        OsString::from("--rpc-url"),
5695                        OsString::from("http://localhost:8545/"),
5696                        OsString::from("--payment-token-address"),
5697                        OsString::from("0x5FbDB2315678afecb367f032d93F642f64180aa3"),
5698                        OsString::from("--data-payments-address"),
5699                        OsString::from("0x8464135c8F25Da09e49BC8782676a84730C318bC"),
5700                    ],
5701                    autostart: true,
5702                    contents: None,
5703                    environment: None,
5704                    label: "antnode1".parse()?,
5705                    program: current_node_bin.to_path_buf(),
5706                    username: Some("ant".to_string()),
5707                    working_directory: None,
5708                    disable_restart_on_failure: true,
5709                }),
5710                eq(false),
5711            )
5712            .times(1)
5713            .returning(|_, _| Ok(()));
5714
5715        // after service restart
5716        mock_service_control
5717            .expect_start()
5718            .with(eq("antnode1"), eq(false))
5719            .times(1)
5720            .returning(|_, _| Ok(()));
5721        mock_service_control
5722            .expect_wait()
5723            .with(eq(3000))
5724            .times(1)
5725            .returning(|_| ());
5726        mock_service_control
5727            .expect_get_process_pid()
5728            .with(eq(current_node_bin.to_path_buf().clone()))
5729            .times(1)
5730            .returning(|_| Ok(100));
5731
5732        mock_rpc_client.expect_node_info().times(1).returning(|| {
5733            Ok(NodeInfo {
5734                pid: 2000,
5735                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
5736                data_path: PathBuf::from("/var/antctl/services/antnode1"),
5737                log_path: PathBuf::from("/var/log/antnode/antnode1"),
5738                version: target_version.to_string(),
5739                uptime: std::time::Duration::from_secs(1), // the service was just started
5740                wallet_balance: 0,
5741            })
5742        });
5743        mock_rpc_client
5744            .expect_network_info()
5745            .times(1)
5746            .returning(|| {
5747                Ok(NetworkInfo {
5748                    connected_peers: Vec::new(),
5749                    listeners: Vec::new(),
5750                })
5751            });
5752
5753        let mut service_data = NodeServiceData {
5754            auto_restart: true,
5755            connected_peers: None,
5756            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
5757            evm_network: EvmNetwork::Custom(CustomNetwork {
5758                rpc_url_http: "http://localhost:8545".parse()?,
5759                payment_token_address: RewardsAddress::from_str(
5760                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
5761                )?,
5762                data_payments_address: RewardsAddress::from_str(
5763                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
5764                )?,
5765            }),
5766            home_network: false,
5767            listen_addr: None,
5768            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
5769            log_format: None,
5770            max_archived_log_files: None,
5771            max_log_files: None,
5772            metrics_port: None,
5773            network_id: None,
5774            node_ip: None,
5775            node_port: None,
5776            number: 1,
5777            peer_id: Some(PeerId::from_str(
5778                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5779            )?),
5780            peers_args: InitialPeersConfig::default(),
5781            pid: Some(1000),
5782            rewards_address: RewardsAddress::from_str(
5783                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5784            )?,
5785            reward_balance: Some(AttoTokens::zero()),
5786
5787            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5788            antnode_path: current_node_bin.to_path_buf(),
5789            service_name: "antnode1".to_string(),
5790            status: ServiceStatus::Running,
5791            upnp: false,
5792            user: Some("ant".to_string()),
5793            user_mode: false,
5794            version: current_version.to_string(),
5795        };
5796        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
5797
5798        let mut service_manager = ServiceManager::new(
5799            service,
5800            Box::new(mock_service_control),
5801            VerbosityLevel::Normal,
5802        );
5803
5804        service_manager
5805            .upgrade(UpgradeOptions {
5806                auto_restart: true,
5807                env_variables: None,
5808                force: false,
5809                start_service: true,
5810                target_bin_path: target_node_bin.to_path_buf(),
5811                target_version: Version::parse(target_version).unwrap(),
5812            })
5813            .await?;
5814
5815        assert!(service_manager.service.service_data.auto_restart,);
5816
5817        Ok(())
5818    }
5819
5820    #[tokio::test]
5821    async fn upgrade_should_retain_the_rewards_address() -> Result<()> {
5822        let current_version = "0.1.0";
5823        let target_version = "0.2.0";
5824
5825        let tmp_data_dir = assert_fs::TempDir::new()?;
5826        let current_install_dir = tmp_data_dir.child("antnode_install");
5827        current_install_dir.create_dir_all()?;
5828
5829        let current_node_bin = current_install_dir.child("antnode");
5830        current_node_bin.write_binary(b"fake antnode binary")?;
5831        let target_node_bin = tmp_data_dir.child("antnode");
5832        target_node_bin.write_binary(b"fake antnode binary")?;
5833
5834        let mut mock_service_control = MockServiceControl::new();
5835        let mut mock_rpc_client = MockRpcClient::new();
5836
5837        // before binary upgrade
5838        mock_service_control
5839            .expect_get_process_pid()
5840            .with(eq(current_node_bin.to_path_buf().clone()))
5841            .times(1)
5842            .returning(|_| Ok(1000));
5843        mock_service_control
5844            .expect_stop()
5845            .with(eq("antnode1"), eq(false))
5846            .times(1)
5847            .returning(|_, _| Ok(()));
5848
5849        // after binary upgrade
5850        mock_service_control
5851            .expect_uninstall()
5852            .with(eq("antnode1"), eq(false))
5853            .times(1)
5854            .returning(|_, _| Ok(()));
5855        mock_service_control
5856            .expect_install()
5857            .with(
5858                eq(ServiceInstallCtx {
5859                    args: vec![
5860                        OsString::from("--rpc"),
5861                        OsString::from("127.0.0.1:8081"),
5862                        OsString::from("--root-dir"),
5863                        OsString::from("/var/antctl/services/antnode1"),
5864                        OsString::from("--log-output-dest"),
5865                        OsString::from("/var/log/antnode/antnode1"),
5866                        OsString::from("--rewards-address"),
5867                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
5868                        OsString::from("evm-custom"),
5869                        OsString::from("--rpc-url"),
5870                        OsString::from("http://localhost:8545/"),
5871                        OsString::from("--payment-token-address"),
5872                        OsString::from("0x5FbDB2315678afecb367f032d93F642f64180aa3"),
5873                        OsString::from("--data-payments-address"),
5874                        OsString::from("0x8464135c8F25Da09e49BC8782676a84730C318bC"),
5875                    ],
5876                    autostart: true,
5877                    contents: None,
5878                    environment: None,
5879                    label: "antnode1".parse()?,
5880                    program: current_node_bin.to_path_buf(),
5881                    username: Some("ant".to_string()),
5882                    working_directory: None,
5883                    disable_restart_on_failure: true,
5884                }),
5885                eq(false),
5886            )
5887            .times(1)
5888            .returning(|_, _| Ok(()));
5889
5890        // after service restart
5891        mock_service_control
5892            .expect_start()
5893            .with(eq("antnode1"), eq(false))
5894            .times(1)
5895            .returning(|_, _| Ok(()));
5896        mock_service_control
5897            .expect_wait()
5898            .with(eq(3000))
5899            .times(1)
5900            .returning(|_| ());
5901        mock_service_control
5902            .expect_get_process_pid()
5903            .with(eq(current_node_bin.to_path_buf().clone()))
5904            .times(1)
5905            .returning(|_| Ok(100));
5906
5907        mock_rpc_client.expect_node_info().times(1).returning(|| {
5908            Ok(NodeInfo {
5909                pid: 2000,
5910                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
5911                data_path: PathBuf::from("/var/antctl/services/antnode1"),
5912                log_path: PathBuf::from("/var/log/antnode/antnode1"),
5913                version: target_version.to_string(),
5914                uptime: std::time::Duration::from_secs(1), // the service was just started
5915                wallet_balance: 0,
5916            })
5917        });
5918        mock_rpc_client
5919            .expect_network_info()
5920            .times(1)
5921            .returning(|| {
5922                Ok(NetworkInfo {
5923                    connected_peers: Vec::new(),
5924                    listeners: Vec::new(),
5925                })
5926            });
5927
5928        let mut service_data = NodeServiceData {
5929            auto_restart: true,
5930            connected_peers: None,
5931            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
5932            evm_network: EvmNetwork::Custom(CustomNetwork {
5933                rpc_url_http: "http://localhost:8545".parse()?,
5934                payment_token_address: RewardsAddress::from_str(
5935                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
5936                )?,
5937                data_payments_address: RewardsAddress::from_str(
5938                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
5939                )?,
5940            }),
5941            home_network: false,
5942            listen_addr: None,
5943            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
5944            log_format: None,
5945            max_archived_log_files: None,
5946            max_log_files: None,
5947            metrics_port: None,
5948            network_id: None,
5949            node_ip: None,
5950            node_port: None,
5951            number: 1,
5952            peer_id: Some(PeerId::from_str(
5953                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
5954            )?),
5955            peers_args: InitialPeersConfig::default(),
5956            pid: Some(1000),
5957            rewards_address: RewardsAddress::from_str(
5958                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
5959            )?,
5960            reward_balance: Some(AttoTokens::zero()),
5961
5962            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
5963            antnode_path: current_node_bin.to_path_buf(),
5964            service_name: "antnode1".to_string(),
5965            status: ServiceStatus::Running,
5966            upnp: false,
5967            user: Some("ant".to_string()),
5968            user_mode: false,
5969            version: current_version.to_string(),
5970        };
5971        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client));
5972
5973        let mut service_manager = ServiceManager::new(
5974            service,
5975            Box::new(mock_service_control),
5976            VerbosityLevel::Normal,
5977        );
5978
5979        service_manager
5980            .upgrade(UpgradeOptions {
5981                auto_restart: true,
5982                env_variables: None,
5983                force: false,
5984                start_service: true,
5985                target_bin_path: target_node_bin.to_path_buf(),
5986                target_version: Version::parse(target_version).unwrap(),
5987            })
5988            .await?;
5989
5990        assert!(service_manager.service.service_data.auto_restart,);
5991
5992        Ok(())
5993    }
5994
5995    #[tokio::test]
5996    async fn upgrade_should_use_dynamic_startup_delay_if_set() -> Result<()> {
5997        let current_version = "0.1.0";
5998        let target_version = "0.2.0";
5999
6000        let tmp_data_dir = assert_fs::TempDir::new()?;
6001        let current_install_dir = tmp_data_dir.child("antnode_install");
6002        current_install_dir.create_dir_all()?;
6003
6004        let current_node_bin = current_install_dir.child("antnode");
6005        current_node_bin.write_binary(b"fake antnode binary")?;
6006        let target_node_bin = tmp_data_dir.child("antnode");
6007        target_node_bin.write_binary(b"fake antnode binary")?;
6008
6009        let mut mock_service_control = MockServiceControl::new();
6010        let mut mock_rpc_client = MockRpcClient::new();
6011
6012        // before binary upgrade
6013        mock_service_control
6014            .expect_get_process_pid()
6015            .with(eq(current_node_bin.to_path_buf().clone()))
6016            .times(1)
6017            .returning(|_| Ok(1000));
6018        mock_service_control
6019            .expect_stop()
6020            .with(eq("antnode1"), eq(false))
6021            .times(1)
6022            .returning(|_, _| Ok(()));
6023
6024        // after binary upgrade
6025        mock_service_control
6026            .expect_uninstall()
6027            .with(eq("antnode1"), eq(false))
6028            .times(1)
6029            .returning(|_, _| Ok(()));
6030        mock_service_control
6031            .expect_install()
6032            .with(
6033                eq(ServiceInstallCtx {
6034                    args: vec![
6035                        OsString::from("--rpc"),
6036                        OsString::from("127.0.0.1:8081"),
6037                        OsString::from("--root-dir"),
6038                        OsString::from("/var/antctl/services/antnode1"),
6039                        OsString::from("--log-output-dest"),
6040                        OsString::from("/var/log/antnode/antnode1"),
6041                        OsString::from("--upnp"),
6042                        OsString::from("--rewards-address"),
6043                        OsString::from("0x03B770D9cD32077cC0bF330c13C114a87643B124"),
6044                        OsString::from("evm-arbitrum-one"),
6045                    ],
6046                    autostart: false,
6047                    contents: None,
6048                    environment: None,
6049                    label: "antnode1".parse()?,
6050                    program: current_node_bin.to_path_buf(),
6051                    username: Some("ant".to_string()),
6052                    working_directory: None,
6053                    disable_restart_on_failure: true,
6054                }),
6055                eq(false),
6056            )
6057            .times(1)
6058            .returning(|_, _| Ok(()));
6059
6060        // after service restart
6061        mock_service_control
6062            .expect_start()
6063            .with(eq("antnode1"), eq(false))
6064            .times(1)
6065            .returning(|_, _| Ok(()));
6066        mock_service_control
6067            .expect_wait()
6068            .with(eq(3000))
6069            .times(1)
6070            .returning(|_| ());
6071        mock_service_control
6072            .expect_get_process_pid()
6073            .with(eq(current_node_bin.to_path_buf().clone()))
6074            .times(1)
6075            .returning(|_| Ok(100));
6076        mock_rpc_client
6077            .expect_is_node_connected_to_network()
6078            .times(1)
6079            .returning(|_| Ok(()));
6080        mock_rpc_client.expect_node_info().times(1).returning(|| {
6081            Ok(NodeInfo {
6082                pid: 2000,
6083                peer_id: PeerId::from_str("12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR")?,
6084                data_path: PathBuf::from("/var/antctl/services/antnode1"),
6085                log_path: PathBuf::from("/var/log/antnode/antnode1"),
6086                version: target_version.to_string(),
6087                uptime: std::time::Duration::from_secs(1), // the service was just started
6088                wallet_balance: 0,
6089            })
6090        });
6091        mock_rpc_client
6092            .expect_network_info()
6093            .times(1)
6094            .returning(|| {
6095                Ok(NetworkInfo {
6096                    connected_peers: Vec::new(),
6097                    listeners: Vec::new(),
6098                })
6099            });
6100
6101        let mut service_data = NodeServiceData {
6102            auto_restart: false,
6103            connected_peers: None,
6104            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
6105            evm_network: EvmNetwork::ArbitrumOne,
6106            home_network: false,
6107            listen_addr: None,
6108            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
6109            log_format: None,
6110            max_archived_log_files: None,
6111            max_log_files: None,
6112            metrics_port: None,
6113            network_id: None,
6114            node_ip: None,
6115            node_port: None,
6116            number: 1,
6117            peer_id: Some(PeerId::from_str(
6118                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
6119            )?),
6120            peers_args: InitialPeersConfig::default(),
6121            pid: Some(1000),
6122            rewards_address: RewardsAddress::from_str(
6123                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
6124            )?,
6125            reward_balance: Some(AttoTokens::zero()),
6126            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
6127            antnode_path: current_node_bin.to_path_buf(),
6128            service_name: "antnode1".to_string(),
6129            status: ServiceStatus::Running,
6130            upnp: true,
6131            user: Some("ant".to_string()),
6132            user_mode: false,
6133            version: current_version.to_string(),
6134        };
6135        let service = NodeService::new(&mut service_data, Box::new(mock_rpc_client))
6136            .with_connection_timeout(Duration::from_secs(
6137                DEFAULT_NODE_STARTUP_CONNECTION_TIMEOUT_S,
6138            ));
6139
6140        let mut service_manager = ServiceManager::new(
6141            service,
6142            Box::new(mock_service_control),
6143            VerbosityLevel::Normal,
6144        );
6145
6146        service_manager
6147            .upgrade(UpgradeOptions {
6148                auto_restart: false,
6149                env_variables: None,
6150                force: false,
6151                start_service: true,
6152                target_bin_path: target_node_bin.to_path_buf(),
6153                target_version: Version::parse(target_version).unwrap(),
6154            })
6155            .await?;
6156
6157        Ok(())
6158    }
6159
6160    #[tokio::test]
6161    async fn remove_should_remove_an_added_node() -> Result<()> {
6162        let temp_dir = assert_fs::TempDir::new()?;
6163        let log_dir = temp_dir.child("antnode1-logs");
6164        log_dir.create_dir_all()?;
6165        let data_dir = temp_dir.child("antnode1-data");
6166        data_dir.create_dir_all()?;
6167        let antnode_bin = data_dir.child("antnode");
6168        antnode_bin.write_binary(b"fake antnode binary")?;
6169
6170        let mut mock_service_control = MockServiceControl::new();
6171        mock_service_control
6172            .expect_uninstall()
6173            .with(eq("antnode1"), eq(false))
6174            .times(1)
6175            .returning(|_, _| Ok(()));
6176
6177        let mut service_data = NodeServiceData {
6178            auto_restart: false,
6179            connected_peers: None,
6180            data_dir_path: data_dir.to_path_buf(),
6181            evm_network: EvmNetwork::Custom(CustomNetwork {
6182                rpc_url_http: "http://localhost:8545".parse()?,
6183                payment_token_address: RewardsAddress::from_str(
6184                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
6185                )?,
6186                data_payments_address: RewardsAddress::from_str(
6187                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
6188                )?,
6189            }),
6190            home_network: false,
6191            listen_addr: None,
6192            log_dir_path: log_dir.to_path_buf(),
6193            log_format: None,
6194            max_archived_log_files: None,
6195            max_log_files: None,
6196            metrics_port: None,
6197            network_id: None,
6198            node_ip: None,
6199            node_port: None,
6200            number: 1,
6201            peers_args: InitialPeersConfig::default(),
6202            peer_id: None,
6203            pid: None,
6204            rewards_address: RewardsAddress::from_str(
6205                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
6206            )?,
6207            reward_balance: Some(AttoTokens::zero()),
6208            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
6209            antnode_path: antnode_bin.to_path_buf(),
6210            status: ServiceStatus::Stopped,
6211            service_name: "antnode1".to_string(),
6212            version: "0.98.1".to_string(),
6213            upnp: false,
6214            user: Some("ant".to_string()),
6215            user_mode: false,
6216        };
6217        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
6218        let mut service_manager = ServiceManager::new(
6219            service,
6220            Box::new(mock_service_control),
6221            VerbosityLevel::Normal,
6222        );
6223
6224        service_manager.remove(false).await?;
6225
6226        assert_matches!(
6227            service_manager.service.service_data.status,
6228            ServiceStatus::Removed
6229        );
6230        log_dir.assert(predicate::path::missing());
6231        data_dir.assert(predicate::path::missing());
6232
6233        Ok(())
6234    }
6235
6236    #[tokio::test]
6237    async fn remove_should_return_an_error_if_attempting_to_remove_a_running_node() -> Result<()> {
6238        let mut mock_service_control = MockServiceControl::new();
6239        mock_service_control
6240            .expect_get_process_pid()
6241            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
6242            .times(1)
6243            .returning(|_| Ok(1000));
6244
6245        let mut service_data = NodeServiceData {
6246            auto_restart: false,
6247            connected_peers: None,
6248            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
6249            evm_network: EvmNetwork::Custom(CustomNetwork {
6250                rpc_url_http: "http://localhost:8545".parse()?,
6251                payment_token_address: RewardsAddress::from_str(
6252                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
6253                )?,
6254                data_payments_address: RewardsAddress::from_str(
6255                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
6256                )?,
6257            }),
6258            home_network: false,
6259            listen_addr: None,
6260            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
6261            log_format: None,
6262            max_archived_log_files: None,
6263            max_log_files: None,
6264            metrics_port: None,
6265            network_id: None,
6266            node_ip: None,
6267            node_port: None,
6268            number: 1,
6269            peers_args: InitialPeersConfig::default(),
6270            pid: Some(1000),
6271            peer_id: Some(PeerId::from_str(
6272                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
6273            )?),
6274            rewards_address: RewardsAddress::from_str(
6275                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
6276            )?,
6277            reward_balance: Some(AttoTokens::zero()),
6278            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
6279            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
6280            service_name: "antnode1".to_string(),
6281            status: ServiceStatus::Running,
6282            upnp: false,
6283            user: Some("ant".to_string()),
6284            user_mode: false,
6285            version: "0.98.1".to_string(),
6286        };
6287        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
6288        let mut service_manager = ServiceManager::new(
6289            service,
6290            Box::new(mock_service_control),
6291            VerbosityLevel::Normal,
6292        );
6293
6294        let result = service_manager.remove(false).await;
6295        match result {
6296            Ok(_) => panic!("This test should result in an error"),
6297            Err(e) => assert_eq!(
6298                "The service(s) is already running: [\"antnode1\"]",
6299                e.to_string()
6300            ),
6301        }
6302
6303        Ok(())
6304    }
6305
6306    #[tokio::test]
6307    async fn remove_should_return_an_error_for_a_node_that_was_marked_running_but_was_not_actually_running(
6308    ) -> Result<()> {
6309        let temp_dir = assert_fs::TempDir::new()?;
6310        let log_dir = temp_dir.child("antnode1-logs");
6311        log_dir.create_dir_all()?;
6312        let data_dir = temp_dir.child("antnode1-data");
6313        data_dir.create_dir_all()?;
6314        let antnode_bin = data_dir.child("antnode");
6315        antnode_bin.write_binary(b"fake antnode binary")?;
6316
6317        let mut mock_service_control = MockServiceControl::new();
6318        mock_service_control
6319            .expect_get_process_pid()
6320            .with(eq(PathBuf::from("/var/antctl/services/antnode1/antnode")))
6321            .times(1)
6322            .returning(|_| {
6323                Err(ServiceError::ServiceProcessNotFound(
6324                    "Could not find process at '/var/antctl/services/antnode1/antnode'".to_string(),
6325                ))
6326            });
6327
6328        let mut service_data = NodeServiceData {
6329            auto_restart: false,
6330            connected_peers: None,
6331            data_dir_path: PathBuf::from("/var/antctl/services/antnode1"),
6332            evm_network: EvmNetwork::Custom(CustomNetwork {
6333                rpc_url_http: "http://localhost:8545".parse()?,
6334                payment_token_address: RewardsAddress::from_str(
6335                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
6336                )?,
6337                data_payments_address: RewardsAddress::from_str(
6338                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
6339                )?,
6340            }),
6341            home_network: false,
6342            listen_addr: None,
6343            log_dir_path: PathBuf::from("/var/log/antnode/antnode1"),
6344            log_format: None,
6345            max_archived_log_files: None,
6346            max_log_files: None,
6347            metrics_port: None,
6348            network_id: None,
6349            node_ip: None,
6350            node_port: None,
6351            number: 1,
6352            peers_args: InitialPeersConfig::default(),
6353            pid: Some(1000),
6354            peer_id: Some(PeerId::from_str(
6355                "12D3KooWS2tpXGGTmg2AHFiDh57yPQnat49YHnyqoggzXZWpqkCR",
6356            )?),
6357            rewards_address: RewardsAddress::from_str(
6358                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
6359            )?,
6360            reward_balance: Some(AttoTokens::zero()),
6361            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
6362            antnode_path: PathBuf::from("/var/antctl/services/antnode1/antnode"),
6363            service_name: "antnode1".to_string(),
6364            status: ServiceStatus::Running,
6365            upnp: false,
6366            user: Some("ant".to_string()),
6367            user_mode: false,
6368            version: "0.98.1".to_string(),
6369        };
6370        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
6371        let mut service_manager = ServiceManager::new(
6372            service,
6373            Box::new(mock_service_control),
6374            VerbosityLevel::Normal,
6375        );
6376
6377        let result = service_manager.remove(false).await;
6378        match result {
6379            Ok(_) => panic!("This test should result in an error"),
6380            Err(e) => assert_eq!(
6381                "The service status is not as expected. Expected: Running",
6382                e.to_string()
6383            ),
6384        }
6385
6386        Ok(())
6387    }
6388
6389    #[tokio::test]
6390    async fn remove_should_remove_an_added_node_and_keep_directories() -> Result<()> {
6391        let temp_dir = assert_fs::TempDir::new()?;
6392        let log_dir = temp_dir.child("antnode1-logs");
6393        log_dir.create_dir_all()?;
6394        let data_dir = temp_dir.child("antnode1-data");
6395        data_dir.create_dir_all()?;
6396        let antnode_bin = data_dir.child("antnode");
6397        antnode_bin.write_binary(b"fake antnode binary")?;
6398
6399        let mut mock_service_control = MockServiceControl::new();
6400        mock_service_control
6401            .expect_uninstall()
6402            .with(eq("antnode1"), eq(false))
6403            .times(1)
6404            .returning(|_, _| Ok(()));
6405
6406        let mut service_data = NodeServiceData {
6407            auto_restart: false,
6408            connected_peers: None,
6409            data_dir_path: data_dir.to_path_buf(),
6410            evm_network: EvmNetwork::Custom(CustomNetwork {
6411                rpc_url_http: "http://localhost:8545".parse()?,
6412                payment_token_address: RewardsAddress::from_str(
6413                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
6414                )?,
6415                data_payments_address: RewardsAddress::from_str(
6416                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
6417                )?,
6418            }),
6419            home_network: false,
6420            listen_addr: None,
6421            log_dir_path: log_dir.to_path_buf(),
6422            log_format: None,
6423            max_archived_log_files: None,
6424            max_log_files: None,
6425            metrics_port: None,
6426            network_id: None,
6427            node_ip: None,
6428            node_port: None,
6429            number: 1,
6430            pid: None,
6431            peers_args: InitialPeersConfig::default(),
6432            peer_id: None,
6433            rewards_address: RewardsAddress::from_str(
6434                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
6435            )?,
6436            reward_balance: Some(AttoTokens::zero()),
6437            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
6438            antnode_path: antnode_bin.to_path_buf(),
6439            service_name: "antnode1".to_string(),
6440            status: ServiceStatus::Stopped,
6441            upnp: false,
6442            user: Some("ant".to_string()),
6443            user_mode: false,
6444            version: "0.98.1".to_string(),
6445        };
6446        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
6447        let mut service_manager = ServiceManager::new(
6448            service,
6449            Box::new(mock_service_control),
6450            VerbosityLevel::Normal,
6451        );
6452
6453        service_manager.remove(true).await?;
6454
6455        assert_matches!(
6456            service_manager.service.service_data.status,
6457            ServiceStatus::Removed
6458        );
6459        log_dir.assert(predicate::path::is_dir());
6460        data_dir.assert(predicate::path::is_dir());
6461
6462        Ok(())
6463    }
6464
6465    #[tokio::test]
6466    async fn remove_should_remove_a_user_mode_service() -> Result<()> {
6467        let temp_dir = assert_fs::TempDir::new()?;
6468        let log_dir = temp_dir.child("antnode1-logs");
6469        log_dir.create_dir_all()?;
6470        let data_dir = temp_dir.child("antnode1-data");
6471        data_dir.create_dir_all()?;
6472        let antnode_bin = data_dir.child("antnode");
6473        antnode_bin.write_binary(b"fake antnode binary")?;
6474
6475        let mut mock_service_control = MockServiceControl::new();
6476        mock_service_control
6477            .expect_uninstall()
6478            .with(eq("antnode1"), eq(true))
6479            .times(1)
6480            .returning(|_, _| Ok(()));
6481
6482        let mut service_data = NodeServiceData {
6483            auto_restart: false,
6484            connected_peers: None,
6485            data_dir_path: data_dir.to_path_buf(),
6486            evm_network: EvmNetwork::Custom(CustomNetwork {
6487                rpc_url_http: "http://localhost:8545".parse()?,
6488                payment_token_address: RewardsAddress::from_str(
6489                    "0x5FbDB2315678afecb367f032d93F642f64180aa3",
6490                )?,
6491                data_payments_address: RewardsAddress::from_str(
6492                    "0x8464135c8F25Da09e49BC8782676a84730C318bC",
6493                )?,
6494            }),
6495            home_network: false,
6496            listen_addr: None,
6497            log_dir_path: log_dir.to_path_buf(),
6498            log_format: None,
6499            max_archived_log_files: None,
6500            max_log_files: None,
6501            metrics_port: None,
6502            network_id: None,
6503            node_ip: None,
6504            node_port: None,
6505            number: 1,
6506            pid: None,
6507            peers_args: InitialPeersConfig::default(),
6508            peer_id: None,
6509            rewards_address: RewardsAddress::from_str(
6510                "0x03B770D9cD32077cC0bF330c13C114a87643B124",
6511            )?,
6512            reward_balance: Some(AttoTokens::zero()),
6513            rpc_socket_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081),
6514            antnode_path: antnode_bin.to_path_buf(),
6515            status: ServiceStatus::Stopped,
6516            service_name: "antnode1".to_string(),
6517            upnp: false,
6518            user: None,
6519            user_mode: true,
6520            version: "0.98.1".to_string(),
6521        };
6522        let service = NodeService::new(&mut service_data, Box::new(MockRpcClient::new()));
6523        let mut service_manager = ServiceManager::new(
6524            service,
6525            Box::new(mock_service_control),
6526            VerbosityLevel::Normal,
6527        );
6528
6529        service_manager.remove(false).await?;
6530
6531        assert_matches!(
6532            service_manager.service.service_data.status,
6533            ServiceStatus::Removed
6534        );
6535        log_dir.assert(predicate::path::missing());
6536        data_dir.assert(predicate::path::missing());
6537
6538        Ok(())
6539    }
6540}