173 lines
4.3 KiB
Rust
173 lines
4.3 KiB
Rust
use axum::body::Body;
|
|
use axum::http::{Request, StatusCode};
|
|
use axum::routing::get;
|
|
use axum::Router;
|
|
use ed25519_dalek::SigningKey;
|
|
use sqlx::sqlite::SqlitePoolOptions;
|
|
use tower::ServiceExt;
|
|
use wiremock::matchers::{method, path_regex};
|
|
use wiremock::{Mock, MockServer, ResponseTemplate};
|
|
|
|
use clean_copy_server::handlers::{activate, checkout};
|
|
use clean_copy_server::AppState;
|
|
|
|
async fn setup_app(mock_url: &str) -> Router {
|
|
let pool = SqlitePoolOptions::new()
|
|
.connect("sqlite::memory:")
|
|
.await
|
|
.unwrap();
|
|
|
|
sqlx::migrate!().run(&pool).await.unwrap();
|
|
|
|
let key = SigningKey::from_bytes(&[42u8; 32]);
|
|
let stripe_client = stripe::Client::from_url(mock_url, "sk_test_fake");
|
|
|
|
let state = AppState {
|
|
config: clean_copy_server::config::Config {
|
|
stripe_secret_key: "sk_test_fake".to_string(),
|
|
stripe_price_id: "price_test_123".to_string(),
|
|
signing_key_hex: hex::encode(key.to_bytes()),
|
|
database_url: "sqlite::memory:".to_string(),
|
|
release_dir: "/tmp/releases".to_string(),
|
|
static_dir: "./static".to_string(),
|
|
port: 3000,
|
|
app_url: "http://localhost:3000".to_string(),
|
|
},
|
|
db: pool,
|
|
signing_key: key,
|
|
stripe_client,
|
|
};
|
|
|
|
Router::new()
|
|
.route("/api/checkout", get(checkout::checkout))
|
|
.route("/api/activate", axum::routing::post(activate::activate))
|
|
.with_state(state)
|
|
}
|
|
|
|
fn checkout_session_json(id: &str, url: Option<&str>) -> serde_json::Value {
|
|
let mut json = serde_json::json!({
|
|
"id": id,
|
|
"object": "checkout.session",
|
|
"mode": "payment",
|
|
"automatic_tax": { "enabled": false },
|
|
"created": 1700000000,
|
|
"expires_at": 1700003600,
|
|
"custom_fields": [],
|
|
"custom_text": {
|
|
"after_submit": null,
|
|
"shipping_address": null,
|
|
"submit": null,
|
|
"terms_of_service_acceptance": null
|
|
},
|
|
"livemode": false,
|
|
"payment_method_types": ["card"],
|
|
"payment_status": "unpaid",
|
|
"shipping_options": [],
|
|
});
|
|
if let Some(u) = url {
|
|
json["url"] = serde_json::json!(u);
|
|
}
|
|
json
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_checkout_redirects_to_stripe() {
|
|
let mock_server = MockServer::start().await;
|
|
|
|
Mock::given(method("POST"))
|
|
.and(path_regex(r"/v1/checkout/sessions"))
|
|
.respond_with(
|
|
ResponseTemplate::new(200).set_body_json(checkout_session_json(
|
|
"cs_test_123",
|
|
Some("https://checkout.stripe.com/pay/cs_test_123"),
|
|
)),
|
|
)
|
|
.expect(1)
|
|
.mount(&mock_server)
|
|
.await;
|
|
|
|
let app = setup_app(&mock_server.uri()).await;
|
|
|
|
let response = app
|
|
.oneshot(
|
|
Request::builder()
|
|
.method("GET")
|
|
.uri("/api/checkout")
|
|
.body(Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::SEE_OTHER);
|
|
|
|
let location = response
|
|
.headers()
|
|
.get("location")
|
|
.unwrap()
|
|
.to_str()
|
|
.unwrap();
|
|
assert_eq!(location, "https://checkout.stripe.com/pay/cs_test_123");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_checkout_stripe_error() {
|
|
let mock_server = MockServer::start().await;
|
|
|
|
Mock::given(method("POST"))
|
|
.and(path_regex(r"/v1/checkout/sessions"))
|
|
.respond_with(ResponseTemplate::new(400).set_body_json(serde_json::json!({
|
|
"error": {
|
|
"type": "invalid_request_error",
|
|
"message": "No such price: 'price_bad'",
|
|
}
|
|
})))
|
|
.expect(1)
|
|
.mount(&mock_server)
|
|
.await;
|
|
|
|
let app = setup_app(&mock_server.uri()).await;
|
|
|
|
let response = app
|
|
.oneshot(
|
|
Request::builder()
|
|
.method("GET")
|
|
.uri("/api/checkout")
|
|
.body(Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_checkout_no_url_in_response() {
|
|
let mock_server = MockServer::start().await;
|
|
|
|
Mock::given(method("POST"))
|
|
.and(path_regex(r"/v1/checkout/sessions"))
|
|
.respond_with(
|
|
ResponseTemplate::new(200).set_body_json(checkout_session_json("cs_test_no_url", None)),
|
|
)
|
|
.expect(1)
|
|
.mount(&mock_server)
|
|
.await;
|
|
|
|
let app = setup_app(&mock_server.uri()).await;
|
|
|
|
let response = app
|
|
.oneshot(
|
|
Request::builder()
|
|
.method("GET")
|
|
.uri("/api/checkout")
|
|
.body(Body::empty())
|
|
.unwrap(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
|
}
|