fix(spend): keep CloudZero and Focus spend-user email when filling alias

Export rows already join user_email from DailyUserSpend.user_id. Recovered
key-owner email must not replace that when only the alias join missed.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-09-04 01:38:26 +00:00
parent ce95afe2bd
commit 33815682bc
No known key found for this signature in database
2 changed files with 30 additions and 1 deletions

View file

@ -186,7 +186,7 @@ def _row_with_recovered_fields(
**row,
alias_field: meta.get("key_alias") or row.get(alias_field),
team_id_field: meta.get("team_id") or row.get(team_id_field),
user_email_field: meta.get("user_email") or row.get(user_email_field),
user_email_field: row.get(user_email_field) or meta.get("user_email"),
}
)

View file

@ -170,3 +170,32 @@ async def test_fill_missing_api_key_aliases_leaves_rows_untouched_when_nothing_i
assert filled == rows
mock_prisma.db.query_raw.assert_not_called()
@pytest.mark.asyncio
async def test_fill_missing_api_key_aliases_keeps_spend_user_email_when_alias_is_missing():
double_hashed = hash_token("f" * 64)
mock_prisma = MagicMock()
mock_prisma.db.query_raw = _query_raw_by_table(
active_rows=[_digest_row(double_hashed, "team-key", "team-9", "key-owner")],
deleted_rows=[],
)
mock_prisma.db.litellm_usertable.find_many = AsyncMock(
return_value=[SimpleNamespace(user_id="key-owner", user_email="owner@example.com")]
)
rows = (
{
"api_key": double_hashed,
"api_key_alias": None,
"team_id": None,
"user_email": "spender@example.com",
"spend": 4.0,
},
)
filled = await fill_missing_api_key_aliases(mock_prisma, rows)
assert filled[0]["api_key_alias"] == "team-key"
assert filled[0]["team_id"] == "team-9"
assert filled[0]["user_email"] == "spender@example.com"