test(billing): validate stripe customer id enforcement

This commit is contained in:
2026-04-13 23:17:20 +05:45
parent b773796743
commit bad81dbbb8
3 changed files with 182 additions and 0 deletions
+109
View File
@@ -732,3 +732,112 @@ impl Billing {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use sqlx::SqlitePool;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use std::str::FromStr;
use crate::models::Activity;
async fn test_pool() -> SqlitePool {
let connect_options = SqliteConnectOptions::from_str("sqlite::memory:")
.expect("valid sqlite memory url")
.create_if_missing(true);
let pool = SqlitePoolOptions::new()
.max_connections(1)
.connect_with(connect_options)
.await
.expect("connect sqlite memory db");
sqlx::migrate!("./migrations")
.run(&pool)
.await
.expect("run migrations");
pool
}
fn test_billing(pool: SqlitePool, stripe_secret_key: &str) -> Billing {
Billing {
nwc_url: String::new(),
stripe_secret_key: stripe_secret_key.to_string(),
stripe_webhook_secret: String::new(),
http: reqwest::Client::new(),
query: Query::new(pool.clone()),
command: Command::new(pool),
robot: Robot::test_stub(),
}
}
#[tokio::test]
async fn stripe_create_customer_requires_secret_key() {
let pool = test_pool().await;
let billing = test_billing(pool, "");
let err = billing
.stripe_create_customer("tenant_pubkey")
.await
.expect_err("missing key should fail before HTTP call");
assert!(
err.to_string().contains("missing STRIPE_SECRET_KEY"),
"unexpected error: {err}"
);
}
#[tokio::test]
async fn sync_relay_subscription_fails_for_empty_tenant_customer_id() {
let pool = test_pool().await;
sqlx::query(
"INSERT INTO tenant (pubkey, nwc_url, created_at, stripe_customer_id)
VALUES (?, ?, ?, ?)",
)
.bind("tenant_pubkey")
.bind("")
.bind(0_i64)
.bind("")
.execute(&pool)
.await
.expect("insert tenant");
sqlx::query(
"INSERT INTO relay (id, tenant, schema, subdomain, plan, status)
VALUES (?, ?, ?, ?, ?, ?)",
)
.bind("relay_1")
.bind("tenant_pubkey")
.bind("relay_1")
.bind("relay-1")
.bind("basic")
.bind("active")
.execute(&pool)
.await
.expect("insert relay");
let billing = test_billing(pool, "sk_test_dummy");
let activity = Activity {
id: "activity_1".to_string(),
tenant: "tenant_pubkey".to_string(),
created_at: 0,
activity_type: "create_relay".to_string(),
resource_type: "relay".to_string(),
resource_id: "relay_1".to_string(),
};
let err = billing
.sync_relay_subscription(&activity)
.await
.expect_err("empty tenant customer id should fail clearly");
assert!(
err.to_string()
.contains("tenant tenant_pubkey has no stripe_customer_id"),
"unexpected error: {err}"
);
}
}
+53
View File
@@ -324,3 +324,56 @@ impl Command {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use sqlx::SqlitePool;
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
use std::str::FromStr;
async fn test_pool() -> SqlitePool {
let connect_options = SqliteConnectOptions::from_str("sqlite::memory:")
.expect("valid sqlite memory url")
.create_if_missing(true);
let pool = SqlitePoolOptions::new()
.max_connections(1)
.connect_with(connect_options)
.await
.expect("connect sqlite memory db");
sqlx::migrate!("./migrations")
.run(&pool)
.await
.expect("run migrations");
pool
}
#[tokio::test]
async fn create_tenant_rejects_empty_stripe_customer_id() {
let pool = test_pool().await;
let command = Command::new(pool);
let tenant = Tenant {
pubkey: "tenant_pubkey".to_string(),
nwc_url: String::new(),
nwc_error: None,
created_at: 0,
stripe_customer_id: " ".to_string(),
stripe_subscription_id: None,
past_due_at: None,
};
let err = command
.create_tenant(&tenant)
.await
.expect_err("empty customer id must be rejected");
assert!(
err.to_string().contains("stripe_customer_id is required"),
"unexpected error: {err}"
);
}
}
+20
View File
@@ -254,3 +254,23 @@ async fn set_cached(
},
);
}
#[cfg(test)]
impl Robot {
pub fn test_stub() -> Self {
let keys = Keys::generate();
let client = Client::new(keys);
Self {
secret: String::new(),
name: String::new(),
description: String::new(),
picture: String::new(),
outbox_client: client.clone(),
indexer_client: client.clone(),
messaging_client: client,
outbox_cache: std::sync::Arc::new(Mutex::new(HashMap::new())),
dm_cache: std::sync::Arc::new(Mutex::new(HashMap::new())),
}
}
}