Skip to main content

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