Commit cdd7db8e authored by Your Name's avatar Your Name

feat(payments): implement quota enforcement with creation order

parent c4e12078
...@@ -4,5 +4,6 @@ Subscription management module ...@@ -4,5 +4,6 @@ Subscription management module
from aisbf.payments.subscription.manager import SubscriptionManager from aisbf.payments.subscription.manager import SubscriptionManager
from aisbf.payments.subscription.renewal import RenewalProcessor from aisbf.payments.subscription.renewal import RenewalProcessor
from aisbf.payments.subscription.retry import PaymentRetryProcessor from aisbf.payments.subscription.retry import PaymentRetryProcessor
from aisbf.payments.subscription.quota import QuotaEnforcer
__all__ = ['SubscriptionManager', 'RenewalProcessor', 'PaymentRetryProcessor'] __all__ = ['SubscriptionManager', 'RenewalProcessor', 'PaymentRetryProcessor', 'QuotaEnforcer']
This diff is collapsed.
import pytest
from aisbf.database import DatabaseManager
from aisbf.payments.migrations import PaymentMigrations
from aisbf.payments.subscription.quota import QuotaEnforcer
@pytest.fixture
def db_manager(tmp_path):
"""Create test database"""
db_path = tmp_path / "test.db"
db_config = {
'type': 'sqlite',
'sqlite_path': str(db_path)
}
db = DatabaseManager(db_config)
migrations = PaymentMigrations(db)
migrations.run_migrations()
# Create test user
user_id = db.create_user(email='test@example.com', username='testuser', password_hash='hash')
return db
def test_quota_enforcement_creation_order(db_manager):
"""Test that oldest configs are used when quota exceeded"""
enforcer = QuotaEnforcer(db_manager)
# Create 3 rotations
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config, created_at) VALUES (1, 'rotation1', '{}', datetime('now', '-3 days'))")
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config, created_at) VALUES (1, 'rotation2', '{}', datetime('now', '-2 days'))")
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config, created_at) VALUES (1, 'rotation3', '{}', datetime('now', '-1 day'))")
conn.commit()
# Enforce quota with max_rotations=2
result = enforcer.enforce_quota(1, {'max_rotations': 2, 'max_autoselections': -1})
assert result['success'] == True
assert result['rotations']['active'] == 2
assert result['rotations']['inactive'] == 1
# Verify oldest 2 are active
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT rotation_id FROM user_rotations WHERE user_id = 1 AND is_active = 1 ORDER BY created_at")
active = [row[0] for row in cursor.fetchall()]
assert active == ['rotation1', 'rotation2']
def test_quota_enforcement_never_deletes(db_manager):
"""Test that configs are never deleted, only marked inactive"""
enforcer = QuotaEnforcer(db_manager)
# Create 3 rotations
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation1', '{}')")
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation2', '{}')")
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation3', '{}')")
conn.commit()
# Enforce quota with max_rotations=1
enforcer.enforce_quota(1, {'max_rotations': 1, 'max_autoselections': -1})
# Verify all configs still exist
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM user_rotations WHERE user_id = 1")
total = cursor.fetchone()[0]
assert total == 3 # All configs still exist
# Verify only 1 is active
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM user_rotations WHERE user_id = 1 AND is_active = 1")
active = cursor.fetchone()[0]
assert active == 1
def test_quota_enforcement_on_downgrade(db_manager):
"""Test quota enforcement when downgrading tiers"""
enforcer = QuotaEnforcer(db_manager)
# Create 5 rotations (simulating premium tier with max 10)
with db_manager._get_connection() as conn:
cursor = conn.cursor()
for i in range(5):
cursor.execute(f"INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation{i+1}', '{{}}')")
conn.commit()
# Initially all active (premium tier)
enforcer.enforce_quota(1, {'max_rotations': 10, 'max_autoselections': -1})
# Downgrade to basic tier (max 2)
result = enforcer.enforce_quota(1, {'max_rotations': 2, 'max_autoselections': -1})
assert result['rotations']['active'] == 2
assert result['rotations']['inactive'] == 3
assert result['rotations']['total'] == 5
def test_quota_enforcement_on_upgrade(db_manager):
"""Test quota expansion when upgrading tiers"""
enforcer = QuotaEnforcer(db_manager)
# Create 5 rotations
with db_manager._get_connection() as conn:
cursor = conn.cursor()
for i in range(5):
cursor.execute(f"INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation{i+1}', '{{}}')")
conn.commit()
# Start with basic tier (max 2) - 3 inactive
enforcer.enforce_quota(1, {'max_rotations': 2, 'max_autoselections': -1})
# Verify 2 active, 3 inactive
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM user_rotations WHERE user_id = 1 AND is_active = 1")
active_before = cursor.fetchone()[0]
assert active_before == 2
# Upgrade to premium tier (max 10)
result = enforcer.reactivate_configs(1, {'max_rotations': 10, 'max_autoselections': -1})
# All 5 should now be active
assert result['rotations']['active'] == 5
assert result['rotations']['inactive'] == 0
def test_get_active_configs(db_manager):
"""Test getting active configs within quota"""
enforcer = QuotaEnforcer(db_manager)
# Create rotations and autoselects
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation1', '{}')")
cursor.execute("INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation2', '{}')")
cursor.execute("INSERT INTO user_autoselects (user_id, autoselect_id, config) VALUES (1, 'autoselect1', '{}')")
conn.commit()
# Enforce quota
enforcer.enforce_quota(1, {'max_rotations': 2, 'max_autoselections': 1})
# Get active configs
active = enforcer.get_active_configs(1)
assert len(active['rotations']) == 2
assert len(active['autoselects']) == 1
def test_unlimited_quota(db_manager):
"""Test that -1 means unlimited quota"""
enforcer = QuotaEnforcer(db_manager)
# Create 10 rotations
with db_manager._get_connection() as conn:
cursor = conn.cursor()
for i in range(10):
cursor.execute(f"INSERT INTO user_rotations (user_id, rotation_id, config) VALUES (1, 'rotation{i+1}', '{{}}')")
conn.commit()
# Enforce with unlimited quota
result = enforcer.enforce_quota(1, {'max_rotations': -1, 'max_autoselections': -1})
# All should remain active
with db_manager._get_connection() as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM user_rotations WHERE user_id = 1 AND is_active = 1")
active = cursor.fetchone()[0]
assert active == 10
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment