def test_ingest_update_subscription_item(dbsession, stripe_subscription):
    """A subscription item can be updated."""
    data = stripe_subscription_data()
    new_price_id = fake_stripe_id("price", "monthly special")
    data["items"]["data"][0]["price"] = {
        "id": fake_stripe_id("price", "monthly special"),
        "object": "price",
        "created": unix_timestamp(datetime(2021, 10, 27, tzinfo=timezone.utc)),
        "product": FAKE_STRIPE_ID["Product"],
        "active": True,
        "currency": "usd",
        "recurring": {
            "interval": "month",
            "interval_count": 1,
        },
        "unit_amount": 499,
    }
    subscription, actions = ingest_stripe_subscription(dbsession, data)
    dbsession.commit()
    dbsession.refresh(subscription)

    assert len(subscription.subscription_items) == 1
    s_item = subscription.subscription_items[0]
    assert s_item.price.stripe_id == new_price_id
    assert actions == {
        "updated": {
            f"subscription_item:{data['items']['data'][0]['id']}",
        },
        "created": {
            f"price:{data['items']['data'][0]['price']['id']}",
        },
        "no_change": {
            f"subscription:{data['id']}",
        },
    }
def test_ingest_update_price_via_subscription(dbsession, stripe_subscription):
    """A price object can be updated via a subscription update."""
    assert stripe_subscription.subscription_items[0].price.active

    data = stripe_subscription_data()
    data["items"]["data"][0]["price"]["active"] = False
    subscription, actions = ingest_stripe_subscription(dbsession, data)
    dbsession.commit()
    dbsession.refresh(subscription)

    assert len(subscription.subscription_items) == 1
    s_item = subscription.subscription_items[0]
    assert not s_item.price.active
    assert actions == {
        "updated": {
            f"price:{data['items']['data'][0]['price']['id']}",
        },
        "no_change": {
            f"subscription:{data['id']}",
            f"subscription_item:{data['items']['data'][0]['id']}",
        },
    }
def test_ingest_cancelled_subscription(dbsession, stripe_subscription):
    """A subscription can move to canceled."""
    assert stripe_subscription.status == "active"
    data = stripe_subscription_data()
    data["cancel_at_period_end"] = True
    data["canceled_at"] = unix_timestamp(datetime(2021, 10, 29))
    data["current_period_end"] = unix_timestamp(datetime(2021, 11, 15))
    data["current_period_start"] = unix_timestamp(datetime(2021, 10, 15))
    data["ended_at"] = unix_timestamp(datetime(2021, 10, 29))
    data["start_date"] = unix_timestamp(datetime(2021, 9, 15))
    data["status"] = "canceled"
    subscription, actions = ingest_stripe_subscription(dbsession, data)
    dbsession.commit()
    assert subscription.stripe_id == stripe_subscription.stripe_id
    assert subscription.status == "canceled"
    assert actions == {
        "no_change": {
            f"price:{data['items']['data'][0]['price']['id']}",
            f"subscription_item:{data['items']['data'][0]['id']}",
        },
        "updated": {
            f"subscription:{data['id']}",
        },
    }
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}",
        },
    }
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']}",
        }
    }