Skip to content

Commit

Permalink
refactor metadata_db tests to make test clearer
Browse files Browse the repository at this point in the history
also makes it easier to add new tests
  • Loading branch information
michaeldjeffrey committed May 7, 2024
1 parent 37f700c commit 5568963
Showing 1 changed file with 175 additions and 129 deletions.
304 changes: 175 additions & 129 deletions mobile_config/src/boosted_hex_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,150 +593,196 @@ mod tests {
Ok(())
}

#[sqlx::test]
#[ignore = "for manual metadata db testing"]
async fn parse_boosted_hex_info_from_database(pool: PgPool) -> anyhow::Result<()> {
let boost_config_address = Pubkey::new_unique();
let now = Utc::now();
fn parse_dt(dt: &str) -> DateTime<Utc> {
NaiveDateTime::parse_from_str(dt, "%Y-%m-%d %H:%M:%S")
.expect("unable_to_parse")
.and_utc()
}

// NOTE(mj): Table creation taken from a dump of the metadata db.
// device_type was added to boosted_hexes as a jsonb field because the
// mobile_hotspot_infos table has a device_type column that maps to an
// enum, and it is a nullable jsonb column.
const CREATE_BOOSTED_HEXES_TABLE: &str = r#"
CREATE TABLE
boosted_hexes (
address character varying(255) NOT NULL PRIMARY KEY,
boost_config character varying(255) NULL,
location numeric NULL,
start_ts numeric NULL,
reserved numeric[] NULL,
bump_seed integer NULL,
boosts_by_period bytea NULL,
version integer NULL,
refreshed_at timestamp with time zone NULL,
created_at timestamp with time zone NOT NULL,
device_type jsonb
)
"#;
mod metadata_db {
/// Table creation was taken from a dump of the metadata db.
/// `device_type` was added to boosted_hexes as a jsonb field because the
/// mobile_hotspot_infos table has a device_type column that maps to an
/// enum, and it is a nullable jsonb column.
///
/// When the `boosted_hexes` or `boost_configs` tables from the metadata_db
/// are updated, these tests will not break until the new table formats are
/// put into `create_tables()`.
use super::*;

#[sqlx::test]
async fn parse_boosted_hex_info_from_database(pool: PgPool) -> anyhow::Result<()> {
let boost_config_address = Pubkey::new_unique();
let now = Utc::now();

create_tables(&pool).await?;
insert_boost_config(&pool, &boost_config_address, now).await?;

let device_types = vec![
None, // legacy boosted hex with NULL device_type
Some(serde_json::json!("cbrsIndoor")),
Some(serde_json::json!("cbrsOutdoor")),
Some(serde_json::json!("wifiIndoor")),
Some(serde_json::json!("wifiOutdoor")),
// Some(serde_json::json!(null)) // this is different from None, and will/should break parsing as it's not part of the enum
];
for device_type in device_types {
insert_boosted_hex(&pool, &boost_config_address, now, device_type, Some(now))
.await?;
}

assert_eq!(
5,
boosted_hexes_count(&pool).await?,
"there should be 1 of each type of boosted hex"
);
assert_eq!(
5,
streamed_hexes_count(&pool).await?,
"not all rows were able to parse"
);

Ok(())
}

const CREATE_BOOST_CONFIG_TABLE: &str = r#"
CREATE TABLE
boost_configs (
address character varying(255) NOT NULL PRIMARY KEY,
price_oracle character varying(255) NULL,
payment_mint character varying(255) NULL,
sub_dao character varying(255) NULL,
rent_reclaim_authority character varying(255) NULL,
boost_price numeric NULL,
period_length integer NULL,
minimum_periods integer NULL,
bump_seed integer NULL,
start_authority character varying(255) NULL,
refreshed_at timestamp with time zone NULL,
created_at timestamp with time zone NOT NULL
)
"#;
async fn create_tables(pool: &PgPool) -> anyhow::Result<()> {
const CREATE_BOOSTED_HEXES_TABLE: &str = r#"
CREATE TABLE
boosted_hexes (
address character varying(255) NOT NULL PRIMARY KEY,
boost_config character varying(255) NULL,
location numeric NULL,
start_ts numeric NULL,
reserved numeric[] NULL,
bump_seed integer NULL,
boosts_by_period bytea NULL,
version integer NULL,
refreshed_at timestamp with time zone NULL,
created_at timestamp with time zone NOT NULL,
device_type jsonb
)
"#;

const CREATE_BOOST_CONFIG_TABLE: &str = r#"
CREATE TABLE
boost_configs (
address character varying(255) NOT NULL PRIMARY KEY,
price_oracle character varying(255) NULL,
payment_mint character varying(255) NULL,
sub_dao character varying(255) NULL,
rent_reclaim_authority character varying(255) NULL,
boost_price numeric NULL,
period_length integer NULL,
minimum_periods integer NULL,
bump_seed integer NULL,
start_authority character varying(255) NULL,
refreshed_at timestamp with time zone NULL,
created_at timestamp with time zone NOT NULL
)
"#;

sqlx::query(CREATE_BOOSTED_HEXES_TABLE)
.execute(pool)
.await?;
sqlx::query(CREATE_BOOST_CONFIG_TABLE).execute(pool).await?;
Ok(())
}

sqlx::query(CREATE_BOOSTED_HEXES_TABLE)
.execute(&pool)
.await?;
sqlx::query(CREATE_BOOST_CONFIG_TABLE)
.execute(&pool)
.await?;
async fn insert_boost_config(
pool: &PgPool,
boost_config_address: &Pubkey,
created_at: DateTime<Utc>,
) -> anyhow::Result<()> {
const INSERT_BOOST_CONFIG: &str = r#"
INSERT INTO boost_configs (
"boost_price", "bump_seed", "minimum_periods", "period_length", "refreshed_at",
-- pubkeys
"price_oracle",
"payment_mint",
"rent_reclaim_authority",
"start_authority",
"sub_dao",
-- our values
"address",
"created_at"
)
VALUES (
'5000', 250, 6, 2592000, '2024-03-12 21:13:52.692+00',
const INSERT_BOOST_CONFIG: &str = r#"
INSERT INTO boost_configs (
"boost_price", "bump_seed", "minimum_periods", "period_length", "refreshed_at",
-- pubkeys
"price_oracle",
"payment_mint",
"rent_reclaim_authority",
"start_authority",
"sub_dao",
-- our values
"address",
"created_at"
)
VALUES (
'5000', 250, 6, 2592000, '2024-03-12 21:13:52.692+00',
$1, $2, $3, $4, $5, $6, $7
)
"#;
$1, $2, $3, $4, $5, $6, $7
)
"#;

sqlx::query(INSERT_BOOST_CONFIG)
.bind(Pubkey::new_unique().to_string()) // price_oracle
.bind(Pubkey::new_unique().to_string()) // payment_mint
.bind(Pubkey::new_unique().to_string()) // rent_reclaim_authority
.bind(Pubkey::new_unique().to_string()) // start_authority
.bind(Pubkey::new_unique().to_string()) // sub_dao
// --
.bind(boost_config_address.to_string()) // address
.bind(created_at) // created_at
.execute(pool)
.await?;

const INSERT_BOOSTED_HEX: &str = r#"
INSERT INTO boosted_hexes (
"boosts_by_period", "bump_seed", "location", "refreshed_at", "reserved", "start_ts", "version",
-- our values
"address",
"boost_config",
"created_at",
"device_type"
)
VALUES (
'ZGRkZGRk', 1, '631798453297853439', '2024-03-12 21:13:53.773+00', '{0,0,0,0,0,0,0,0}', '1708304400', 1,
$1, $2, $3, $4
)
"#;
Ok(())
}

// Insert boost config that boosted hexes will point to.
sqlx::query(INSERT_BOOST_CONFIG)
.bind(Pubkey::new_unique().to_string()) // price_oracle
.bind(Pubkey::new_unique().to_string()) // payment_mint
.bind(Pubkey::new_unique().to_string()) // rent_reclaim_authority
.bind(Pubkey::new_unique().to_string()) // start_authority
.bind(Pubkey::new_unique().to_string()) // sub_dao
// --
.bind(boost_config_address.to_string()) // address
.bind(now) // created_at
.execute(&pool)
.await?;
async fn insert_boosted_hex(
pool: &PgPool,
boost_config_address: &Pubkey,
created_at: DateTime<Utc>,
device_type: Option<serde_json::Value>,
start_ts: Option<DateTime<Utc>>,
) -> anyhow::Result<()> {
const INSERT_BOOSTED_HEX: &str = r#"
INSERT INTO boosted_hexes (
"boosts_by_period", "bump_seed", "location", "refreshed_at", "reserved", "version",
-- our values
"address",
"boost_config",
"created_at",
"device_type",
"start_ts"
)
VALUES (
'ZGRkZGRk', 1, '631798453297853439', '2024-03-12 21:13:53.773+00', '{0,0,0,0,0,0,0,0}', 1,
// Legacy boosted hex with NULL device_type
sqlx::query(INSERT_BOOSTED_HEX)
.bind(Pubkey::new_unique().to_string()) // address
.bind(boost_config_address.to_string()) // boost_config
.bind(now) // created_at
.bind(None as Option<serde_json::Value>) // device_type
.execute(&pool)
.await?;
$1, $2, $3, $4, $5
)
"#;

// Boosted hex with new device types
for device_type in &["cbrsIndoor", "cbrsOutdoor", "wifiIndoor", "wifiOutdoor"] {
sqlx::query(INSERT_BOOSTED_HEX)
.bind(Pubkey::new_unique().to_string()) // address
.bind(boost_config_address.to_string()) // boost_config
.bind(now) // created_at
.bind(serde_json::json!(device_type)) // device_type
.execute(&pool)
.bind(created_at) // created_at
.bind(device_type) // device_type
.bind(start_ts.map(|t| t.timestamp()).unwrap_or_default()) // start_ts
.execute(pool)
.await?;
}

let count: i64 = sqlx::query_scalar("select count(*) from boosted_hexes")
.fetch_one(&pool)
.await?;
assert_eq!(5, count, "there should be 1 of each type of boosted hex");

let mut infos = super::db::all_info_stream(&pool);
let mut print_count = 0;
while let Some(_info) = infos.next().await {
// println!("info: {_info:?}");
print_count += 1;
Ok(())
}

assert_eq!(5, print_count, "not all rows were able to parse");

Ok(())
}
async fn boosted_hexes_count(pool: &PgPool) -> anyhow::Result<i64> {
let count: i64 = sqlx::query_scalar("select count(*) from boosted_hexes")
.fetch_one(pool)
.await?;
Ok(count)
}

fn parse_dt(dt: &str) -> DateTime<Utc> {
NaiveDateTime::parse_from_str(dt, "%Y-%m-%d %H:%M:%S")
.expect("unable_to_parse")
.and_utc()
async fn streamed_hexes_count(pool: &PgPool) -> anyhow::Result<usize> {
// If a row cannot be parsed, it is dropped from the stream with no erros.
let mut infos = super::db::all_info_stream(pool);
let mut count = 0;
while let Some(_info) = infos.next().await {
// println!("info: {_info:?}");
count += 1;
}
Ok(count)
}
}
}

0 comments on commit 5568963

Please sign in to comment.