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