orcs 0.0.8

Microservices monorepo orchestration tool
Documentation
use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use std::process::Command;

mod common;

/// Retrieve a parameter value set at the global level
#[test]
fn get_global() -> Result<(), Box<dyn std::error::Error>> {
    // Create a temporary project
    let root_dir = common::create_project("get-global")?;
    let folder = root_dir.path();

    // Create a configuration file with a global parameter
    let mut cfg_file = File::create(folder.join("orcs.toml"))?;
    let cfg_data = "global.params.my_param = \"my_value\"";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Run the command
    let mut cmd = Command::cargo_bin("orcs")?;
    cmd.arg("-p").arg(&folder).arg("get").arg("my_param");
    cmd.assert()
        .success()
        .stdout(predicate::str::contains("my_value"));

    Ok(())
}

/// Retrieve a parameter value set by a stage
#[test]
fn get_stage() -> Result<(), Box<dyn std::error::Error>> {
    // Create a temporary project
    let root_dir = common::create_project("get-stage")?;
    let folder = root_dir.path();

    // Write a configuration file with a stage parameter
    let mut cfg_file = File::create(folder.join("orcs.toml"))?;
    let cfg_data = "stages.my_stage.params.my_param = \"my_value\"";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Run the command
    let mut cmd = Command::cargo_bin("orcs")?;
    cmd.env("ORCS_SERVICE", "my_service")
        .env("ORCS_STAGE", "my_stage")
        .arg("-p")
        .arg(&folder)
        .arg("get")
        .arg("my_param");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("my_value"));

    Ok(())
}

/// Retrieve a parameter value set by another service
#[test]
fn get_service() -> Result<(), Box<dyn std::error::Error>> {
    // Create a temporary project and services
    let root_dir = common::create_project("get-stage")?;
    let folder = root_dir.path();
    common::create_service(folder, "service-a")?;
    common::create_service(folder, "service-b")?;

    // Override the project file
    let mut cfg_file = File::create(folder.join("orcs.toml"))?;
    let cfg_data = "[stages.my_stage]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Override service B file
    // We need to mark service-a as a dependency of service-b, otherwise
    // we'll get a permission denied error.
    let mut cfg_file = File::create(folder.join("srv").join("service-b").join("orcs.toml"))?;
    let cfg_data = "stages.my_stage.depends_on = [\"service-a\"]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Create the parameter store
    create_dir_all(&folder.join(".store"))?;
    let mut file = File::create(&folder.join(".store").join("store.json"))?;
    let data = "{\"service-a:my_param\":\"my-param-value\"}";
    file.write_all(data.as_bytes())?;

    // Run the command
    let mut cmd = Command::cargo_bin("orcs")?;
    cmd.env("ORCS_SERVICE", "service-b")
        .env("ORCS_STAGE", "my_stage")
        .arg("-p")
        .arg(&folder)
        .arg("get")
        .arg("service-a:my_param");

    cmd.assert()
        .success()
        .stdout(predicate::str::contains("my-param-value"));

    Ok(())
}

/// Retrieve a parameter value set by another service, but the service doesn't
/// depend on it.
#[test]
fn get_service_denied() -> Result<(), Box<dyn std::error::Error>> {
    // Create a temporary project and services
    let root_dir = common::create_project("get-stage")?;
    let folder = root_dir.path();
    common::create_service(folder, "service-a")?;
    common::create_service(folder, "service-b")?;
    common::create_service(folder, "service-c")?;

    // Override the project file
    let mut cfg_file = File::create(folder.join("orcs.toml"))?;
    let cfg_data = "[stages.my_stage]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Override service B file
    // Since we put only service-c as a dependency, and not service-a, we will
    // have an error when trying to retrieve the value.
    let mut cfg_file = File::create(folder.join("srv").join("service-b").join("orcs.toml"))?;
    let cfg_data = "stages.my_stage.depends_on = [\"service-c\"]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Create the parameter store
    create_dir_all(&folder.join(".store"))?;
    let mut file = File::create(&folder.join(".store").join("store.json"))?;
    let data = "{\"service-a:my_param\":\"my-param-value\"}";
    file.write_all(data.as_bytes())?;

    // Run the command
    let mut cmd = Command::cargo_bin("orcs")?;
    cmd.env("ORCS_SERVICE", "service-b")
        .env("ORCS_STAGE", "my_stage")
        .arg("-p")
        .arg(&folder)
        .arg("get")
        .arg("service-a:my_param");

    cmd.assert().failure();

    Ok(())
}

/// Retrieve a parameter value set by another service that doesn't exist
#[test]
fn get_service_not_exist() -> Result<(), Box<dyn std::error::Error>> {
    // Create a temporary project and services
    let root_dir = common::create_project("get-stage")?;
    let folder = root_dir.path();
    common::create_service(folder, "service-a")?;
    common::create_service(folder, "service-b")?;

    // Override the project file
    let mut cfg_file = File::create(folder.join("orcs.toml"))?;
    let cfg_data = "[stages.my_stage]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Override service B file
    // We need to mark service-a as a dependency of service-b, otherwise
    // we'll get a permission denied error.
    let mut cfg_file = File::create(folder.join("srv").join("service-b").join("orcs.toml"))?;
    let cfg_data = "stages.my_stage.depends_on = [\"service-a\"]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Create the parameter store
    create_dir_all(&folder.join(".store"))?;
    let mut file = File::create(&folder.join(".store").join("store.json"))?;
    let data = "{\"service-a:my_param\":\"my-param-value\"}";
    file.write_all(data.as_bytes())?;

    // Run the command
    let mut cmd = Command::cargo_bin("orcs")?;
    cmd.env("ORCS_SERVICE", "service-b")
        .env("ORCS_STAGE", "my_stage")
        .arg("-p")
        .arg(&folder)
        .arg("get")
        .arg("service-a:my_non_existent_param");

    cmd.assert().failure();

    Ok(())
}

/// Retrieve a parameter file set by another service
#[test]
fn get_service_file() -> Result<(), Box<dyn std::error::Error>> {
    // Create a temporary project and services
    let root_dir = common::create_project("get-stage")?;
    let folder = root_dir.path();
    common::create_service(folder, "service-a")?;
    common::create_service(folder, "service-b")?;

    // Override the project file
    let mut cfg_file = File::create(folder.join("orcs.toml"))?;
    let cfg_data = "[stages.my_stage]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Override service B file
    // We need to mark service-a as a dependency of service-b, otherwise
    // we'll get a permission denied error.
    let mut cfg_file = File::create(folder.join("srv").join("service-b").join("orcs.toml"))?;
    let cfg_data = "stages.my_stage.depends_on = [\"service-a\"]";
    cfg_file.write_all(cfg_data.as_bytes())?;

    // Store the file
    create_dir_all(&folder.join(".store"))?;
    let mut file = File::create(&folder.join(".store").join("service-a:my_param.store"))?;
    let data = "Hello, world!";
    file.write_all(data.as_bytes())?;

    // Retrieve the parameter value
    let param_filename = &folder.join("test.txt");
    let mut cmd = Command::cargo_bin("orcs")?;
    cmd.env("ORCS_SERVICE", "service-b")
        .env("ORCS_STAGE", "my_stage")
        .arg("-p")
        .arg(&folder)
        .arg("get-file")
        .arg("service-a:my_param")
        .arg(&param_filename);

    cmd.assert().success();

    // Check the parameter file
    let mut param_file = File::open(param_filename)?;
    let mut param_data = String::new();
    param_file.read_to_string(&mut param_data)?;

    assert_eq!(data, param_data);

    Ok(())
}