coffer/tests/test_product.py

147 lines
6.1 KiB
Python

import tempfile
import unittest
from coffer.ledger import GrantedCreditNotForGoods
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
with self.assertRaises(GrantedCreditNotForGoods):
self.product.buy_shirt("shopper") # free grant credit never buys physical goods
self.assertEqual(self.product.balance("shopper")["spendable"], 20.0)
self.product.top_up("shopper", 20) # funded credit can
out = self.product.buy_shirt("shopper")
self.assertEqual(out["order"]["credits_charged"], 20)
self.assertEqual(out["order"]["sku"], "coffer-shirt")
self.assertEqual((out["balance"]["spendable"], out["balance"]["granted"], out["balance"]["funded"]), (20.0, 20.0, 0.0))
self.assertEqual(len(out["txs"]), 1)
self.assertEqual(out["txs"][0]["kind"], "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")
self.product.top_up("hist-agent", 20)
self.product.buy_shirt("hist-agent")
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()