Skip to content

Commit c16925e

Browse files
committed
feat: add tests for handling zero and negative balance scenarios in friends' balance summary
1 parent 338722a commit c16925e

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

backend/tests/expenses/test_expense_service.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2192,7 +2192,7 @@ async def test_get_friends_balance_summary_user_fetch_error(expense_service):
21922192

21932193
@pytest.mark.asyncio
21942194
async def test_get_friends_balance_summary_zero_balance_filtering(expense_service):
2195-
"""Test that friends with zero balance are filtered out"""
2195+
"""Test that friends with zero balance are filtered out - covers line 1061"""
21962196
user_id_str = str(ObjectId())
21972197

21982198
with patch("app.expenses.service.mongodb") as mock_mongodb:
@@ -2226,5 +2226,54 @@ async def test_get_friends_balance_summary_zero_balance_filtering(expense_servic
22262226
assert result["summary"]["activeGroups"] == 1
22272227

22282228

2229+
@pytest.mark.asyncio
2230+
async def test_get_friends_balance_summary_negative_balance(expense_service):
2231+
"""Test friends balance with negative balance (user owes) - covers line 1141"""
2232+
user_id_str = str(ObjectId())
2233+
friend_id_str = str(ObjectId())
2234+
group_id = str(ObjectId())
2235+
2236+
with patch("app.expenses.service.mongodb") as mock_mongodb:
2237+
mock_db = MagicMock()
2238+
mock_mongodb.database = mock_db
2239+
2240+
# Mock groups
2241+
mock_groups = [
2242+
{
2243+
"_id": ObjectId(group_id),
2244+
"name": "Test Group",
2245+
"members": [{"userId": user_id_str}, {"userId": friend_id_str}],
2246+
}
2247+
]
2248+
mock_groups_cursor = AsyncMock()
2249+
mock_groups_cursor.to_list.return_value = mock_groups
2250+
mock_db.groups.find.return_value = mock_groups_cursor
2251+
2252+
# Mock aggregation with NEGATIVE balance (user owes friend)
2253+
mock_agg_cursor = AsyncMock()
2254+
mock_agg_cursor.to_list.return_value = [
2255+
{
2256+
"_id": friend_id_str,
2257+
"totalBalance": -100.0, # Negative = user owes friend
2258+
"groups": [{"groupId": group_id, "balance": -100.0}],
2259+
}
2260+
]
2261+
mock_db.settlements.aggregate.return_value = mock_agg_cursor
2262+
2263+
# Mock user fetch
2264+
mock_users_cursor = AsyncMock()
2265+
mock_users_cursor.to_list.return_value = [
2266+
{"_id": ObjectId(friend_id_str), "name": "Friend", "imageUrl": None}
2267+
]
2268+
mock_db.users.find.return_value = mock_users_cursor
2269+
2270+
result = await expense_service.get_friends_balance_summary(user_id_str)
2271+
2272+
# Should have totalYouOwe = 100 (covers line 1141 - else branch)
2273+
assert result["summary"]["totalOwedToYou"] == 0
2274+
assert result["summary"]["totalYouOwe"] == 100.0
2275+
assert result["summary"]["netBalance"] == -100.0
2276+
2277+
22292278
if __name__ == "__main__":
22302279
pytest.main([__file__])

backend/tests/groups/test_enrich_members.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ async def test_enrich_members_with_user_details_success(self):
7171

7272
@pytest.mark.asyncio
7373
async def test_enrich_members_empty_list(self):
74-
"""Test enrichment with empty members list"""
74+
"""Test enrichment with empty members list - covers line 35"""
7575
mock_db = MagicMock()
7676

7777
with patch.object(self.service, "get_db", return_value=mock_db):
@@ -81,6 +81,55 @@ async def test_enrich_members_empty_list(self):
8181
# Verify no database call was made
8282
mock_db.users.find.assert_not_called()
8383

84+
@pytest.mark.asyncio
85+
async def test_enrich_members_invalid_object_ids(self):
86+
"""Test enrichment with invalid ObjectIds - covers lines 46-47"""
87+
members = [
88+
{"userId": "invalid_id_123", "role": "admin", "joinedAt": "2023-01-01"},
89+
{"userId": "also_invalid", "role": "member", "joinedAt": "2023-01-02"},
90+
]
91+
92+
mock_db = MagicMock()
93+
94+
with patch.object(self.service, "get_db", return_value=mock_db):
95+
enriched = await self.service._enrich_members_with_user_details(members)
96+
97+
# Should return fallback members since no valid ObjectIds - covers line 52
98+
assert len(enriched) == 2
99+
assert "User" in enriched[0]["user"]["name"]
100+
assert enriched[0]["role"] == "admin"
101+
102+
@pytest.mark.asyncio
103+
async def test_enrich_members_member_without_userId(self):
104+
"""Test enrichment when member has no userId - covers line 99"""
105+
user_id_1 = str(ObjectId())
106+
107+
members = [
108+
{"userId": user_id_1, "role": "admin", "joinedAt": "2023-01-01"},
109+
{"role": "member", "joinedAt": "2023-01-02"}, # No userId
110+
]
111+
112+
mock_users = [
113+
{"_id": ObjectId(user_id_1), "name": "Admin User", "imageUrl": "admin.jpg"},
114+
]
115+
116+
mock_db = MagicMock()
117+
mock_users_collection = MagicMock()
118+
mock_db.users = mock_users_collection
119+
120+
mock_cursor = AsyncMock()
121+
mock_cursor.to_list.return_value = mock_users
122+
mock_users_collection.find.return_value = mock_cursor
123+
124+
with patch.object(self.service, "get_db", return_value=mock_db):
125+
enriched = await self.service._enrich_members_with_user_details(members)
126+
127+
assert len(enriched) == 2
128+
assert enriched[0]["user"]["name"] == "Admin User"
129+
# Second member should be returned as-is since no userId
130+
assert enriched[1]["role"] == "member"
131+
assert "user" not in enriched[1] or enriched[1] == members[1]
132+
84133
@pytest.mark.asyncio
85134
async def test_enrich_members_missing_user_data(self):
86135
"""Test enrichment when some users are not found in database"""

0 commit comments

Comments
 (0)