Esempio n. 1
0
def test_ingest_update_customer(dbsession, stripe_customer):
    """A Stripe Customer can be updated."""
    data = stripe_customer_data()
    # Change payment method
    new_source_id = fake_stripe_id("card", "new credit card")
    data["default_source"] = new_source_id
    data["invoice_settings"]["default_payment_method"] = None

    with StatementWatcher(dbsession.connection()) as watcher:
        customer, actions = ingest_stripe_customer(dbsession, data)
        dbsession.commit()
    assert watcher.count == 2
    stmt1 = watcher.statements[0][0]
    assert stmt1.startswith("SELECT stripe_customer."), stmt1
    assert stmt1.endswith(" FOR UPDATE"), stmt1
    stmt2 = watcher.statements[1][0]
    assert stmt2.startswith("UPDATE stripe_customer SET "), stmt2

    assert customer.default_source_id == new_source_id
    assert customer.invoice_settings_default_payment_method_id is None
    assert actions == {
        "updated": {
            f"customer:{data['id']}",
        }
    }
Esempio n. 2
0
def test_ingest_existing_contact(dbsession, example_contact):
    """A Stripe Customer is associated with the existing contact."""
    data = stripe_customer_data()
    data["description"] = example_contact.fxa.fxa_id
    data["email"] = example_contact.fxa.primary_email

    with StatementWatcher(dbsession.connection()) as watcher:
        customer, actions = ingest_stripe_customer(dbsession, data)
        dbsession.commit()
    assert watcher.count == 3
    stmt1 = watcher.statements[0][0]
    assert stmt1.startswith("SELECT stripe_customer."), stmt1
    assert stmt1.endswith(" FOR UPDATE"), stmt1
    stmt2 = watcher.statements[1][0]
    assert stmt2.startswith("SELECT stripe_customer."), stmt2
    assert stmt2.endswith(" FOR UPDATE"), stmt2
    stmt3 = watcher.statements[2][0]
    assert stmt3.startswith("INSERT INTO stripe_customer "), stmt3

    assert customer.stripe_id == FAKE_STRIPE_ID["Customer"]
    assert not customer.deleted
    assert customer.default_source_id is None
    assert (
        customer.invoice_settings_default_payment_method_id
        == FAKE_STRIPE_ID["Payment Method"]
    )
    assert customer.fxa_id == example_contact.fxa.fxa_id
    assert customer.get_email_id() == example_contact.email.email_id
    assert actions == {
        "created": {
            f"customer:{customer.stripe_id}",
        }
    }
Esempio n. 3
0
def test_sync_acoustic_record_delete_path(
    dbsession,
    sync_obj,
    maximal_contact,
    settings,
):
    no_metrics = sync_obj.metric_service is None
    sync_obj.ctms_to_acoustic = MagicMock(
        **{"attempt_to_upload_ctms_contact.return_value": True}
    )
    _setup_pending_record(dbsession, email_id=maximal_contact.email.email_id)

    end_time = datetime.now(timezone.utc) + timedelta(hours=12)
    with StatementWatcher(dbsession.connection()) as watcher:
        context = sync_obj.sync_records(dbsession, end_time=end_time)
        dbsession.flush()
    sync_obj.ctms_to_acoustic.attempt_to_upload_ctms_contact.assert_called_with(
        maximal_contact
    )
    expected_context = {
        "batch_limit": 20,
        "retry_limit": 5,
        "count_total": 1,
        "count_synced": 1,
        "end_time": end_time.isoformat(),
    }
    if no_metrics:
        assert watcher.count == 4  # Get All Records, Get Contact(x2), Increment Retry
        assert context == expected_context
        return

    # Metrics adds two DB queries (total records and retries)
    assert watcher.count == 6
    expected_context["retry_backlog"] = 0
    expected_context["sync_backlog"] = 1
    assert context == expected_context

    registry = sync_obj.metric_service.registry
    labels = {
        "app_kubernetes_io_component": "background",
        "app_kubernetes_io_instance": "ctms",
        "app_kubernetes_io_name": "ctms",
    }
    prefix = "ctms_background_acoustic_sync"
    assert registry.get_sample_value(f"{prefix}_total", labels) == 1
    assert registry.get_sample_value(f"{prefix}_retries", labels) == 0
    assert registry.get_sample_value(f"{prefix}_backlog", labels) == 1
    assert 0.0 <= registry.get_sample_value(f"{prefix}_age_s", labels) <= 0.2
Esempio n. 4
0
def test_ingest_updated_invoice(dbsession, stripe_invoice):
    """An existing Stripe Invoice is updated."""
    assert stripe_invoice.status == "open"
    data = stripe_invoice_data()
    data["status"] = "void"
    with StatementWatcher(dbsession.connection()) as watcher:
        invoice, actions = ingest_stripe_invoice(dbsession, data)
        dbsession.commit()
    assert watcher.count == 5
    stmt1, stmt2, stmt3, stmt4, stmt5 = [pair[0] for pair in watcher.statements]
    assert stmt1.startswith("SELECT stripe_invoice."), stmt1
    assert stmt1.endswith(" FOR UPDATE"), stmt1
    # Get all IDs
    assert stmt2.startswith("SELECT stripe_invoice_line_item.stripe_id "), stmt2
    assert stmt2.endswith(" FOR UPDATE"), stmt2
    # Load line item 1
    # Can't eager load items with FOR UPDATE, need to query twice
    assert stmt3.startswith("SELECT stripe_price."), stmt3
    assert stmt3.endswith(" FOR UPDATE"), stmt3
    assert stmt4.startswith("SELECT stripe_invoice_line_item."), stmt4
    assert stmt4.endswith(" FOR UPDATE"), stmt4
    # Updates invoice
    assert stmt5.startswith("UPDATE stripe_invoice "), stmt5

    assert invoice.status == "void"
    assert len(invoice.line_items) == 1

    assert actions == {
        "updated": {
            f"invoice:{data['id']}",
        },
        "no_change": {
            f"line_item:{data['lines']['data'][0]['id']}",
            f"price:{data['lines']['data'][0]['price']['id']}",
        },
    }
Esempio n. 5
0
def test_ingest_new_invoice(dbsession):
    """A new Stripe Invoice is ingested."""
    data = stripe_invoice_data()

    with StatementWatcher(dbsession.connection()) as watcher:
        invoice, actions = ingest_stripe_invoice(dbsession, data)
        dbsession.commit()
    assert watcher.count == 6
    stmt1, stmt2, stmt3, stmt4, stmt5, stmt6 = [pair[0] for pair in watcher.statements]
    assert stmt1.startswith("SELECT stripe_invoice."), stmt1
    assert stmt1.endswith(" FOR UPDATE"), stmt1
    assert stmt2.startswith("SELECT stripe_price."), stmt2
    assert stmt2.endswith(" FOR UPDATE"), stmt2
    assert stmt3.startswith("SELECT stripe_invoice_line_item."), stmt3
    assert stmt3.endswith(" FOR UPDATE"), stmt3
    # Insert order could be swapped
    assert stmt4.startswith("INSERT INTO stripe_"), stmt4
    assert stmt5.startswith("INSERT INTO stripe_"), stmt5
    assert stmt6.startswith("INSERT INTO stripe_"), stmt6

    assert invoice.stripe_id == FAKE_STRIPE_ID["Invoice"]
    assert invoice.stripe_created == datetime(2021, 10, 28, tzinfo=timezone.utc)
    assert invoice.stripe_customer_id == FAKE_STRIPE_ID["Customer"]
    assert invoice.currency == "usd"
    assert invoice.total == 1000
    assert invoice.status == "open"
    assert invoice.default_payment_method_id is None
    assert invoice.default_source_id is None
    assert invoice.get_email_id() is None

    # Can be created without the Customer object
    assert invoice.customer is None

    assert len(invoice.line_items) == 1
    item = invoice.line_items[0]
    assert item.stripe_id == FAKE_STRIPE_ID["(Invoice) Line Item"]
    assert item.invoice == invoice
    assert item.stripe_subscription_id == FAKE_STRIPE_ID["Subscription"]
    assert item.stripe_subscription_item_id == FAKE_STRIPE_ID["Subscription Item"]
    assert item.stripe_invoice_item_id is None
    assert item.amount == 1000
    assert item.currency == "usd"
    assert item.get_email_id() is None

    price = item.price
    assert price.stripe_id == FAKE_STRIPE_ID["Price"]
    assert price.stripe_created == datetime(2020, 10, 27, 10, 45, tzinfo=timezone.utc)
    assert price.stripe_product_id == FAKE_STRIPE_ID["Product"]
    assert price.active
    assert price.currency == "usd"
    assert price.recurring_interval == "month"
    assert price.recurring_interval_count == 1
    assert price.unit_amount == 999
    assert price.get_email_id() is None

    assert actions == {
        "created": {
            f"invoice:{data['id']}",
            f"line_item:{item.stripe_id}",
            f"price:{price.stripe_id}",
        },
    }
Esempio n. 6
0
def test_ingest_update_subscription(dbsession, stripe_subscription):
    """An existing subscription is updated."""
    data = stripe_subscription_data()
    # Change to yearly
    current_period_end = stripe_subscription.current_period_end + timedelta(days=365)
    si_created = stripe_subscription.current_period_start + timedelta(days=15)
    data["current_period_end"] = unix_timestamp(current_period_end)
    old_sub_item_id = data["items"]["data"][0]["id"]
    new_sub_item_id = fake_stripe_id("si", "new subscription item id")
    new_price_id = fake_stripe_id("price", "yearly price")
    data["items"]["data"][0] = {
        "id": new_sub_item_id,
        "object": "subscription_item",
        "created": unix_timestamp(current_period_end),
        "subscription": data["id"],
        "price": {
            "id": new_price_id,
            "object": "price",
            "created": unix_timestamp(si_created),
            "product": FAKE_STRIPE_ID["Product"],
            "active": True,
            "currency": "usd",
            "recurring": {
                "interval": "year",
                "interval_count": 1,
            },
            "unit_amount": 4999,
        },
    }
    data["default_payment_method"] = fake_stripe_id("pm", "my new credit card")

    with StatementWatcher(dbsession.connection()) as watcher:
        subscription, actions = ingest_stripe_subscription(dbsession, data)
        dbsession.commit()
    assert watcher.count == 8
    stmt1, stmt2, stmt3, stmt4, stmt5, stmt6, stmt7, stmt8 = [
        pair[0] for pair in watcher.statements
    ]
    assert stmt1.startswith("SELECT stripe_subscription."), stmt1
    assert stmt1.endswith(" FOR UPDATE"), stmt1
    # Get all IDs
    assert stmt2.startswith("SELECT stripe_subscription_item.stripe_id "), stmt2
    assert stmt2.endswith(" FOR UPDATE"), stmt2
    # Load item 1
    # Can't eager load items with FOR UPDATE, need to query twice
    assert stmt3.startswith("SELECT stripe_price."), stmt3
    assert stmt3.endswith(" FOR UPDATE"), stmt3
    assert stmt4.startswith("SELECT stripe_subscription_item."), stmt4
    assert stmt4.endswith(" FOR UPDATE"), stmt4
    # Delete old item
    assert stmt5.startswith("DELETE FROM stripe_subscription_item "), stmt5
    # Insert order could be swapped
    insert = "INSERT INTO stripe_"
    update = "UPDATE stripe_subscription SET "
    assert stmt6.startswith(insert) or stmt6.startswith(update), stmt6
    assert stmt7.startswith(insert) or stmt7.startswith(update), stmt7
    assert stmt8.startswith(insert) or stmt8.startswith(update), stmt8

    assert subscription.current_period_end == current_period_end
    assert subscription.default_payment_method_id == data["default_payment_method"]

    assert len(subscription.subscription_items) == 1
    s_item = subscription.subscription_items[0]
    assert s_item.stripe_id == new_sub_item_id
    assert s_item.price.stripe_id == new_price_id

    assert actions == {
        "created": {
            f"subscription_item:{new_sub_item_id}",
            f"price:{new_price_id}",
        },
        "updated": {
            f"subscription:{data['id']}",
        },
        "deleted": {
            f"subscription_item:{old_sub_item_id}",
        },
    }
Esempio n. 7
0
def test_ingest_new_subscription(dbsession):
    """A new Stripe Subscription is ingested."""
    data = stripe_subscription_data()

    with StatementWatcher(dbsession.connection()) as watcher:
        subscription, actions = ingest_stripe_subscription(dbsession, data)
        dbsession.commit()
    assert watcher.count == 6
    stmt1, stmt2, stmt3, stmt4, stmt5, stmt6 = [pair[0] for pair in watcher.statements]
    assert stmt1.startswith("SELECT stripe_subscription."), stmt1
    assert stmt1.endswith(" FOR UPDATE"), stmt1
    assert stmt2.startswith("SELECT stripe_price."), stmt2
    assert stmt2.endswith(" FOR UPDATE"), stmt2
    assert stmt3.startswith("SELECT stripe_subscription_item."), stmt3
    assert stmt3.endswith(" FOR UPDATE"), stmt3
    # Insert order could be swapped
    assert stmt4.startswith("INSERT INTO stripe_"), stmt4
    assert stmt5.startswith("INSERT INTO stripe_"), stmt5
    assert stmt6.startswith("INSERT INTO stripe_"), stmt6

    assert subscription.stripe_id == FAKE_STRIPE_ID["Subscription"]
    assert subscription.stripe_created == datetime(2021, 9, 27, tzinfo=timezone.utc)
    assert subscription.stripe_customer_id == FAKE_STRIPE_ID["Customer"]
    assert not subscription.cancel_at_period_end
    assert subscription.canceled_at is None
    assert subscription.current_period_end == datetime(
        2021, 11, 27, tzinfo=timezone.utc
    )
    assert subscription.current_period_start == datetime(
        2021, 10, 27, tzinfo=timezone.utc
    )
    assert subscription.ended_at is None
    assert subscription.start_date == datetime(2021, 9, 27, tzinfo=timezone.utc)
    assert subscription.status == "active"
    assert subscription.default_payment_method_id is None
    assert subscription.get_email_id() is None

    # Can be created without the Customer object
    assert subscription.customer is None

    assert len(subscription.subscription_items) == 1
    item = subscription.subscription_items[0]
    assert item.stripe_id == FAKE_STRIPE_ID["Subscription Item"]
    assert item.subscription == subscription
    assert item.get_email_id() is None

    price = item.price
    assert price.stripe_id == FAKE_STRIPE_ID["Price"]
    assert price.stripe_created == datetime(2020, 10, 27, 10, 45, tzinfo=timezone.utc)
    assert price.stripe_product_id == FAKE_STRIPE_ID["Product"]
    assert price.active
    assert price.currency == "usd"
    assert price.recurring_interval == "month"
    assert price.recurring_interval_count == 1
    assert price.unit_amount == 999
    assert price.get_email_id() is None

    assert actions == {
        "created": {
            f"subscription:{FAKE_STRIPE_ID['Subscription']}",
            f"subscription_item:{FAKE_STRIPE_ID['Subscription Item']}",
            f"price:{FAKE_STRIPE_ID['Price']}",
        }
    }