mirror of
https://codeberg.org/demostf/api-test.git
synced 2026-06-03 17:44:07 +02:00
init testing
This commit is contained in:
parent
a011c118b4
commit
679650369a
9 changed files with 2669 additions and 4 deletions
60
src/harness.rs
Normal file
60
src/harness.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
use demostf_client::ApiClient;
|
||||
use sqlx::{Pool, Postgres};
|
||||
use color_eyre::Result;
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
|
||||
pub struct Harness {
|
||||
client: ApiClient,
|
||||
db: Pool<Postgres>,
|
||||
}
|
||||
|
||||
impl Harness {
|
||||
pub async fn new(base_url: &str, db_url: &str) -> Result<Self> {
|
||||
let client = ApiClient::with_base_url(base_url)?;
|
||||
let db = PgPoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&db_url)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(Harness {
|
||||
client,
|
||||
db,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn reset(&self) -> Result<()> {
|
||||
let tables = [
|
||||
"chat",
|
||||
"demos",
|
||||
"kills",
|
||||
"players",
|
||||
"storage_keys",
|
||||
"teams",
|
||||
"upload_blacklist",
|
||||
"users",
|
||||
];
|
||||
|
||||
let mut transaction = self.db.begin().await?;
|
||||
|
||||
for table in &tables {
|
||||
sqlx::query(&format!("TRUNCATE TABLE {}", table))
|
||||
.execute(&mut transaction)
|
||||
.await?;
|
||||
sqlx::query(&format!("ALTER SEQUENCE {}_id_seq RESTART with 1", table))
|
||||
.execute(&mut transaction)
|
||||
.await?;
|
||||
}
|
||||
|
||||
sqlx::query("INSERT INTO users(steamid, name, avatar, token)\
|
||||
VALUES(76561198024494988, 'Icewind', 'http://cdn.akamai.steamstatic.com/steamcommunity/public/images/avatars/75/75b84075b70535c5cfb3499af03b3e4e7a7b556f_medium.jpg', 'token')")
|
||||
.execute(&mut transaction).await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn client(&self) -> ApiClient {
|
||||
self.client.clone()
|
||||
}
|
||||
}
|
||||
186
src/main.rs
186
src/main.rs
|
|
@ -1,3 +1,185 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
mod harness;
|
||||
mod report;
|
||||
|
||||
use crate::harness::Harness;
|
||||
use bitbuffer::{BitReadBuffer, LittleEndian};
|
||||
use color_eyre::{eyre::WrapErr, Report, Result};
|
||||
use demostf_client::{Class, Demo, SteamID, Team};
|
||||
use report::{assert_eq, Test};
|
||||
use std::str::FromStr;
|
||||
use tf_demo_parser::DemoParser;
|
||||
|
||||
macro_rules! assert_object_eq {
|
||||
($obj:expr => { $($name:ident == $value:expr),* }) => {
|
||||
$(report::assert_eq_borrow(&$obj.$name, $value)?;)*
|
||||
};
|
||||
($obj:expr => { $($name:ident == $value:expr),* , }) => {
|
||||
$(report::assert_eq_borrow(&$obj.$name, $value)?;)*
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let harness = Harness::new(&dotenv::var("BASE_URL")?, &dotenv::var("DB_URL")?).await?;
|
||||
|
||||
Test::run(
|
||||
"Upload demo, then retrieve info",
|
||||
&harness,
|
||||
|test| async move {
|
||||
let id = test
|
||||
.step("upload", |client| async move {
|
||||
Ok(client
|
||||
.upload_demo(
|
||||
String::from("test.dem"),
|
||||
std::fs::read("data/gully.dem")?,
|
||||
String::from("RED"),
|
||||
String::from("BLUE"),
|
||||
String::from("token"),
|
||||
)
|
||||
.await?)
|
||||
})
|
||||
.await?;
|
||||
|
||||
assert_eq(id, 1)?;
|
||||
|
||||
test.step("get", |client| async move {
|
||||
let demo = client.get(id).await?;
|
||||
assert_object_eq!(demo => {
|
||||
id == 1,
|
||||
name == "test.dem",
|
||||
map == "cp_gullywash_final1",
|
||||
red_score == 5,
|
||||
blue_score == 3,
|
||||
player_count == 12,
|
||||
});
|
||||
verify_demo(&demo, std::fs::read("data/gully.dem")?)?;
|
||||
assert_eq(demo.uploader.id(), 1)?;
|
||||
|
||||
let uploader = demo.uploader.resolve(client).await?;
|
||||
assert_eq(&uploader.name, "Icewind")?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_demo(api_result: &Demo, demo: Vec<u8>) -> Result<()> {
|
||||
use tf_demo_parser::demo::parser::gamestateanalyser;
|
||||
|
||||
fn map_team(team: Team) -> gamestateanalyser::Team {
|
||||
match team {
|
||||
Team::Red => gamestateanalyser::Team::Red,
|
||||
Team::Blue => gamestateanalyser::Team::Blue,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_class(class: Class) -> gamestateanalyser::Class {
|
||||
match class {
|
||||
Class::Scout => gamestateanalyser::Class::Scout,
|
||||
Class::Soldier => gamestateanalyser::Class::Soldier,
|
||||
Class::Pyro => gamestateanalyser::Class::Pyro,
|
||||
Class::Demoman => gamestateanalyser::Class::Demoman,
|
||||
Class::HeavyWeapons => gamestateanalyser::Class::Heavy,
|
||||
Class::Medic => gamestateanalyser::Class::Medic,
|
||||
Class::Engineer => gamestateanalyser::Class::Engineer,
|
||||
Class::Sniper => gamestateanalyser::Class::Sniper,
|
||||
Class::Spy => gamestateanalyser::Class::Spy,
|
||||
}
|
||||
}
|
||||
|
||||
let parser = DemoParser::new(BitReadBuffer::new(demo, LittleEndian).into());
|
||||
let (header, state) = parser
|
||||
.parse()
|
||||
.map_err(|_| Report::msg("Failed to parse demo"))?;
|
||||
assert_eq(&api_result.map, &header.map).wrap_err("Failed to compare map")?;
|
||||
assert_eq(
|
||||
api_result.red_score,
|
||||
state
|
||||
.rounds
|
||||
.iter()
|
||||
.filter(|round| round.winner == gamestateanalyser::Team::Red)
|
||||
.count() as u8,
|
||||
)
|
||||
.wrap_err("Failed to compare red score")?;
|
||||
assert_eq(
|
||||
api_result.blue_score,
|
||||
state
|
||||
.rounds
|
||||
.iter()
|
||||
.filter(|round| round.winner == gamestateanalyser::Team::Blue)
|
||||
.count() as u8,
|
||||
)
|
||||
.wrap_err("Failed to compare blue score")?;
|
||||
|
||||
let mut players = state
|
||||
.users
|
||||
.values()
|
||||
.filter(|user| user.team.is_player())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
players.sort_by(|a, b| {
|
||||
SteamID::from_str(&a.steam_id)
|
||||
.unwrap()
|
||||
.account_id()
|
||||
.cmp(&SteamID::from_str(&b.steam_id).unwrap().account_id())
|
||||
});
|
||||
|
||||
let mut api_players = api_result.players.iter().collect::<Vec<_>>();
|
||||
api_players.sort_by(|a, b| {
|
||||
a.user
|
||||
.steam_id
|
||||
.account_id()
|
||||
.cmp(&b.user.steam_id.account_id())
|
||||
});
|
||||
|
||||
assert_eq(api_result.player_count, players.len() as u8)
|
||||
.wrap_err("Failed to compare player count")?;
|
||||
assert_eq(api_players.len(), players.len()).wrap_err("Failed to compare player count")?;
|
||||
|
||||
for (api_player, player) in api_players.iter().zip(players.iter()) {
|
||||
assert_eq(&api_player.user.name, &player.name).wrap_err_with(|| {
|
||||
format!("Failed to compare player name for {}", api_player.user.name)
|
||||
})?;
|
||||
assert_eq(
|
||||
&api_player.user.steam_id,
|
||||
&SteamID::from_str(&player.steam_id).unwrap(),
|
||||
)
|
||||
.wrap_err_with(|| format!("Failed to compare steam id for {}", api_player.user.name))?;
|
||||
assert_eq(map_team(api_player.team), player.team)
|
||||
.wrap_err_with(|| format!("Failed to compare team for {}", api_player.user.name))?;
|
||||
assert_eq(
|
||||
map_class(api_player.class),
|
||||
player.classes.sorted().next().unwrap().0,
|
||||
)
|
||||
.wrap_err_with(|| format!("Failed to compare class for {}", api_player.user.name))?;
|
||||
let kills = state
|
||||
.deaths
|
||||
.iter()
|
||||
.filter(|kill| kill.killer == player.user_id)
|
||||
.count() as u8;
|
||||
let assists = state
|
||||
.deaths
|
||||
.iter()
|
||||
.filter(|kill| kill.assister == Some(player.user_id))
|
||||
.count() as u8;
|
||||
let deaths = state
|
||||
.deaths
|
||||
.iter()
|
||||
.filter(|kill| kill.victim == player.user_id)
|
||||
.count() as u8;
|
||||
assert_eq(api_player.kills, kills)
|
||||
.wrap_err_with(|| format!("Failed to compare kills for {}", api_player.user.name))?;
|
||||
assert_eq(api_player.assists, assists)
|
||||
.wrap_err_with(|| format!("Failed to compare assists for {}", api_player.user.name))?;
|
||||
assert_eq(api_player.deaths, deaths)
|
||||
.wrap_err_with(|| format!("Failed to compare deaths for {}", api_player.user.name))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
86
src/report.rs
Normal file
86
src/report.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
use crate::harness::Harness;
|
||||
use color_eyre::{Report, Result};
|
||||
use colored::Colorize;
|
||||
use demostf_client::ApiClient;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Test {
|
||||
client: ApiClient,
|
||||
}
|
||||
|
||||
impl Test {
|
||||
pub async fn run<'a, Fut: Future<Output = Result<()>> + 'a, F: FnOnce(Test) -> Fut + 'a>(
|
||||
name: &str,
|
||||
harness: &'a Harness,
|
||||
f: F,
|
||||
) {
|
||||
println!(" - {}", name);
|
||||
|
||||
if let Err(e) = harness.reset().await {
|
||||
println!(" {}: {:#}", "Reset api server".red(), e);
|
||||
println!(" {}", "❌".red());
|
||||
return;
|
||||
} else {
|
||||
println!(" {}", "Reset api server".green());
|
||||
}
|
||||
|
||||
let test = Test {
|
||||
client: harness.client(),
|
||||
};
|
||||
|
||||
match f(test).await {
|
||||
Ok(_) => {
|
||||
println!(" {}", "✓".green());
|
||||
}
|
||||
Err(e) => {
|
||||
println!(" {}: {:#}", "❌".red(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn step<
|
||||
'a,
|
||||
T,
|
||||
Fut: Future<Output = Result<T>> + 'a,
|
||||
F: FnOnce(&'a ApiClient) -> Fut + 'a,
|
||||
>(
|
||||
&'a self,
|
||||
name: &str,
|
||||
f: F,
|
||||
) -> Result<T> {
|
||||
match f(&self.client).await {
|
||||
Ok(res) => {
|
||||
println!(" - {}", name.green());
|
||||
Ok(res)
|
||||
}
|
||||
Err(e) => {
|
||||
println!(" - {}: {:#}", name.red(), e);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_eq<A: Debug, B: PartialEq<A> + Debug>(a: A, b: B) -> Result<()> {
|
||||
if b.eq(&a) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Report::msg(format!(
|
||||
"Failed asserting that {:?} equals {:?}",
|
||||
a, b
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assert_eq_borrow<A: Debug, B: PartialEq<A> + Debug>(a: &A, b: B) -> Result<()> {
|
||||
if b.eq(a) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Report::msg(format!(
|
||||
"Failed asserting that {:?} equals {:?}",
|
||||
a, b
|
||||
)))
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue