1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use super::{BootstrapCommandBuilder, ServerSettingsBuilder};
use crate::common::{
    paths::BLOCK0_BIN,
    server::Server,
    startup::{db::DbBuilderError, get_exe},
};
use assert_fs::fixture::PathChild;
use assert_fs::TempDir;
use std::path::PathBuf;
use std::process::Stdio;
use thiserror::Error;
use vit_servicing_station_lib::server::settings::LogLevel;

pub struct ServerBootstrapper {
    settings_builder: ServerSettingsBuilder,
    allowed_origins: Option<String>,
    service_version: String,
}

impl ServerBootstrapper {
    pub fn new() -> Self {
        let mut settings_builder: ServerSettingsBuilder = Default::default();
        settings_builder
            .with_random_localhost_address()
            .with_block0_path(BLOCK0_BIN.to_string());

        Self {
            settings_builder,
            allowed_origins: None,
            service_version: Default::default(),
        }
    }

    pub fn with_localhost_address(&mut self, port: u32) -> &mut Self {
        self.settings_builder.with_localhost_address(port);
        self
    }

    pub fn with_log_level(&mut self, log_level: LogLevel) -> &mut Self {
        self.settings_builder.with_log_level(log_level);
        self
    }

    pub fn with_db_path<S: Into<String>>(&mut self, db_url: S) -> &mut Self {
        self.settings_builder.with_db_path(db_url.into());
        self
    }

    pub fn with_block0_path<S: Into<String>>(&mut self, block0_path: S) -> &mut Self {
        self.settings_builder.with_block0_path(block0_path.into());
        self
    }

    pub fn with_allowed_origins<S: Into<String>>(&mut self, allowed_origins: S) -> &mut Self {
        self.allowed_origins = Some(allowed_origins.into());
        self
    }

    pub fn with_api_tokens(&mut self, enabled: bool) -> &mut Self {
        self.settings_builder.with_api_tokens(enabled);
        self
    }

    pub fn with_service_version(&mut self, service_version: String) -> &mut Self {
        self.service_version = service_version;
        self
    }

    pub fn start_with_exe(
        &self,
        temp_dir: &TempDir,
        exe: PathBuf,
    ) -> Result<Server, ServerBootstrapperError> {
        let settings = self.settings_builder.build();
        let logger_file: PathBuf = temp_dir.child("log.log").path().into();
        let mut command_builder = BootstrapCommandBuilder::new(exe);

        command_builder
            .address(&settings.address.to_string())
            .db_url(&settings.db_url)
            .log_file(&logger_file)
            .enable_api_tokens(settings.enable_api_tokens)
            .block0_path(settings.block0_path.clone())
            .service_version(&self.service_version);

        if let Some(allowed_origins) = self.allowed_origins.as_ref() {
            command_builder.allowed_origins(allowed_origins);
        }

        if let Some(log_level) = &settings.log.log_level {
            command_builder.log_level(log_level.to_string().as_str());
        }

        let mut command = command_builder.build();
        println!("{:?}", command);
        let child = command.stdout(Stdio::inherit()).spawn()?;

        std::thread::sleep(std::time::Duration::from_secs(1));
        Ok(Server::new(child, settings, logger_file))
    }

    pub fn start(&self, temp_dir: &TempDir) -> Result<Server, ServerBootstrapperError> {
        self.start_with_exe(temp_dir, get_exe())
    }
}

impl Default for ServerBootstrapper {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Debug, Error)]
pub enum ServerBootstrapperError {
    #[error("cannot spawn process")]
    ProcessSpawnError(#[from] std::io::Error),
    #[error("cannot find binary (0)")]
    CargoError(#[from] assert_cmd::cargo::CargoError),
    #[error("failed to bootstrap")]
    FailToBootstrap,
    #[error("database builder error")]
    DbBuilderError(#[from] DbBuilderError),
}