Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

crates/monetize-server/src/estate_order/tests/start_retry.rs

//! ★ **F-B-01 (A17, 2026-10-23): the same buyer's retry of `EstateOrder.Start`.** A Start that
//! ran past the relay's 20 s answers 504, and the buyer's page asks again: the SAME account (a
//! visitor: the same new account at the same address), the same name, the same sizes, while its
//! order is still ORDERED and its page payable, is answered that order's own checkout page — no
//! second hold. Anyone else still reads "taken".

use monetize_proto as pb;
use monetize_proto::estate_order_server::EstateOrder as _;

use super::*;
use crate::services::estate_order::EstateOrderService;

/// A retry of the same order is that order: the same page, no second hold.
#[test]
fn the_same_accounts_retry_is_answered_its_own_unpaid_orders_page() {
    let r = rig("start-retry");
    let first = start_for(
        &r.state,
        "acme",
        "buyer@example.com",
        "",
        "alice",
        &EstateSizes::default(),
    )
    .unwrap();
    let held = r.pay.0.requests.lock().unwrap().len();
    let again = start_for(
        &r.state,
        "acme",
        "buyer@example.com",
        "",
        "alice",
        &EstateSizes::default(),
    )
    .expect("the buyer's own retry is answered, not taken");
    assert_eq!(
        (
            again.reference.as_str(),
            again.checkout_url.as_str(),
            again.handle.clone()
        ),
        (
            first.reference.as_str(),
            first.checkout_url.as_str(),
            first.handle.clone()
        )
    );
    assert!(!again.checkout_url.is_empty());
    assert_eq!(
        r.pay.0.requests.lock().unwrap().len(),
        held,
        "no second hold"
    );
    assert_eq!(
        r.state.estates.get("acme").unwrap().ordered_at_ms,
        first.ordered_at_ms,
        "the run is the same"
    );
}

/// Another account, other sizes, or a page past its time: "taken", as before.
#[test]
fn another_account_other_sizes_or_a_stale_page_are_still_taken() {
    let r = rig("start-retry-taken");
    start_for(
        &r.state,
        "acme",
        "buyer@example.com",
        "",
        "alice",
        &EstateSizes::default(),
    )
    .unwrap();
    assert!(
        matches!(
            start_for(
                &r.state,
                "acme",
                "bob@example.com",
                "",
                "bob",
                &EstateSizes::default()
            ),
            Err(StartRefusal::Taken(_))
        ),
        "another account"
    );
    let bigger = EstateSizes {
        main_data_gib: 200,
        ..EstateSizes::default()
    };
    assert!(
        matches!(
            start_for(&r.state, "acme", "buyer@example.com", "", "alice", &bigger),
            Err(StartRefusal::Taken(_))
        ),
        "other sizes are another order"
    );
    r.clock.fetch_add(2 * 3_600_000, Ordering::SeqCst);
    assert!(
        matches!(
            start_for(
                &r.state,
                "acme",
                "buyer@example.com",
                "",
                "alice",
                &EstateSizes::default()
            ),
            Err(StartRefusal::Taken(_))
        ),
        "a page past its time is not answered again"
    );
    // A paid order is never answered as a page to pay again.
    let r = rig("start-retry-paid");
    let run = start_for(
        &r.state,
        "acme",
        "buyer@example.com",
        "",
        "alice",
        &EstateSizes::default(),
    )
    .unwrap();
    r.pay.authorize(&run.handle.unwrap().id);
    drive(&r.state, "acme");
    assert!(matches!(
        start_for(
            &r.state,
            "acme",
            "buyer@example.com",
            "",
            "alice",
            &EstateSizes::default()
        ),
        Err(StartRefusal::Taken(_))
    ));
}

const WELCOME: &str =
    "https://gunnar.rs/welcome?session={session}&next=%2Fsignup%3Fplan%3Dprivate%26company%3Dacme";

fn visitor(account: &str, email: &str) -> pb::EstateOrderStartRequest {
    pb::EstateOrderStartRequest {
        company: "acme".into(),
        email: email.into(),
        account: account.into(),
        new_account: true,
        welcome_url: WELCOME.into(),
        ..Default::default()
    }
}

/// A visitor (no account yet) retries with the same new account and address: its own page.
/// Another address for that name is refused, as before.
#[test]
fn a_visitors_retry_with_the_same_name_and_address_is_answered_its_own_page() {
    let r = rig("start-retry-visitor");
    r.mails.3.store(true, Ordering::SeqCst);
    let plans = std::collections::BTreeMap::from([(
        "gunnar/account".to_string(),
        crate::config::SignupPlan {
            amount_minor: 0,
            currency: monetize_pay::Currency::Sek,
            cap_bytes: 0,
            period_days: 0,
        },
    )]);
    r.state.signup_plans.set(plans, Default::default());
    let svc = EstateOrderService(r.state.clone());
    let rt = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap();
    let first = rt
        .block_on(svc.start(tonic::Request::new(visitor("newbie", "new@example.com"))))
        .expect("a visitor's order starts")
        .into_inner();
    let session = r
        .state
        .estates
        .get("acme")
        .unwrap()
        .new_account
        .unwrap()
        .session;
    let again = rt
        .block_on(svc.start(tonic::Request::new(visitor("newbie", "New@Example.com"))))
        .expect("the visitor's own retry is answered")
        .into_inner();
    assert_eq!(
        (again.reference.as_str(), again.checkout_url.as_str()),
        (first.reference.as_str(), first.checkout_url.as_str())
    );
    let run = r.state.estates.get("acme").unwrap();
    assert_eq!(
        run.new_account.as_ref().unwrap().session,
        session,
        "the order's own account, as it was"
    );
    let hold = r
        .state
        .signups
        .name_hold(
            "gunnar",
            &r.state.registry.home_of("gunnar").unwrap_or_default(),
            "newbie",
            r.state.now() as i64,
        )
        .unwrap()
        .expect("the name is still held for the page");
    assert_eq!(hold.session, session, "held for the order's own session");
    let other = rt
        .block_on(svc.start(tonic::Request::new(visitor("newbie", "other@example.com"))))
        .unwrap_err();
    assert_ne!(other.code(), tonic::Code::Ok);
    assert_eq!(
        r.state
            .estates
            .get("acme")
            .unwrap()
            .new_account
            .unwrap()
            .email,
        "new@example.com"
    );
}