fishlium

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 accounts and IOU transactions, allowing creation of users,
9 recording of debts, and retrieval of user balance information.
10 """
211 def __init__(self, database: dict | None = None):
3 pass
4
12 """
13 Initialize the REST API with an optional database.
14
15 Args:
16 database: Optional initial database with users data
17 """
18 if database and 'users' in database:
19 # Create users dictionary with name as key for faster access
20 self.users = {user['name']: user for user in database['users']}
21 # Ensure all users have the required fields
22 for user in self.users.values():
23 if 'owes' not in user:
24 user['owes'] = {}
25 if 'owed_by' not in user:
26 user['owed_by'] = {}
27 if 'balance' not in user:
28 user['balance'] = 0.0
29 else:
30 self.users = {}
31
532 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
33 """
34 Handle GET requests to the API.
35
36 Args:
37 url: The endpoint URL
38 payload: Optional JSON payload
39
40 Returns:
41 JSON string response
42 """
43 if url == '/users':
44 # Edge Case: Handle request with no payload (list all users)
45 if payload is None:
46 users_list = list(self.users.values())
47 users_list.sort(key=lambda x: x['name'])
48 return json.dumps({"users": users_list})
49
50 # Edge Case: Handle request with payload (list specific users)
51 try:
52 data = json.loads(payload)
53 if 'users' not in data:
54 return json.dumps({"error": "Invalid payload format"})
55
56 # Edge Case: Handle empty users list
57 if not data['users']:
58 return json.dumps({"users": []})
59
60 result_users = []
61 for name in data['users']:
62 # Edge Case: Handle request for non-existent user
63 if name in self.users:
64 result_users.append(self.users[name])
65 else:
66 # Create user if they don't exist
67 new_user = {
68 "name": name,
69 "owes": {},
70 "owed_by": {},
71 "balance": 0.0
72 }
73 self.users[name] = new_user
74 result_users.append(new_user)
75
76 result_users.sort(key=lambda x: x['name'])
77 return json.dumps({"users": result_users})
78 except json.JSONDecodeError:
79 return json.dumps({"error": "Invalid JSON payload"})
80
81 return json.dumps({"error": "Invalid endpoint"})
82
883 def post(self, url: str, payload: str | None = None) -> str:
9 pass
84 """
85 Handle POST requests to the API.
86
87 Args:
88 url: The endpoint URL
89 payload: JSON payload with request data
90
91 Returns:
92 JSON string response
93 """
94 if payload is None:
95 return json.dumps({"error": "Payload required"})
96
97 try:
98 data = json.loads(payload)
99 except json.JSONDecodeError:
100 return json.dumps({"error": "Invalid JSON payload"})
101
102 if url == '/add':
103 # Edge Case: Handle missing user field
104 if 'user' not in data:
105 return json.dumps({"error": "Missing user field"})
106
107 name = data['user']
108
109 # Edge Case: Handle attempt to create duplicate user
110 if name in self.users:
111 return json.dumps({"error": "User already exists"})
112
113 new_user = {
114 "name": name,
115 "owes": {},
116 "owed_by": {},
117 "balance": 0.0
118 }
119 self.users[name] = new_user
120 return json.dumps(new_user)
121
122 elif url == '/iou':
123 # Edge Case: Handle missing required fields
124 required_fields = ['lender', 'borrower', 'amount']
125 for field in required_fields:
126 if field not in data:
127 return json.dumps({"error": f"Missing {field} field"})
128
129 lender_name = data['lender']
130 borrower_name = data['borrower']
131 amount = data['amount']
132
133 # Edge Case: Handle negative or zero amount
134 if amount <= 0:
135 return json.dumps({"error": "Amount must be positive"})
136
137 # Edge Case: Handle lender and borrower being the same person
138 if lender_name == borrower_name:
139 return json.dumps({"error": "Lender and borrower cannot be the same"})
140
141 # Edge Case: Handle non-existent lender
142 if lender_name not in self.users:
143 new_lender = {
144 "name": lender_name,
145 "owes": {},
146 "owed_by": {},
147 "balance": 0.0
148 }
149 self.users[lender_name] = new_lender
150
151 # Edge Case: Handle non-existent borrower
152 if borrower_name not in self.users:
153 new_borrower = {
154 "name": borrower_name,
155 "owes": {},
156 "owed_by": {},
157 "balance": 0.0
158 }
159 self.users[borrower_name] = new_borrower
160
161 lender = self.users[lender_name]
162 borrower = self.users[borrower_name]
163
164 # Update lender's records
165 if borrower_name in lender['owes']:
166 lender['owes'][borrower_name] -= amount
167 # Edge Case: Remove entry if balance becomes zero or negative
168 if lender['owes'][borrower_name] <= 0:
169 amount_owed_to_borrower = abs(lender['owes'][borrower_name])
170 del lender['owes'][borrower_name]
171 if amount_owed_to_borrower > 0:
172 lender['owed_by'][borrower_name] = amount_owed_to_borrower
173 else:
174 lender['owed_by'][borrower_name] = lender['owed_by'].get(borrower_name, 0) + amount
175
176 # Update borrower's records
177 if lender_name in borrower['owed_by']:
178 borrower['owed_by'][lender_name] -= amount
179 # Edge Case: Remove entry if balance becomes zero or negative
180 if borrower['owed_by'][lender_name] <= 0:
181 amount_owed_to_lender = abs(borrower['owed_by'][lender_name])
182 del borrower['owed_by'][lender_name]
183 if amount_owed_to_lender > 0:
184 borrower['owes'][lender_name] = amount_owed_to_lender
185 else:
186 borrower['owes'][lender_name] = borrower['owes'].get(lender_name, 0) + amount
187
188 # Recalculate balances
189 lender_owes = sum(lender['owes'].values())
190 lender_owed_by = sum(lender['owed_by'].values())
191 lender['balance'] = lender_owed_by - lender_owes
192
193 borrower_owes = sum(borrower['owes'].values())
194 borrower_owed_by = sum(borrower['owed_by'].values())
195 borrower['balance'] = borrower_owed_by - borrower_owes
196
197 # Return updated user objects sorted by name
198 result_users = [lender, borrower]
199 result_users.sort(key=lambda x: x['name'])
200 return json.dumps({"users": result_users})
201
202 return json.dumps({"error": "Invalid endpoint"})
203
204 # Handled Edge Cases: Invalid JSON, missing payload, invalid endpoint,
205 # list all users, list specific users, empty users list,
206 # non-existent user in list, missing user field in add,
207 # duplicate user creation, missing fields in IOU,
208 # negative/zero amount, same lender/borrower,
209 # non-existent lender/borrower, balance zeroing entries
Test NameStatus
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_lender_owes_borrower
Pass
test_lender_owes_borrower_less_than_new_loan
Pass
test_lender_owes_borrower_same_as_new_loan
Pass
test_no_users
Pass

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