shackle-shell 0.4.1

A shell for restricting access on a version control server
Documentation
use anyhow::Result;
use assert_cmd::cargo::cargo_bin;
use rexpect::session::{spawn_command, PtySession};
use shackle_shell::user_info::{get_user_groups, get_username};
use std::path::PathBuf;
use tempfile::TempDir;

pub struct TestContext {
    pub p: PtySession,
    pub workdir: TempDir,
}

impl TestContext {
    pub fn new_interactive() -> Result<TestContext> {
        let workdir = tempfile::tempdir()?;

        let path = cargo_bin(env!("CARGO_PKG_NAME"));
        let mut command = std::process::Command::new(&path);
        command.current_dir(&workdir);
        let p = spawn_command(command, Some(3000))?;
        let mut c = TestContext { p, workdir };
        c.expect_prompt()?;
        Ok(c)
    }

    pub fn new_batch(batch_command: &str) -> Result<TestContext> {
        let workdir = tempfile::tempdir()?;

        let path = cargo_bin(env!("CARGO_PKG_NAME"));
        let mut command = std::process::Command::new(&path);
        command.current_dir(&workdir);
        command.args(["-c", batch_command]);
        let p = spawn_command(command, Some(3000))?;

        Ok(TestContext { p, workdir })
    }

    pub fn personal_repo_dir(&self, repo_name: &str) -> PathBuf {
        let username = get_username().unwrap();
        self.workdir
            .as_ref()
            .join("git")
            .join(username)
            .join(&format!("{}.git", repo_name))
    }

    pub fn group_repo_dir(&self, group: &str, repo_name: &str) -> PathBuf {
        self.workdir
            .as_ref()
            .join("git")
            .join(group)
            .join(&format!("{}.git", repo_name))
    }

    pub fn expect_prompt(&mut self) -> Result<()> {
        self.p.exp_string("> ")?;
        Ok(())
    }

    pub fn expect_successful_init_message(&mut self, repo_path: &str) -> Result<()> {
        self.p
            .exp_string(&format!("Successfully created \"{repo_path}\""))?;
        self.expect_prompt()
    }

    pub fn expect_list_table(&mut self, repos: &[(&str, &str)]) -> Result<()> {
        let mut repos = Vec::from(repos);
        repos.sort_unstable_by_key(|r| r.0);

        self.p.send_line("list")?;

        self.p.exp_regex(r"\+-+\+-+\+\r\n")?;
        self.p.exp_regex(r"^\| path +\| description +\|\r\n")?;
        self.p.exp_regex(r"^\+=+\+\r\n")?;
        for (path, description) in repos {
            assert_eq!("", self.p.exp_string("| ")?);
            assert_eq!("", self.p.exp_string(path)?);
            self.p.exp_regex(r"^ +\| ")?;
            assert_eq!("", self.p.exp_string(&description)?);
            self.p.exp_regex(r"^ +\|\r\n")?;
            self.p.exp_regex(r"^[|\+]-+\+-+[|\+]\r\n")?;
        }

        self.expect_prompt()?;
        Ok(())
    }

    pub fn expect_list_table_verbose(&mut self, repos: &[(&str, &str)]) -> Result<()> {
        let mut repos = Vec::from(repos);
        repos.sort_unstable_by_key(|r| r.0);

        self.p.send_line("list --verbose")?;
        self.p.exp_regex(r"\+-+\+-+\+-+\+\r\n")?;
        self.p
            .exp_regex(r"^\| path +\| description +\| size +\|\r\n")?;
        self.p.exp_regex(r"^\+=+\+\r\n")?;
        for (path, description) in repos {
            assert_eq!("", self.p.exp_string("| ")?);
            assert_eq!("", self.p.exp_string(path)?);
            self.p.exp_regex(r"^ +\| ")?;
            assert_eq!("", self.p.exp_string(&description)?);
            self.p.exp_regex(r"^ +\| ")?;
            self.p.exp_regex(r"^\d+(\.\d+)? (MiB|KiB|B)")?;
            self.p.exp_regex(r"^ +\|\r\n")?;
            self.p.exp_regex(r"^[|\+]-+\+-+\+-+[|\+]\r\n")?;
        }
        self.expect_prompt()?;
        Ok(())
    }
}

pub fn arbitrary_user_group() -> String {
    get_user_groups().into_iter().next().unwrap()
}

pub fn personal_repo_path(repo_name: &str) -> String {
    let username = get_username().unwrap();
    format!("git/{username}/{repo_name}.git")
}

pub fn group_repo_path(group: &str, repo_name: &str) -> String {
    format!("git/{group}/{repo_name}.git")
}