race-test 0.0.8

Testkit for RACE Protocol
Documentation
use crate::TestClient;
use borsh::BorshSerialize;
use race_core::types::{ClientMode, GameAccount, PlayerJoin, ServerJoin, GameBundle};

pub fn test_game_addr() -> String {
    "TEST".into()
}

pub struct TestGameAccountBuilder {
    account: GameAccount,
}

impl Default for TestGameAccountBuilder {
    fn default() -> Self {
        let account = GameAccount {
            addr: "TEST".into(),
            title: "Unnamed".into(),
            bundle_addr: "".into(),
            owner_addr: "".into(),
            settle_version: 0,
            access_version: 0,
            players: vec![],
            data_len: 0,
            data: vec![],
            transactor_addr: None,
            servers: vec![],
            votes: vec![],
            unlock_time: None,
            max_players: 6,
            deposits: vec![],
            max_deposit: 2,
            min_deposit: 1,
            token_addr: "".into(),
        };
        TestGameAccountBuilder { account }
    }
}

impl TestGameAccountBuilder {
    pub fn new() -> Self {
        TestGameAccountBuilder::default()
    }

    pub fn from_account(account: &GameAccount) -> Self {
        TestGameAccountBuilder {
            account: account.clone(),
        }
    }

    pub fn build(self) -> GameAccount {
        self.account
    }

    pub fn with_max_players(mut self, max_players: u16) -> Self {
        if max_players < self.account.players.len() as _ {
            panic!("Invalid max_players specified, more players were added");
        }
        self.account.max_players = max_players;
        self
    }

    pub fn with_deposit_range(mut self, min: u64, max: u64) -> Self {
        if max < min {
            panic!("Invalid deposit value, the max must be greater than the min");
        }
        self.account.max_deposit = max;
        self.account.min_deposit = min;
        self
    }

    pub fn set_transactor(mut self, server: &TestClient) -> Self {
        if server.get_mode().ne(&ClientMode::Transactor) {
            panic!("A test client in TRANSACTOR Mode is required");
        }
        if self.account.transactor_addr.is_some() {
            panic!("Only one transactor is allowed");
        }
        if self
            .account
            .servers
            .iter()
            .find(|s| s.addr.eq(&server.get_addr()))
            .is_some()
        {
            panic!("Server already added")
        }
        self.account.transactor_addr = Some(server.get_addr());
        self.account.access_version += 1;
        self.account.servers.insert(
            0,
            ServerJoin {
                addr: server.get_addr(),
                endpoint: "".into(),
                access_version: self.account.access_version,
                verify_key: "".into(),
            },
        );
        self
    }

    pub fn add_validator(mut self, server: &TestClient) -> Self {
        if server.get_mode().ne(&ClientMode::Validator) {
            panic!("A test client in VALIDATOR Mode is required");
        }
        if self
            .account
            .servers
            .iter()
            .find(|s| s.addr.eq(&server.get_addr()))
            .is_some()
        {
            panic!("Server already added")
        }
        self.account.access_version += 1;
        self.account.servers.push(ServerJoin {
            addr: server.get_addr(),
            endpoint: "".into(),
            access_version: self.account.access_version,
            verify_key: "".into(),
        });
        self
    }

    pub fn add_player(self, player: &TestClient, deposit: u64) -> Self {
        let mut position = None;
        for i in 0..self.account.max_players {
            if self
                .account
                .players
                .iter()
                .find(|p| p.position == i)
                .is_some()
            {
                continue;
            } else {
                position = Some(i);
                break;
            }
        }
        if let Some(position) = position {
            self.add_player_with_position(player, deposit, position)
        } else {
            panic!("Can't add player, game account is full");
        }
    }

    pub fn add_player_with_position(
        mut self,
        player: &TestClient,
        deposit: u64,
        position: u16,
    ) -> Self {
        if self
            .account
            .players
            .iter()
            .find(|p| p.addr.eq(&player.get_addr()))
            .is_some()
        {
            panic!("Player already added")
        }
        if player.get_mode().ne(&ClientMode::Player) {
            panic!("A test client in PLAYER mode is required");
        }
        self.account.access_version += 1;
        for p in self.account.players.iter() {
            if p.position == position {
                panic!("Player position occupied");
            }
        }
        if position >= self.account.max_players {
            panic!("Player position occupied");
        }
        self.account.players.push(PlayerJoin {
            addr: player.get_addr(),
            position,
            access_version: self.account.access_version,
            balance: deposit,
            verify_key: "".into(),
        });
        self
    }

    pub fn with_data<T: BorshSerialize>(self, account_data: T) -> Self {
        let data = account_data.try_to_vec().expect("Serialize data failed");
        self.with_data_vec(data)
    }

    pub fn with_data_vec(mut self, data: Vec<u8>) -> Self {
        self.account.data_len = data.len() as _;
        self.account.data = data;
        self
    }
}

pub fn create_game_bundle_from_path(path: &str) -> GameBundle {
    let proj_root = project_root::get_project_root().expect("No project root found");
    let bundle_path = proj_root.join(path);
    let data = std::fs::read(bundle_path).expect("Can't read file");
    GameBundle {
        uri: "".to_string(),
        name: "FILE BUNDLE".to_string(),
        data,
    }
}