"""Step definitions for CostTracker service tests.""" from __future__ import annotations import tempfile from pathlib import Path from behave import given, then, when from cleveragents.infrastructure.database.cost_tracker import CostRecord, CostTracker @given("a temporary data directory for cost tracking") def step_create_temp_dir(context): """Create a temporary directory for cost tracking.""" context.temp_dir = tempfile.mkdtemp() context.data_dir = Path(context.temp_dir) @given("a CostTracker instance with default model pricing") def step_create_cost_tracker(context): """Create a CostTracker instance with default pricing.""" context.cost_tracker = CostTracker(context.data_dir) context.recorded_records = [] @when('I record usage for session "{session_id}" and plan "{plan_id}"') def step_record_usage(context, session_id, plan_id): """Record usage for a session and plan.""" for row in context.table: tokens_in = int(row["tokens_in"]) tokens_out = int(row["tokens_out"]) model = row["model"] record = context.cost_tracker.record_usage( session_id=session_id, plan_id=plan_id, tokens_in=tokens_in, tokens_out=tokens_out, model=model, ) context.recorded_records.append(record) @then("the recorded cost should be approximately {cost} USD") def step_check_recorded_cost(context, cost): """Check the recorded cost.""" expected_cost = float(cost) actual_cost = context.recorded_records[-1].cost_usd assert abs(actual_cost - expected_cost) < 0.000001, ( f"Expected {expected_cost}, got {actual_cost}" ) @then('the cost record should have session_id "{session_id}"') def step_check_session_id(context, session_id): """Check the session_id of the recorded cost.""" assert context.recorded_records[-1].session_id == session_id @then('the cost record should have plan_id "{plan_id}"') def step_check_plan_id(context, plan_id): """Check the plan_id of the recorded cost.""" assert context.recorded_records[-1].plan_id == plan_id @when('I record multiple usages for session "{session_id}"') def step_record_multiple_usages_session(context, session_id): """Record multiple usages for a session.""" for row in context.table: plan_id = row["plan_id"] tokens_in = int(row["tokens_in"]) tokens_out = int(row["tokens_out"]) model = row["model"] record = context.cost_tracker.record_usage( session_id=session_id, plan_id=plan_id, tokens_in=tokens_in, tokens_out=tokens_out, model=model, ) context.recorded_records.append(record) @then('the total session cost for "{session_id}" should be approximately {cost} USD') def step_check_session_cost(context, session_id, cost): """Check the total cost for a session.""" expected_cost = float(cost) actual_cost = context.cost_tracker.get_session_cost(session_id) assert abs(actual_cost - expected_cost) < 0.000001, ( f"Expected {expected_cost}, got {actual_cost}" ) @when('I record multiple usages for plan "{plan_id}"') def step_record_multiple_usages_plan(context, plan_id): """Record multiple usages for a plan.""" for row in context.table: session_id = row["session_id"] tokens_in = int(row["tokens_in"]) tokens_out = int(row["tokens_out"]) model = row["model"] record = context.cost_tracker.record_usage( session_id=session_id, plan_id=plan_id, tokens_in=tokens_in, tokens_out=tokens_out, model=model, ) context.recorded_records.append(record) @then('the total plan cost for "{plan_id}" should be approximately {cost} USD') def step_check_plan_cost(context, plan_id, cost): """Check the total cost for a plan.""" expected_cost = float(cost) actual_cost = context.cost_tracker.get_plan_cost(plan_id) assert abs(actual_cost - expected_cost) < 0.000001, ( f"Expected {expected_cost}, got {actual_cost}" ) @when("I create a new CostTracker instance with the same data directory") def step_create_new_tracker(context): """Create a new CostTracker instance with the same data directory.""" context.cost_tracker = CostTracker(context.data_dir) @given("a CostTracker instance with custom model pricing") def step_create_tracker_custom_pricing(context): """Create a CostTracker instance with custom pricing.""" pricing = {} for row in context.table: model = row["model"] input_price = float(row["input_price"]) output_price = float(row["output_price"]) pricing[model] = {"input": input_price, "output": output_price} context.cost_tracker = CostTracker(context.data_dir, model_pricing=pricing) context.recorded_records = [] @then('I should get {count} entries for session "{session_id}"') def step_check_session_entries_count(context, count, session_id): """Check the number of entries for a session.""" entries = context.cost_tracker.get_session_entries(session_id) assert len(entries) == int(count), f"Expected {count} entries, got {len(entries)}" context.session_entries = entries @then("each entry should have the correct session_id and plan_id") def step_check_entries_ids(context): """Check that each entry has the correct IDs.""" entries = getattr(context, "session_entries", None) or getattr( context, "plan_entries", [] ) for entry in entries: assert isinstance(entry, CostRecord) assert entry.session_id is not None assert entry.plan_id is not None @then('I should get {count} entries for plan "{plan_id}"') def step_check_plan_entries_count(context, count, plan_id): """Check the number of entries for a plan.""" entries = context.cost_tracker.get_plan_entries(plan_id) assert len(entries) == int(count), f"Expected {count} entries, got {len(entries)}" context.plan_entries = entries