"""Test subscription manager — $1/month subscription with free trial. Payment integration with Soulmate OS wallet API. """ import sys import os import tempfile import time import json from unittest.mock import patch, MagicMock sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from splitbit_llm.subscription import ( SubscriptionManager, MONTHLY_PRICE, SOULMATE_API_URL, FOUNDER_EMAIL, FOUNDER_PASSWORD, ACCEPTED_TOKENS, NETWORK, BANK_ROUTING, BANK_ACCOUNT, AUTO_TRANSFER_ENABLED, ) def test_no_subscription_blocks_access(): """Test that no subscription blocks access.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) access = sub.check_access() assert not access["has_access"] assert access["status"] == "none" print(f" No subscription: access blocked — '{access['message']}'") def test_free_trial(): """Test 7-day free trial.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Start trial result = sub.start_trial() assert result["success"] assert result["status"] == "trial" assert result["days_remaining"] == 7 print(f" Trial started: {result['days_remaining']} days") # Access should be granted access = sub.check_access() assert access["has_access"] assert access["status"] == "trial" print(f" Trial access: granted — '{access['message']}'") # Can't start trial again result2 = sub.start_trial() assert not result2["success"] print(f" Second trial: blocked — '{result2['error']}'") def test_subscribe(): """Test $1/month subscription.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Subscribe result = sub.subscribe(create_deposit=False) assert result["success"] assert result["status"] == "active" assert result["price"] == MONTHLY_PRICE assert result["days_remaining"] >= 29 print(f" Subscribed: ${result['price']:.2f}/month, {result['days_remaining']} days") # Access granted access = sub.check_access() assert access["has_access"] assert access["status"] == "active" print(f" Access: granted — '{access['message']}'") # Stats stats = sub.get_stats() assert stats["months_subscribed"] == 1 assert stats["total_paid"] == MONTHLY_PRICE assert stats["auto_renew"] is True assert len(stats["payment_history"]) == 1 print(f" Stats: {stats['months_subscribed']} month, ${stats['total_paid']:.2f} paid") def test_subscribe_extends(): """Test that subscribing while active extends the subscription.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # First subscription result1 = sub.subscribe(create_deposit=False) assert result1["success"] assert result1["days_remaining"] >= 29 # Second subscription should extend result2 = sub.subscribe(create_deposit=False) assert result2["success"] assert result2["days_remaining"] >= 59 # 30 + 30 print(f" Extended: {result2['days_remaining']} days (30 + 30)") stats = sub.get_stats() assert stats["months_subscribed"] == 2 assert stats["total_paid"] == MONTHLY_PRICE * 2 print(f" Total: {stats['months_subscribed']} months, ${stats['total_paid']:.2f}") def test_unsubscribe(): """Test cancelling subscription.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub.subscribe(create_deposit=False) # Unsubscribe result = sub.unsubscribe() assert result["success"] assert result["status"] == "cancelled" print(f" Unsubscribed: {result['message']}") # Access still works until expiration access = sub.check_access() assert access["has_access"] # still has access until expiration print(f" Access after cancel: still granted until expiration") def test_expired_blocks_access(): """Test that expired subscription blocks access.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Subscribe sub.subscribe(create_deposit=False) # Manually expire the subscription sub._state["expiration_date"] = time.time() - 1 # expired 1 second ago sub._state["auto_renew"] = False sub._save_state() # Should enter grace period first access = sub.check_access() assert access["has_access"] # grace period assert access["status"] == "grace" print(f" Grace period: {access['days_remaining']} days — '{access['message']}'") # Expire grace period sub._state["expiration_date"] = time.time() - (4 * 86400) # 4 days ago (past 3-day grace) sub._save_state() access2 = sub.check_access() assert not access2["has_access"] assert access2["status"] == "expired" print(f" Expired: access blocked — '{access2['message']}'") def test_auto_renew(): """Test auto-renewal when subscription expires.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub.subscribe(create_deposit=False) months_before = sub._state["months_subscribed"] # Simulate expiration with auto-renew on sub._state["expiration_date"] = time.time() - 1 sub._state["auto_renew"] = True sub._save_state() access = sub.check_access() assert access["has_access"] assert access["status"] == "active" # auto-renewed print(f" Auto-renewed: {access['days_remaining']} days — '{access['message']}'") stats = sub.get_stats() assert stats["months_subscribed"] == months_before + 1 print(f" Months subscribed: {stats['months_subscribed']} (auto-renewed)") def test_trial_persistence(): """Test that subscription state persists across restarts.""" with tempfile.TemporaryDirectory() as tmpdir: sub1 = SubscriptionManager(data_dir=tmpdir) sub1.subscribe(create_deposit=False) # Create new instance — should load state sub2 = SubscriptionManager(data_dir=tmpdir) stats = sub2.get_stats() assert stats["status"] == "active" assert stats["months_subscribed"] == 1 print(f" Persisted: status={stats['status']}, months={stats['months_subscribed']}") access = sub2.check_access() assert access["has_access"] print(f" Access after restart: granted") def test_trial_then_subscribe(): """Test using trial first, then subscribing.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Start trial trial = sub.start_trial() assert trial["success"] print(f" Trial: {trial['days_remaining']} days") # Subscribe while on trial result = sub.subscribe(create_deposit=False) assert result["success"] assert result["status"] == "active" print(f" Subscribed during trial: {result['days_remaining']} days") # Can't start trial again trial2 = sub.start_trial() assert not trial2["success"] print(f" Trial after subscribe: blocked") def test_payment_history(): """Test payment history tracking.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Multiple payments sub.subscribe(payment_reference="stripe-001", create_deposit=False) sub.subscribe(payment_reference="stripe-002", create_deposit=False) sub.subscribe(payment_reference="stripe-003", create_deposit=False) stats = sub.get_stats() assert stats["months_subscribed"] == 3 assert stats["total_paid"] == MONTHLY_PRICE * 3 assert len(stats["payment_history"]) == 3 print(f" Payments: {stats['months_subscribed']} months, ${stats['total_paid']:.2f}") print(f" History: {len(stats['payment_history'])} records") def test_payment_instructions(): """Test payment instructions are returned correctly.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Set a fake wallet in state so we don't hit the API sub._state["founder_wallet"] = "0xABC123DEF456" sub._founder_wallet = "0xABC123DEF456" instructions = sub.get_payment_instructions() assert instructions["amount"] == MONTHLY_PRICE assert instructions["currency"] == "USD" assert instructions["period"] == "monthly" assert instructions["founder_wallet"] == "0xABC123DEF456" assert instructions["network"] == NETWORK assert "USDT" in instructions["accepted_tokens"] assert "USDC" in instructions["accepted_tokens"] assert "BNB" in instructions["accepted_tokens"] assert "INC" in instructions["accepted_tokens"] assert "soulmate_wallet_url" in instructions assert SOULMATE_API_URL in instructions["soulmate_wallet_url"] print(f" Amount: ${instructions['amount']:.2f}") print(f" Wallet: {instructions['founder_wallet']}") print(f" Network: {instructions['network']}") print(f" Tokens: {instructions['accepted_tokens']}") print(f" Google Pay URL: {instructions['soulmate_wallet_url']}") def test_payment_instructions_with_wallet(): """Test payment instructions include wallet from state.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub._state["founder_wallet"] = "0xDEADBEEF1234" sub._founder_wallet = "0xDEADBEEF1234" instructions = sub.get_payment_instructions() assert instructions["founder_wallet"] == "0xDEADBEEF1234" assert "0xDEADBEEF1234" in instructions["instructions"] print(f" Wallet in instructions: {instructions['founder_wallet']}") def test_create_deposit(): """Test deposit creation (mocked API).""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub._state["founder_wallet"] = "0xTEST123" sub._founder_wallet = "0xTEST123" # Mock the API call — deposit creation will fall back to pending result = sub.create_deposit("test-user", "USDT") assert "deposit_id" in result assert result["founder_wallet"] == "0xTEST123" assert result["token"] == "USDT" assert result["amount"] == MONTHLY_PRICE print(f" Deposit ID: {result['deposit_id'][:16]}...") print(f" Status: {result.get('status', 'pending')}") print(f" Wallet: {result['founder_wallet']}") def test_verify_payment(): """Test payment verification (mocked API — will return error since no real API).""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Verify a non-existent deposit — should return some status result = sub.verify_payment("fake-deposit-123") assert isinstance(result, dict) # Without a real API, it'll be "error", "not_found", or have some other status print(f" Verify result: {result.get('status', 'unknown')} — '{result.get('message', '')}'") def test_subscribe_with_deposit(): """Test subscribing with deposit creation.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub._state["founder_wallet"] = "0xWALLET456" sub._founder_wallet = "0xWALLET456" result = sub.subscribe(user_id="test-user", token="USDT") assert result["success"] assert result["status"] == "active" assert "deposit_id" in result assert result["founder_wallet"] == "0xWALLET456" assert "payment" in result assert result["payment"]["founder_wallet"] == "0xWALLET456" print(f" Subscribed with deposit: {result['deposit_id'][:16]}...") print(f" Payment instructions included: {bool(result.get('payment'))}") # Check payment history has deposit info stats = sub.get_stats() last_payment = stats["payment_history"][-1] assert last_payment["token"] == "USDT" assert last_payment["deposit_id"] == result["deposit_id"] assert "0xWALLET456" in last_payment["founder_wallet"] or last_payment["founder_wallet"].startswith("0xWALLET") print(f" Payment history: token={last_payment['token']}, deposit={last_payment['deposit_id'][:16]}...") def test_soulmate_config(): """Test Soulmate OS config constants are set correctly.""" assert SOULMATE_API_URL == "https://191.44.121.29.sslip.io" assert FOUNDER_EMAIL == "hawpetossjustin25@gmail.com" assert "USDT" in ACCEPTED_TOKENS assert "USDC" in ACCEPTED_TOKENS assert "BNB" in ACCEPTED_TOKENS assert "INC" in ACCEPTED_TOKENS assert NETWORK == "BSC (Binance Smart Chain)" print(f" API URL: {SOULMATE_API_URL}") print(f" Founder: {FOUNDER_EMAIL}") print(f" Tokens: {ACCEPTED_TOKENS}") print(f" Network: {NETWORK}") def test_founder_unlock_correct_password(): """Test founder unlock with correct password grants free forever access.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # No access before unlock access = sub.check_access() assert not access["has_access"] # Unlock with correct password result = sub.founder_unlock(FOUNDER_PASSWORD) assert result["success"] assert result["status"] == "founder" print(f" Unlock: {result['message']}") # Now has access — forever access = sub.check_access() assert access["has_access"] assert access["status"] == "founder" assert access["days_remaining"] == -1 # forever print(f" Access: {access['status']} — {access['message']}") def test_founder_unlock_wrong_password(): """Test founder unlock with wrong password fails.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) result = sub.founder_unlock("wrongpassword123") assert not result["success"] assert "Invalid" in result["error"] print(f" Wrong password: {result['error']}") # Still no access access = sub.check_access() assert not access["has_access"] print(f" Access after wrong password: blocked") def test_founder_unlock_persists(): """Test founder unlock persists across restarts.""" with tempfile.TemporaryDirectory() as tmpdir: sub1 = SubscriptionManager(data_dir=tmpdir) sub1.founder_unlock(FOUNDER_PASSWORD) # New instance should load founder status sub2 = SubscriptionManager(data_dir=tmpdir) access = sub2.check_access() assert access["has_access"] assert access["status"] == "founder" print(f" Persisted: status={access['status']}, forever={access['days_remaining'] == -1}") def test_founder_unlock_bypasses_payment(): """Test founder doesn't need to pay — access is free forever.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub.founder_unlock(FOUNDER_PASSWORD) # Founder should have no payment history stats = sub.get_stats() assert stats["months_subscribed"] == 0 assert stats["total_paid"] == 0.0 print(f" Founder payments: ${stats['total_paid']:.2f} (free)") # But has full access access = sub.check_access() assert access["has_access"] print(f" Founder access: free forever") def test_auto_transfer_init(): """Test auto-transfer state initializes correctly.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub._init_auto_transfer() assert "auto_transfer_history" in sub._state assert "transferred_payments" in sub._state assert "auto_transfer_enabled" in sub._state print(f" Auto-transfer enabled: {sub._state['auto_transfer_enabled']}") def test_auto_transfer_stats_empty(): """Test auto-transfer stats with no transfers.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) stats = sub.get_auto_transfer_stats() assert stats["transfer_count"] == 0 assert stats["total_transferred"] == 0.0 assert "auto_transfer_enabled" in stats print(f" Stats: {stats['transfer_count']} transfers, ${stats['total_transferred']:.2f}") def test_auto_transfer_process_no_payments(): """Test auto-transfer when there are no payments to transfer.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) result = sub.process_auto_transfers() assert result["status"] == "ok" assert result["transfers_made"] == 0 print(f" No payments: {result['message']}") def test_auto_transfer_process_with_payment(): """Test auto-transfer processes a confirmed $1 payment.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Simulate a confirmed payment sub.subscribe(payment_reference="test-pay-001", create_deposit=False) # Process auto-transfers result = sub.process_auto_transfers() assert result["status"] == "ok" assert result["transfers_made"] == 1 assert result["total_amount"] == MONTHLY_PRICE print(f" Processed: {result['transfers_made']} transfer(s), ${result['total_amount']:.2f}") # Verify transfer recorded stats = sub.get_auto_transfer_stats() assert stats["transfer_count"] == 1 assert stats["total_transferred"] == MONTHLY_PRICE print(f" Recorded: {stats['transfer_count']} transfer(s), ${stats['total_transferred']:.2f}") def test_auto_transfer_no_duplicates(): """Test auto-transfer doesn't transfer the same payment twice.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) sub.subscribe(payment_reference="test-pay-002", create_deposit=False) # First transfer result1 = sub.process_auto_transfers() assert result1["transfers_made"] == 1 # Second call should not re-transfer result2 = sub.process_auto_transfers() assert result2["transfers_made"] == 0 assert "No pending transfers" in result2["message"] print(f" No duplicates: first={result1['transfers_made']}, second={result2['transfers_made']}") def test_auto_transfer_toggle(): """Test enabling/disabling auto-transfer.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Disable result = sub.set_auto_transfer(False) assert result["success"] assert not result["auto_transfer_enabled"] # Process should be skipped sub.subscribe(payment_reference="test-pay-003", create_deposit=False) process_result = sub.process_auto_transfers() assert process_result["status"] == "disabled" print(f" Disabled: {process_result['message']}") # Re-enable result = sub.set_auto_transfer(True) assert result["success"] assert result["auto_transfer_enabled"] print(f" Re-enabled: {result['message']}") def test_auto_transfer_multiple_payments(): """Test auto-transfer processes multiple payments at once.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) # Three payments sub.subscribe(payment_reference="pay-001", create_deposit=False) sub.subscribe(payment_reference="pay-002", create_deposit=False) sub.subscribe(payment_reference="pay-003", create_deposit=False) # Process all result = sub.process_auto_transfers() assert result["transfers_made"] == 3 assert result["total_amount"] == MONTHLY_PRICE * 3 print(f" Batch: {result['transfers_made']} transfers, ${result['total_amount']:.2f}") stats = sub.get_auto_transfer_stats() assert stats["transfer_count"] == 3 assert stats["total_transferred"] == MONTHLY_PRICE * 3 print(f" Total: {stats['transfer_count']} transfers, ${stats['total_transferred']:.2f}") def test_bank_info(): """Test bank info returns masked account details.""" with tempfile.TemporaryDirectory() as tmpdir: sub = SubscriptionManager(data_dir=tmpdir) info = sub.get_bank_info() assert "routing_number" in info assert "account_number" in info assert "auto_transfer_enabled" in info assert "total_transferred" in info # Should be masked or "Not set" assert info["routing_number"] == "Not set" or "****" in info["routing_number"] assert info["account_number"] == "Not set" or "****" in info["account_number"] print(f" Routing: {info['routing_number']}") print(f" Account: {info['account_number']}") def test_auto_transfer_persists(): """Test auto-transfer history persists across restarts.""" with tempfile.TemporaryDirectory() as tmpdir: sub1 = SubscriptionManager(data_dir=tmpdir) sub1.subscribe(payment_reference="persist-pay-001", create_deposit=False) sub1.process_auto_transfers() # New instance sub2 = SubscriptionManager(data_dir=tmpdir) stats = sub2.get_auto_transfer_stats() assert stats["transfer_count"] == 1 assert stats["total_transferred"] == MONTHLY_PRICE print(f" Persisted: {stats['transfer_count']} transfers, ${stats['total_transferred']:.2f}") if __name__ == "__main__": print("Running subscription manager tests...") test_no_subscription_blocks_access() print(" ✓ test_no_subscription_blocks_access") test_free_trial() print(" ✓ test_free_trial") test_subscribe() print(" ✓ test_subscribe") test_subscribe_extends() print(" ✓ test_subscribe_extends") test_unsubscribe() print(" ✓ test_unsubscribe") test_expired_blocks_access() print(" ✓ test_expired_blocks_access") test_auto_renew() print(" ✓ test_auto_renew") test_trial_persistence() print(" ✓ test_trial_persistence") test_trial_then_subscribe() print(" ✓ test_trial_then_subscribe") test_payment_history() print(" ✓ test_payment_history") test_payment_instructions() print(" ✓ test_payment_instructions") test_payment_instructions_with_wallet() print(" ✓ test_payment_instructions_with_wallet") test_create_deposit() print(" ✓ test_create_deposit") test_verify_payment() print(" ✓ test_verify_payment") test_subscribe_with_deposit() print(" ✓ test_subscribe_with_deposit") test_soulmate_config() print(" ✓ test_soulmate_config") test_founder_unlock_correct_password() print(" ✓ test_founder_unlock_correct_password") test_founder_unlock_wrong_password() print(" ✓ test_founder_unlock_wrong_password") test_founder_unlock_persists() print(" ✓ test_founder_unlock_persists") test_founder_unlock_bypasses_payment() print(" ✓ test_founder_unlock_bypasses_payment") test_auto_transfer_init() print(" ✓ test_auto_transfer_init") test_auto_transfer_stats_empty() print(" ✓ test_auto_transfer_stats_empty") test_auto_transfer_process_no_payments() print(" ✓ test_auto_transfer_process_no_payments") test_auto_transfer_process_with_payment() print(" ✓ test_auto_transfer_process_with_payment") test_auto_transfer_no_duplicates() print(" ✓ test_auto_transfer_no_duplicates") test_auto_transfer_toggle() print(" ✓ test_auto_transfer_toggle") test_auto_transfer_multiple_payments() print(" ✓ test_auto_transfer_multiple_payments") test_bank_info() print(" ✓ test_bank_info") test_auto_transfer_persists() print(" ✓ test_auto_transfer_persists") print("\nAll subscription tests passed!")