Skip to main content

max / makenotwork

Phase 3: harness helpers + test documentation New harness helpers: connect_stripe(), create_creator_with_stripe(), create_buyers(). Eliminates repeated 5-line Stripe SQL blocks. Added tests/README.md documenting all test types, harness features, constructors, running instructions, and fixtures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-04-29 17:00 UTC
Commit: de9f60ecf905af4da85f594768dbc882e17c8f26
Parent: 7be03bb
3 files changed, +111 insertions, -12 deletions
@@ -187,18 +187,16 @@ Modules with 1-3 tests that need expansion, plus missing modules for features we
187 187
188 188 ### Phase 3: Harness optimization
189 189
190 - #### New helper: `create_creator_with_stripe`
191 - - [ ] Combines `create_creator` + mock Stripe Connect setup (stripe_account_id, charges_enabled, onboarding_complete)
192 - - [ ] Returns `CreatorSetup` with stripe account info for webhook tests
193 - - [ ] Eliminates repeated 5-line SQL blocks across stripe_webhooks, mock_payment_flows, fan_plus, tier_enforcement
194 -
195 - #### New helper: `create_buyers(n)`
196 - - [ ] Batch-create n buyer accounts for tests needing multiple purchasers
197 - - [ ] Returns Vec<UserId> for concurrent purchase tests
198 -
199 - #### Harness speedups
200 - - [ ] `TestHarness::minimal()` alias — skips storage/stripe/scanner setup for auth-only tests
201 - - [ ] Document test database requirements in tests/README.md (PostgreSQL version, template DB, TEST_DATABASE_URL)
190 + #### New helpers (added to harness/mod.rs)
191 + - [x] `connect_stripe(user_id, account_id)` — sets stripe_account_id + charges_enabled + onboarding + payouts in one call
192 + - [x] `create_creator_with_stripe(username)` — create_creator + grant_tier + connect_stripe in one call
193 + - [x] `create_buyers(count)` — batch-create buyer accounts, returns Vec<UserId>
194 +
195 + #### Documentation
196 + - [x] `tests/README.md` — documents all test types, harness features, constructors, running instructions, and fixtures
197 +
198 + #### Not needed
199 + - `TestHarness::minimal()` — `new()` is already the minimal constructor (DB only, no extras)
202 200
203 201 ---
204 202
@@ -0,0 +1,64 @@
1 + # MNW Server Tests
2 +
3 + ## Prerequisites
4 +
5 + - PostgreSQL running locally (default: `postgres://localhost/postgres`)
6 + - Set `TEST_DATABASE_URL` if using a non-default admin connection
7 +
8 + ## Test Types
9 +
10 + ### Unit Tests (`cargo test --lib`)
11 +
12 + 986 tests covering pure logic: pricing, validation, formatting, enums, error handling, CSRF, RSS, file scanning, import parsing, etc. No database required.
13 +
14 + ### Integration Tests (`cargo test --test integration`)
15 +
16 + 679 tests across 78 workflow modules. Each test gets an isolated PostgreSQL database cloned from a shared template (migrations applied once per test run).
17 +
18 + **Harness features:**
19 + - In-process Axum app (no network, uses `tower::ServiceExt::oneshot`)
20 + - Cookie-aware HTTP client with automatic CSRF token management
21 + - Mock Stripe (`MockPaymentProvider`) — captures checkout sessions, supports webhook signing
22 + - Mock email (`MockEmailTransport`) — captures all sent emails for assertion
23 + - Mock S3 (`InMemoryStorage`) — in-memory file storage
24 + - Direct SQL helpers for test setup (`grant_creator`, `grant_tier`, `connect_stripe`, etc.)
25 +
26 + **Harness constructors:**
27 + - `TestHarness::new()` — DB only (fastest, for auth/CRUD tests)
28 + - `with_storage()` — adds in-memory S3
29 + - `with_mocks()` — mock Stripe + email (for payment flow tests)
30 + - `with_stripe()` — real Stripe SDK with test keys
31 + - `with_admin()` — pre-created admin user
32 + - `with_storage_and_scanner()` — file scanning pipeline
33 + - `with_git_repos(path)` — git repository support
34 +
35 + ### Load Tests (`cargo test --test load -- --ignored --nocapture`)
36 +
37 + Multi-scenario virtual user simulation. Requires `--ignored` flag. Configurable via env vars:
38 + - `LOAD_VUS` — virtual users (default: 20)
39 + - `LOAD_DURATION_SECS` — duration (default: 30)
40 + - `LOAD_RAMP_SECS` — ramp-up (default: 5)
41 +
42 + ### Health Tests (`cargo test --test health`)
43 +
44 + External HTTP tests against a running server at `http://localhost:3000`. Skips gracefully if server is not running.
45 +
46 + ## Running
47 +
48 + ```bash
49 + # Unit tests only (fast, no DB)
50 + cargo test --lib
51 +
52 + # Integration tests (requires PostgreSQL)
53 + cargo test --test integration
54 +
55 + # Specific workflow
56 + cargo test --test integration sandbox
57 +
58 + # All tests
59 + cargo test
60 + ```
61 +
62 + ## Fixtures
63 +
64 + Test media files in `tests/fixtures/`: `.mp3`, `.mp4`, `.flac`, `.webm`, `.ogg`, `.wav`, `.m4a`
@@ -491,6 +491,43 @@ impl TestHarness {
491 491 CreatorSetup { user_id, project_id, item_id, slug }
492 492 }
493 493
494 + /// Connect a user's Stripe account via direct SQL.
495 + /// Sets stripe_account_id, stripe_charges_enabled, and stripe_onboarding_complete.
496 + /// Use after `create_creator()` for tests that need a Stripe-connected seller.
497 + pub async fn connect_stripe(&self, user_id: UserId, account_id: &str) {
498 + sqlx::query(
499 + "UPDATE users SET stripe_account_id = $2, stripe_charges_enabled = true, \
500 + stripe_onboarding_complete = true, stripe_payouts_enabled = true WHERE id = $1",
501 + )
502 + .bind(user_id)
503 + .bind(account_id)
504 + .execute(&self.db)
505 + .await
506 + .expect("Failed to connect Stripe");
507 + }
508 +
509 + /// Create a test creator with Stripe connected. Shorthand for `create_creator` + `grant_tier` + `connect_stripe`.
510 + /// Returns the user ID. Creator is logged in afterward.
511 + pub async fn create_creator_with_stripe(&mut self, username: &str) -> UserId {
512 + let user_id = self.create_creator(username).await;
513 + self.grant_tier(user_id, "small_files").await;
514 + self.connect_stripe(user_id, &format!("acct_mock_{}", username)).await;
515 + user_id
516 + }
517 +
518 + /// Batch-create buyer accounts. Returns a Vec of user IDs.
519 + /// Each buyer gets username "buyer{n}", email "buyer{n}@test.com", password "password123".
520 + /// The last buyer is left logged in.
521 + pub async fn create_buyers(&mut self, count: usize) -> Vec<UserId> {
522 + let mut ids = Vec::with_capacity(count);
523 + for i in 0..count {
524 + let username = format!("buyer{}", i);
525 + let id = self.signup(&username, &format!("{}@test.com", username), "password123").await;
526 + ids.push(id);
527 + }
528 + ids
529 + }
530 +
494 531 /// Publish both a project and an item.
495 532 pub async fn publish_project_and_item(&mut self, project_id: &str, item_id: &str) {
496 533 self.client