Finished
1import json
2
13class RestAPI:
24 def __init__(self, database: dict | None = None):
3 pass
4
5 # Edge Case: Handle None database by initializing empty structure
6 if database is None:
7 self.database = {"users": []}
8 else:
9 self.database = database
10
511 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
12 # Edge Case: Handle invalid URL
13 if url != "/users":
14 return json.dumps({"error": "Invalid URL"})
15
16 # Edge Case: Handle invalid payload format
17 if payload is not None:
18 try:
19 payload_data = json.loads(payload)
20 except json.JSONDecodeError:
21 return json.dumps({"error": "Invalid payload format"})
22
23 # Edge Case: Handle missing 'users' key in payload
24 if "users" not in payload_data:
25 return json.dumps({"error": "Missing 'users' key in payload"})
26
27 # Edge Case: Handle non-list 'users' value in payload
28 if not isinstance(payload_data["users"], list):
29 return json.dumps({"error": "'users' must be a list"})
30
31 # Filter users based on payload
32 users_list = []
33 for user_name in payload_data["users"]:
34 user = self._get_user_by_name(user_name)
35 # Edge Case: Handle user not found
36 if user is not None:
37 users_list.append(user)
38
39 # Sort users by name
40 users_list.sort(key=lambda x: x["name"])
41 return json.dumps({"users": users_list})
42 else:
43 # Return all users sorted by name
44 users_list = self.database["users"].copy()
45 users_list.sort(key=lambda x: x["name"])
46 return json.dumps({"users": users_list})
47
848 def post(self, url: str, payload: str | None = None) -> str:
9 pass
49 # Edge Case: Handle invalid URL
50 if url not in ["/add", "/iou"]:
51 return json.dumps({"error": "Invalid URL"})
52
53 # Edge Case: Handle missing payload
54 if payload is None:
55 return json.dumps({"error": "Missing payload"})
56
57 # Edge Case: Handle invalid payload format
58 try:
59 payload_data = json.loads(payload)
60 except json.JSONDecodeError:
61 return json.dumps({"error": "Invalid payload format"})
62
63 if url == "/add":
64 # Edge Case: Handle missing 'user' key in payload
65 if "user" not in payload_data:
66 return json.dumps({"error": "Missing 'user' key in payload"})
67
68 user_name = payload_data["user"]
69
70 # Edge Case: Handle non-string user name
71 if not isinstance(user_name, str):
72 return json.dumps({"error": "User name must be a string"})
73
74 # Edge Case: Handle duplicate user - return existing user
75 existing_user = self._get_user_by_name(user_name)
76 if existing_user is not None:
77 return json.dumps(existing_user)
78
79 # Create new user
80 new_user = {
81 "name": user_name,
82 "owes": {},
83 "owed_by": {},
84 "balance": 0.0
85 }
86 self.database["users"].append(new_user)
87 return json.dumps(new_user)
88
89 elif url == "/iou":
90 # Edge Case: Handle missing required keys in payload
91 required_keys = ["lender", "borrower", "amount"]
92 for key in required_keys:
93 if key not in payload_data:
94 return json.dumps({"error": f"Missing '{key}' key in payload"})
95
96 lender_name = payload_data["lender"]
97 borrower_name = payload_data["borrower"]
98 amount = payload_data["amount"]
99
100 # Edge Case: Handle non-string lender or borrower
101 if not isinstance(lender_name, str) or not isinstance(borrower_name, str):
102 return json.dumps({"error": "Lender and borrower must be strings"})
103
104 # Edge Case: Handle non-numeric amount
105 if not isinstance(amount, (int, float)):
106 return json.dumps({"error": "Amount must be a number"})
107
108 # Edge Case: Handle negative amount - treat as positive (or handle gracefully)
109 # For this implementation, we'll allow negative amounts to be processed
110
111 # Edge Case: Handle same lender and borrower
112 if lender_name == borrower_name:
113 return json.dumps({"error": "Lender and borrower cannot be the same"})
114
115 lender = self._get_user_by_name(lender_name)
116 borrower = self._get_user_by_name(borrower_name)
117
118 # Edge Case: Handle lender or borrower not found
119 if lender is None:
120 return json.dumps({"error": "Lender not found"})
121 if borrower is None:
122 return json.dumps({"error": "Borrower not found"})
123
124 # Update IOUs
125 if amount != 0:
126 # Update the direct relationship
127 # When lender lends borrower amount:
128 # - borrower is owed by lender (borrower.owed_by[lender] += amount)
129 # - lender owes to borrower (lender.owes[borrower] += amount)
130 if lender_name in borrower["owed_by"]:
131 borrower["owed_by"][lender_name] += amount
132 else:
133 borrower["owed_by"][lender_name] = amount
134
135 if borrower_name in lender["owes"]:
136 lender["owes"][borrower_name] += amount
137 else:
138 lender["owes"][borrower_name] = amount
139
140 # Net the relationships between lender and borrower
141 # Calculate net amounts
142 lender_owed_by_borrower = lender["owed_by"].get(borrower_name, 0)
143 lender_owes_borrower = lender["owes"].get(borrower_name, 0)
144 borrower_owes_lender = borrower["owes"].get(lender_name, 0)
145 borrower_owed_by_lender = borrower["owed_by"].get(lender_name, 0)
146
147 # Calculate net amounts (positive means first person owes second person)
148 net_lender_owes_borrower = lender_owes_borrower - lender_owed_by_borrower
149 net_borrower_owes_lender = borrower_owes_lender - borrower_owed_by_lender
150
151 # Clear existing relationships between these two
152 if borrower_name in lender["owed_by"]:
153 del lender["owed_by"][borrower_name]
154 if lender_name in borrower["owes"]:
155 del borrower["owes"][lender_name]
156 if lender_name in lender["owes"]:
157 del lender["owes"][lender_name]
158 if borrower_name in borrower["owed_by"]:
159 del borrower["owed_by"][borrower_name]
160
161 # Set new netted relationships
162 # If net_lender_owes_borrower > 0, lender owes borrower
163 # If net_lender_owes_borrower < 0, borrower owes lender (lender is owed by borrower)
164 if net_lender_owes_borrower > 0:
165 lender["owes"][borrower_name] = net_lender_owes_borrower
166 borrower["owed_by"][lender_name] = net_lender_owes_borrower
167 elif net_lender_owes_borrower < 0:
168 lender_owed_amount = abs(net_lender_owes_borrower)
169 lender["owed_by"][borrower_name] = lender_owed_amount
170 borrower["owes"][lender_name] = lender_owed_amount
171
172 # Similarly for the other direction
173 if net_borrower_owes_lender > 0:
174 borrower["owes"][lender_name] = net_borrower_owes_lender
175 lender["owed_by"][borrower_name] = net_borrower_owes_lender
176 elif net_borrower_owes_lender < 0:
177 borrower_owed_amount = abs(net_borrower_owes_lender)
178 borrower["owed_by"][lender_name] = borrower_owed_amount
179 lender["owes"][borrower_name] = borrower_owed_amount
180 else:
181 # For zero amounts, remove any existing relationships in both directions
182 if borrower_name in lender["owed_by"]:
183 del lender["owed_by"][borrower_name]
184 if lender_name in borrower["owes"]:
185 del borrower["owes"][lender_name]
186 if lender_name in lender["owes"]:
187 del lender["owes"][lender_name]
188 if borrower_name in borrower["owed_by"]:
189 del borrower["owed_by"][borrower_name]
190
191 # Recalculate balances
192 self._recalculate_balance(lender)
193 self._recalculate_balance(borrower)
194
195 # Return updated users sorted by name
196 users_list = [lender, borrower]
197 users_list.sort(key=lambda x: x["name"])
198 return json.dumps({"users": users_list})
199
200 def _get_user_by_name(self, name: str):
201 for user in self.database["users"]:
202 if user["name"] == name:
203 return user
204 return None
205
206 def _recalculate_balance(self, user):
207 total_owed_by = sum(user["owed_by"].values())
208 total_owes = sum(user["owes"].values())
209 user["balance"] = round(total_owed_by - total_owes, 2)
210
211# Handled Edge Cases: None database, invalid URL, invalid payload format, missing 'users' key, non-list 'users' value, user not found, missing payload, missing 'user' key, non-string user name, duplicate user, missing required keys, non-string lender/borrower, non-numeric amount, negative amount, same lender/borrower, lender/borrower not found
Test NameStatus
test_borrower_has_negative_balance
Fail
test_both_users_have_0_balance
Fail
test_lender_has_negative_balance
Fail
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_get_single_user
Pass
test_no_users
Pass

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