Feat: Convert data_models.py cloud/billing models

This commit is contained in:
2025-12-05 13:19:26 -05:00
parent 407b7ec53e
commit 5ed288ff5a
8 changed files with 712 additions and 5 deletions
+289
View File
@@ -1,6 +1,7 @@
"""Step definitions for domain models testing."""
from datetime import datetime
from decimal import Decimal
from pathlib import Path
from behave import given, then, when
@@ -11,16 +12,55 @@ from cleveragents.domain.models.aimodelsdatamodels import AIModel, Provider
from cleveragents.domain.models.core import (
Change,
ChangeSet,
CloudBillingFields,
Context,
ContextType,
ContextUpdateResult,
CreditType,
CreditsTransaction,
CreditsTransactionType,
Invite,
MaxContextCount,
OperationType,
Org,
OrgRole,
OrgUser,
Plan,
PlanStatus,
Project,
ProjectSettings,
SummaryForUpdateContextParams,
User,
)
from cleveragents.domain.models.core.enums import ModelProvider
from cleveragents.domain.models.orguserconfig import OrgUserConfig
from cleveragents.domain.models.planconfig import PlanConfig
def _sample_plan_config() -> PlanConfig:
"""Create a reusable PlanConfig instance for tests."""
return PlanConfig(
auto_mode="manual",
editor="vim",
editor_command="vim",
editor_args=[],
editor_open_manually=False,
auto_continue=False,
auto_build=False,
auto_update_context=False,
auto_context=False,
smart_context=False,
auto_apply=False,
auto_commit=False,
skip_commit=False,
can_exec=False,
auto_exec=False,
auto_debug=False,
auto_debug_tries=0,
auto_revert_on_rewind=False,
skip_changes_menu=False,
)
@given("I have valid project data")
@@ -381,6 +421,255 @@ def step_stats_count_operations(context):
assert "applied" in context.stats
# Domain models from cloud/organization data
def _sample_billing_payload() -> dict[str, object]:
"""Provide representative camelCase billing payload."""
return {
"creditsBalance": "125.5",
"monthlyGrant": "200.0",
"autoRebuyEnabled": True,
"autoRebuyMinThreshold": "5.0",
"autoRebuyToBalance": "50.0",
"notifyThreshold": "10.0",
"maxThresholdPerMonth": "500.0",
"billingCycleStartedAt": "2025-01-01T00:00:00Z",
"changedBillingMode": False,
"trialPaid": True,
"stripeSubscriptionId": "sub-123",
"subscriptionStatus": "active",
"subscriptionPausedAt": "2025-01-10T00:00:00Z",
"stripePaymentMethod": "pm_abc",
"subscriptionActionRequired": False,
"subscriptionActionRequiredInvoiceUrl": None,
}
@given("I have sample cloud billing data")
def step_sample_cloud_billing(context):
"""Prepare representative cloud billing data."""
context.cloud_billing_data = _sample_billing_payload()
@when("I create a CloudBillingFields model")
def step_create_billing_fields(context):
"""Instantiate CloudBillingFields from camelCase payload."""
context.billing_fields = CloudBillingFields(**context.cloud_billing_data)
@then("the billing fields should expose decimal values")
def step_verify_billing_fields(context):
"""Validate decimal conversion for billing fields."""
billing = context.billing_fields
assert billing.credits_balance == Decimal("125.5")
assert billing.monthly_grant == Decimal("200.0")
assert billing.subscription_action_required is False
@given("I have organization data with billing")
def step_org_with_billing(context):
"""Prepare organization payload referencing billing fields."""
context.org_payload = {
"id": "org-1",
"name": "Example Org",
"isTrial": True,
"autoAddDomainUsers": True,
"integratedModelsMode": False,
}
context.org_billing_payload = _sample_billing_payload()
@when("I create an Org domain model")
def step_create_org_model(context):
"""Instantiate Org with nested billing fields."""
billing = CloudBillingFields(**context.org_billing_payload)
context.org_model = Org(cloud_billing_fields=billing, **context.org_payload)
@then("the org should include billing configuration")
def step_verify_org_model(context):
"""Ensure the org retains billing configuration."""
org = context.org_model
assert org.cloud_billing_fields is not None
assert org.cloud_billing_fields.notify_threshold == Decimal("10.0")
@given("I have an org role definition")
def step_org_role_definition(context):
"""Set up org role payload."""
context.org_role_data = {
"id": "role-1",
"isDefault": True,
"label": "Owner",
"description": "Full access",
}
@when("I create an OrgRole model")
def step_create_org_role(context):
"""Instantiate OrgRole."""
context.org_role = OrgRole(**context.org_role_data)
@then("the org role should retain its label")
def step_verify_org_role(context):
"""Ensure org role values persist."""
assert context.org_role.label == "Owner"
assert context.org_role.is_default is True
@given("I have user data referencing a default plan config")
def step_user_with_plan_config(context):
"""Prepare user payload referencing plan config."""
context.user_plan_config = _sample_plan_config()
context.user_data = {
"id": "user-1",
"name": "Casey",
"email": "casey@example.com",
"isTrial": False,
"numNonDraftPlans": 3,
}
@when("I create a User model")
def step_create_user_model(context):
"""Instantiate User with nested plan config."""
context.user_model = User(
default_plan_config=context.user_plan_config, **context.user_data
)
@then("the user should include the default plan config")
def step_verify_user_model(context):
"""Ensure user retains plan config."""
assert context.user_model.default_plan_config is not None
assert context.user_model.default_plan_config.auto_mode == "manual"
@given("I have an org user association")
def step_org_user_association(context):
"""Prepare OrgUser payload."""
context.org_user_config = OrgUserConfig(
prompted_claude_max=False,
use_claude_subscription=True,
claude_subscription_cooldown_started_at=datetime(2025, 1, 1, 0, 0, 0),
)
context.org_user_data = {
"orgId": "org-1",
"userId": "user-1",
"orgRoleId": "role-1",
}
@when("I create an OrgUser model")
def step_create_org_user(context):
"""Instantiate OrgUser."""
context.org_user_model = OrgUser(
config=context.org_user_config, **context.org_user_data
)
@then("the org user config should be attached")
def step_verify_org_user(context):
"""Ensure OrgUser carries config payload."""
assert context.org_user_model.config is not None
assert context.org_user_model.config.use_claude_subscription is True
@given("I have invite metadata")
def step_invite_metadata(context):
"""Prepare Invite payload."""
context.invite_data = {
"id": "invite-1",
"orgId": "org-1",
"email": "new@example.com",
"name": "New User",
"orgRoleId": "role-1",
"inviterId": "user-1",
"inviteeId": None,
"acceptedAt": "2025-02-01T12:00:00Z",
"createdAt": "2025-01-15T08:30:00Z",
}
@when("I create an Invite model")
def step_create_invite(context):
"""Instantiate Invite."""
context.invite_model = Invite(**context.invite_data)
@then("the invite should track acceptance timestamps")
def step_verify_invite(context):
"""Ensure Invite datetimes parsed."""
assert isinstance(context.invite_model.accepted_at, datetime)
assert context.invite_model.accepted_at.year == 2025
@given("I have credits transaction data")
def step_credits_transaction_data(context):
"""Prepare CreditsTransaction payload."""
context.credits_transaction_data = {
"id": "txn-1",
"orgId": "org-1",
"orgName": "Example Org",
"userId": "user-1",
"userEmail": "casey@example.com",
"userName": "Casey",
"transactionType": CreditsTransactionType.CREDIT,
"amount": "42.0",
"startBalance": "20.0",
"endBalance": "62.0",
"creditType": CreditType.TRIAL,
"creditIsAutoRebuy": True,
"creditAutoRebuyMinThreshold": "5.0",
"creditAutoRebuyToBalance": "40.0",
"debitModelProvider": ModelProvider.OPENAI,
"debitModelRole": "ModelRolePlanner",
"debitModelName": "gpt-4.1",
"debitPlanId": "plan-1",
"debitPlanName": "Core Plan",
"createdAt": "2025-01-20T00:00:00Z",
}
@when("I create a CreditsTransaction model")
def step_create_credits_transaction(context):
"""Instantiate CreditsTransaction."""
context.credits_transaction = CreditsTransaction(**context.credits_transaction_data)
@then("the credits transaction should track balances")
def step_verify_credits_transaction(context):
"""Ensure CreditsTransaction stores decimals and provider metadata."""
tx = context.credits_transaction
assert tx.amount == Decimal("42.0")
assert tx.debit_model_provider == ModelProvider.OPENAI
assert tx.end_balance == Decimal("62.0")
# Auto-generated models steps
@when("I import the auto-generated models")
def step_import_autogenerated(context):