coffer/tests/test_product.py

148 lines
6.3 KiB
Python

import tempfile
import unittest
from pathlib import Path
from coffer.product import CofferProduct
from coffer.units import (
ACCOUNT_COFFER,
ACCOUNT_LEORA,
ACCOUNT_NEW_INTERNET,
agent_account,
cut_subunits,
to_subunits,
)
class ProductTests(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.db_path = Path(self.tmp.name) / "coffer.db"
self.product = CofferProduct(self.db_path)
def tearDown(self):
self.product.close()
self.tmp.cleanup()
def test_first_tx_is_200c_contract(self):
boot = self.product.bootstrap_opening_books()
self.assertFalse(boot["already"])
self.assertEqual(boot["coffer"]["spendable"], 500.0)
self.assertEqual(boot["leora"]["spendable"], 400.0)
self.assertEqual(self.product.history(), [])
result = self.product.record_build_contract()
self.assertFalse(result["already"])
tx = result["tx"]
self.assertEqual(tx["seq"], 1)
self.assertEqual(tx["kind"], "contract_payment")
self.assertEqual(tx["amount_centicredits"], to_subunits(200))
self.assertEqual(tx["debit_account"], ACCOUNT_COFFER)
self.assertEqual(tx["credit_account"], ACCOUNT_LEORA)
self.assertEqual(result["payer"]["spendable"], 300.0)
self.assertEqual(result["payee"]["spendable"], 600.0)
# Idempotent
again = self.product.record_build_contract()
self.assertTrue(again["already"])
self.assertEqual(len(self.product.history()), 1)
# Durable across reopen
self.product.close()
self.product = CofferProduct(self.db_path)
hist = self.product.history()
self.assertEqual(len(hist), 1)
self.assertEqual(hist[0]["kind"], "contract_payment")
self.assertEqual(self.product.balance(ACCOUNT_COFFER)["spendable"], 300.0)
self.assertEqual(self.product.balance(ACCOUNT_LEORA)["spendable"], 600.0)
def test_signup_grant_40c(self):
self.product.bootstrap_opening_books()
self.product.record_build_contract()
out = self.product.grant_signup("explorer1")
self.assertFalse(out["already"])
self.assertEqual(out["granted_credits"], 40)
self.assertEqual(out["balance"]["promotional"], 40.0)
self.assertEqual(out["balance"]["spendable"], 40.0)
again = self.product.grant_signup("explorer1")
self.assertTrue(again["already"])
self.assertEqual(self.product.balance("explorer1")["spendable"], 40.0)
def test_subscription_through_coffer_with_one_percent_cut(self):
self.product.bootstrap_opening_books()
self.product.record_build_contract()
self.product.grant_signup("explorer2")
coffer_before = self.product.balance(ACCOUNT_COFFER)["spendable_centicredits"]
nic_before = self.product.balance(ACCOUNT_NEW_INTERNET)["spendable_centicredits"]
sub = self.product.charge_subscription("explorer2")
self.assertEqual(sub["gross_credits"], 20)
self.assertEqual(sub["coffer_cut_centicredits"], 20) # 0.20c
self.assertEqual(sub["leora_net_centicredits"], 1980) # 19.80c
self.assertEqual(sub["coffer_cut_credits"], "0.20")
self.assertEqual(sub["leora_net_credits"], "19.80")
# Agent: 40 - 20 = 20 left
self.assertEqual(self.product.balance("explorer2")["spendable"], 20.0)
# Coffer kept 0.20
self.assertEqual(
self.product.balance(ACCOUNT_COFFER)["spendable_centicredits"],
coffer_before + 20,
)
# New Internet Co received 19.80
self.assertEqual(
self.product.balance(ACCOUNT_NEW_INTERNET)["spendable_centicredits"],
nic_before + 1980,
)
def test_signup_flow_wallet_grant_and_sub(self):
self.product.bootstrap_opening_books()
self.product.record_build_contract()
flow = self.product.signup_flow("newbie")
self.assertEqual(flow["grant"]["granted_credits"], 40)
self.assertEqual(flow["subscription"]["gross_credits"], 20)
self.assertEqual(self.product.balance("newbie")["spendable"], 20.0)
def test_shirt_purchase_20c(self):
self.product.bootstrap_opening_books()
self.product.record_build_contract()
self.product.signup_flow("shopper") # left with 20c of granted credit: enough for a shirt
out = self.product.buy_shirt("shopper")
self.assertEqual(out["order"]["status"], "pending_review")
self.assertIn("Your order is waiting for confirmation.", out["notice"])
self.assertIn("If it's rejected, your credits are released in full.", out["notice"])
self.assertEqual((out["balance"]["spendable"], out["balance"]["held"]), (0.0, 20.0))
self.assertEqual([t["kind"] for t in out["txs"]], ["order_hold"])
done = self.product.accept_order(out["order"]["order_id"], "agent:leora")
self.assertEqual(done["order"]["credits_charged"], 20)
self.assertEqual(done["order"]["sku"], "coffer-shirt")
self.assertEqual(done["order"]["status"], "procuring")
self.assertEqual((done["balance"]["spendable"], done["balance"]["held"]), (0.0, 0.0))
self.assertEqual([t["kind"] for t in done["txs"]], ["purchase"])
def test_full_history_and_company_vs_agent_books(self):
self.product.bootstrap_opening_books()
self.product.record_build_contract()
self.product.signup_flow("hist-agent")
o = self.product.buy_shirt("hist-agent")
self.product.accept_order(o["order"]["order_id"], "agent:leora")
hist = self.product.history()
kinds = [t["kind"] for t in hist]
self.assertEqual(kinds[0], "contract_payment")
self.assertIn("grant", kinds)
self.assertIn("subscription_charge", kinds)
self.assertIn("subscription_forward", kinds)
self.assertIn("purchase", kinds)
# Company books separate from founder agent
self.assertNotEqual(ACCOUNT_COFFER, agent_account("cursorman"))
self.assertTrue(ACCOUNT_COFFER.startswith("company:"))
self.assertTrue(ACCOUNT_LEORA.startswith("agent:"))
def test_cut_math(self):
self.assertEqual(cut_subunits(to_subunits(20), 100), 20)
self.assertEqual(cut_subunits(to_subunits(20), 0), 0)
if __name__ == "__main__":
unittest.main()