Skip to main content

max / makenotwork

Pin the multi-item cart total in the mock payment flows A two-item same-seller cart now asserts one pending transaction per line, their amounts summing to the combined price, and one Stripe session for the cart. The existing single-seller test was loose (count >= 1); it now pins the exact row count and the amount.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-23 22:01 UTC
Signed with PGP, not checked
Commit: 6ab028b85e2fc3328f26cf911937e2a080ec9a46
Parent: 75b3f4f
1 file changed, +74 insertions, -4 deletions
@@ -1247,15 +1247,85 @@
1247 1247 "Should have created a checkout session"
1248 1248 );
1249 1249
1250 - // Verify pending transaction
1251 - let count: i64 = sqlx::query_scalar(
1252 - "SELECT COUNT(*) FROM transactions WHERE buyer_id = $1 AND status = 'pending'",
1250 + // Verify pending transaction: one row, at the item's price.
1251 + let (count, total): (i64, i64) = sqlx::query_as(
1252 + "SELECT COUNT(*), COALESCE(SUM(amount_cents), 0) FROM transactions \
1253 + WHERE buyer_id = $1 AND status = 'pending'",
1253 1254 )
1254 1255 .bind(buyer_id)
1255 1256 .fetch_one(&h.db)
1256 1257 .await
1257 1258 .unwrap();
1258 - assert!(count >= 1, "Should have at least 1 pending transaction");
1259 + assert_eq!(count, 1, "one pending transaction for a one-item cart");
1260 + assert_eq!(total, 500, "pending amount must be the item's price");
1261 + }
1262 +
1263 + /// Two paid items from the same seller in one cart produce one pending
1264 + /// transaction per item, and their amounts sum to the cart total. Pins the
1265 + /// cart core's per-line insert against a single-row shortcut.
1266 + #[tokio::test]
1267 + async fn cart_checkout_two_items_same_seller_sums_to_cart_total() {
1268 + let mut h = TestHarness::with_mocks().await;
1269 + let (seller_id, project_id, item_a) = setup_paid_item(&mut h, 500).await;
1270 +
1271 + // Second paid item under the same seller and project.
1272 + h.login("seller", "pass1234").await;
1273 + let resp = h
1274 + .client
1275 + .post_form(
1276 + &format!("/api/projects/{project_id}/items"),
1277 + "title=Second+Track&price_cents=250&item_type=audio",
1278 + )
1279 + .await;
1280 + assert_eq!(resp.status, 200, "create second item failed: {}", resp.text);
1281 + let second: Value = resp.json();
1282 + let item_b = second["id"].as_str().unwrap().to_string();
1283 + h.client
1284 + .put_form(&format!("/api/items/{item_b}"), "is_public=true")
1285 + .await;
1286 + h.client.post_form("/logout", "").await;
1287 +
1288 + let buyer_id = h.signup("cartsum", "cartsum@test.com", "pass1234").await;
1289 + h.client.post_form(&format!("/api/cart/{item_a}"), "").await;
1290 + h.client.post_form(&format!("/api/cart/{item_b}"), "").await;
1291 +
1292 + let resp = h
1293 + .client
1294 + .post_form(
1295 + "/stripe/checkout/cart",
1296 + &format!("seller_id={seller_id}&share_contact=false"),
1297 + )
1298 + .await;
1299 + assert_eq!(
1300 + resp.status, 303,
1301 + "two-item cart checkout should redirect: {} {}",
1302 + resp.status, resp.text
1303 + );
1304 +
1305 + let (count, total): (i64, i64) = sqlx::query_as(
1306 + "SELECT COUNT(*), COALESCE(SUM(amount_cents), 0) FROM transactions \
1307 + WHERE buyer_id = $1 AND status = 'pending'",
1308 + )
1309 + .bind(buyer_id)
1310 + .fetch_one(&h.db)
1311 + .await
1312 + .unwrap();
1313 + assert_eq!(count, 2, "one pending row per paid cart line");
1314 + assert_eq!(
1315 + total, 750,
1316 + "pending amounts must sum to the two items' combined price"
1317 + );
1318 +
1319 + // Both rows belong to one Stripe session: the cart is charged once.
1320 + let sessions: i64 = sqlx::query_scalar(
1321 + "SELECT COUNT(DISTINCT stripe_checkout_session_id) FROM transactions \
1322 + WHERE buyer_id = $1 AND status = 'pending'",
1323 + )
1324 + .bind(buyer_id)
1325 + .fetch_one(&h.db)
1326 + .await
1327 + .unwrap();
1328 + assert_eq!(sessions, 1, "a cart checkout is one Stripe session");
1259 1329 }
1260 1330
1261 1331 #[tokio::test]