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