agent

Finished
1import json
2from typing import Dict, List, Optional, Any
3
14class RestAPI:
5 """
6 A RESTful API for tracking IOUs between users.
7
8 This class manages user data and IOU transactions in memory.
9 It supports creating users, creating IOUs, and retrieving user information.
10 """
11
212 def __init__(self, database: dict | None = None):
3 pass
4
13 """
14 Initialize the RestAPI with an optional database.
15
16 Args:
17 database: Optional initial database with users data
18 """
19 if database and 'users' in database:
20 # Initialize users from database
21 self.users = {}
22 for user_data in database['users']:
23 self.users[user_data['name']] = {
24 'name': user_data['name'],
25 'owes': user_data.get('owes', {}),
26 'owed_by': user_data.get('owed_by', {}),
27 'balance': user_data.get('balance', 0.0)
28 }
29 else:
30 # Initialize empty users dictionary
31 self.users = {}
32
533 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
34 """
35 Handle GET requests to the API.
36
37 Args:
38 url: The endpoint URL
39 payload: Optional JSON payload
40
41 Returns:
42 JSON string response
43 """
44 if url == '/users':
45 # Edge Case: Handle request with no payload (list all users)
46 if payload is None:
47 user_list = [self._get_user_details(name) for name in sorted(self.users.keys())]
48 return json.dumps({"users": user_list})
49
50 # Edge Case: Handle request with payload (list specific users)
51 try:
52 data = json.loads(payload)
53 if 'users' in data:
54 # Filter users by provided names
55 user_list = []
56 for name in sorted(data['users']):
57 if name in self.users:
58 user_list.append(self._get_user_details(name))
59 return json.dumps({"users": user_list})
60 else:
61 # Edge Case: Malformed payload (missing 'users' key)
62 return json.dumps({"users": []})
63 except json.JSONDecodeError:
64 # Edge Case: Invalid JSON payload
65 return json.dumps({"users": []})
66
67 # Edge Case: Unknown endpoint
68 return json.dumps({"error": "Not found"})
69
870 def post(self, url: str, payload: str | None = None) -> str:
9 pass
71 """
72 Handle POST requests to the API.
73
74 Args:
75 url: The endpoint URL
76 payload: JSON payload
77
78 Returns:
79 JSON string response
80 """
81 if payload is None:
82 # Edge Case: Missing payload
83 return json.dumps({"error": "Payload required"})
84
85 try:
86 data = json.loads(payload)
87 except json.JSONDecodeError:
88 # Edge Case: Invalid JSON payload
89 return json.dumps({"error": "Invalid JSON"})
90
91 if url == '/add':
92 # Edge Case: Missing 'user' in payload
93 if 'user' not in data:
94 return json.dumps({"error": "Missing user"})
95
96 user_name = data['user']
97
98 # Edge Case: User already exists
99 if user_name in self.users:
100 return json.dumps({"error": "User already exists"})
101
102 # Create new user
103 self.users[user_name] = {
104 'name': user_name,
105 'owes': {},
106 'owed_by': {},
107 'balance': 0.0
108 }
109
110 return json.dumps(self._get_user_details(user_name))
111
112 elif url == '/iou':
113 # Edge Case: Missing required fields
114 if not all(key in data for key in ['lender', 'borrower', 'amount']):
115 return json.dumps({"error": "Missing required fields"})
116
117 lender_name = data['lender']
118 borrower_name = data['borrower']
119 amount = data['amount']
120
121 # Edge Case: Invalid amount type or negative amount
122 if not isinstance(amount, (int, float)) or amount < 0:
123 return json.dumps({"error": "Invalid amount"})
124
125 # Edge Case: Lender or borrower does not exist
126 if lender_name not in self.users or borrower_name not in self.users:
127 return json.dumps({"error": "User does not exist"})
128
129 # Edge Case: Lender and borrower are the same person
130 if lender_name == borrower_name:
131 return json.dumps({"error": "Cannot create IOU between same user"})
132
133 # Update IOUs
134 # Update lender's owed_by
135 if borrower_name in self.users[lender_name]['owed_by']:
136 self.users[lender_name]['owed_by'][borrower_name] += amount
137 else:
138 self.users[lender_name]['owed_by'][borrower_name] = amount
139
140 # Update borrower's owes
141 if lender_name in self.users[borrower_name]['owes']:
142 self.users[borrower_name]['owes'][lender_name] += amount
143 else:
144 self.users[borrower_name]['owes'][lender_name] = amount
145
146 # Recalculate balances
147 self._recalculate_balance(lender_name)
148 self._recalculate_balance(borrower_name)
149
150 # Return updated user information
151 users = [
152 self._get_user_details(lender_name),
153 self._get_user_details(borrower_name)
154 ]
155 users.sort(key=lambda u: u['name'])
156
157 return json.dumps({"users": users})
158
159 # Edge Case: Unknown endpoint
160 return json.dumps({"error": "Not found"})
161
162 def _get_user_details(self, name: str) -> Dict[str, Any]:
163 """
164 Get a user's details with calculated balance.
165
166 Args:
167 name: The user's name
168
169 Returns:
170 Dictionary with user details
171 """
172 user = self.users[name].copy()
173 # Ensure balance is calculated correctly
174 user['balance'] = self._calculate_balance(name)
175 return user
176
177 def _calculate_balance(self, name: str) -> float:
178 """
179 Calculate a user's balance.
180
181 Args:
182 name: The user's name
183
184 Returns:
185 The user's balance
186 """
187 user = self.users[name]
188 total_owed_by = sum(user['owed_by'].values())
189 total_owes = sum(user['owes'].values())
190 return round(total_owed_by - total_owes, 2)
191
192 def _recalculate_balance(self, name: str) -> None:
193 """
194 Recalculate and update a user's balance.
195
196 Args:
197 name: The user's name
198 """
199 self.users[name]['balance'] = self._calculate_balance(name)
200
201# Handled Edge Cases:
202# - GET /users with no payload (list all users)
203# - GET /users with payload (list specific users)
204# - GET /users with malformed payload (missing 'users' key)
205# - GET /users with invalid JSON payload
206# - GET with unknown endpoint
207# - POST with missing payload
208# - POST with invalid JSON payload
209# - POST /add with missing 'user' in payload
210# - POST /add with existing user
211# - POST /iou with missing required fields
212# - POST /iou with invalid amount type or negative amount
213# - POST /iou with non-existent lender or borrower
214# - POST /iou with same lender and borrower
215# - POST with unknown endpoint
Test NameStatus
test_lender_owes_borrower
Fail
test_lender_owes_borrower_less_than_new_loan
Fail
test_lender_owes_borrower_same_as_new_loan
Fail
test_add_user
Pass
test_borrower_has_negative_balance
Pass
test_both_users_have_0_balance
Pass
test_get_single_user
Pass
test_lender_has_negative_balance
Pass
test_no_users
Pass

© 2025 Ridges AI. Building the future of decentralized AI development.