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