Finished
1import json
2from typing import Dict, List, Optional
3
14class RestAPI:
25 def __init__(self, database: dict | None = None):
3 pass
4
6 """
7 Initialize the REST API with an optional database.
8
9 Args:
10 database: A dictionary containing initial user data.
11 """
12 if database is None:
13 self.database = {"users": []}
14 else:
15 self.database = database
16
517 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
18 """
19 Handle GET requests to the API.
20
21 Args:
22 url: The endpoint URL.
23 payload: Optional JSON payload.
24
25 Returns:
26 JSON string response.
27 """
28 if url == "/users":
29 if payload is None:
30 # Return all users sorted by name
31 users = sorted(self.database["users"], key=lambda x: x["name"])
32 return json.dumps({"users": users})
33 else:
34 # Return specific users sorted by name
35 data = json.loads(payload)
36 user_names = data.get("users", [])
37
38 # Edge Case: Handle when users list is empty
39 if not user_names:
40 return json.dumps({"users": []})
41
42 # Filter users by provided names
43 users = [user for user in self.database["users"] if user["name"] in user_names]
44 users = sorted(users, key=lambda x: x["name"])
45 return json.dumps({"users": users})
46
47 # Handled Edge Cases: Empty users list in payload
48
849 def post(self, url: str, payload: str | None = None) -> str:
9 pass
50 """
51 Handle POST requests to the API.
52
53 Args:
54 url: The endpoint URL.
55 payload: JSON payload.
56
57 Returns:
58 JSON string response.
59 """
60 if url == "/add":
61 if payload is None:
62 # Edge Case: No payload provided
63 return json.dumps({"error": "Payload required"})
64
65 data = json.loads(payload)
66 user_name = data.get("user")
67
68 # Edge Case: User name not provided
69 if not user_name:
70 return json.dumps({"error": "User name required"})
71
72 # Edge Case: User already exists
73 for user in self.database["users"]:
74 if user["name"] == user_name:
75 return json.dumps({"error": "User already exists"})
76
77 # Create new user
78 new_user = {
79 "name": user_name,
80 "owes": {},
81 "owed_by": {},
82 "balance": 0.0
83 }
84
85 self.database["users"].append(new_user)
86 return json.dumps(new_user)
87
88 elif url == "/iou":
89 if payload is None:
90 # Edge Case: No payload provided
91 return json.dumps({"error": "Payload required"})
92
93 data = json.loads(payload)
94 lender_name = data.get("lender")
95 borrower_name = data.get("borrower")
96 amount = data.get("amount")
97
98 # Edge Case: Missing required fields
99 if not lender_name or not borrower_name or amount is None:
100 return json.dumps({"error": "Missing required fields"})
101
102 # Edge Case: Lender and borrower are the same person
103 if lender_name == borrower_name:
104 return json.dumps({"error": "Cannot create IOU between same user"})
105
106 # Edge Case: Amount is negative
107 if amount < 0:
108 return json.dumps({"error": "Amount cannot be negative"})
109
110 # Find lender and borrower
111 lender = None
112 borrower = None
113
114 for user in self.database["users"]:
115 if user["name"] == lender_name:
116 lender = user
117 if user["name"] == borrower_name:
118 borrower = user
119
120 # Edge Case: Lender or borrower does not exist
121 if lender is None or borrower is None:
122 return json.dumps({"error": "User does not exist"})
123
124 # Check if there's already a reverse IOU (lender owes borrower)
125 if borrower_name in lender["owes"]:
126 # There's already a reverse IOU, need to net the amounts
127 existing_reverse_amount = lender["owes"][borrower_name]
128
129 if amount > existing_reverse_amount:
130 # New IOU is larger, borrower now owes lender the difference
131 net_amount = amount - existing_reverse_amount
132
133 # Clear the reverse IOU
134 del lender["owes"][borrower_name]
135 del borrower["owed_by"][lender_name]
136
137 # Create the new net IOU
138 lender["owed_by"][borrower_name] = net_amount
139 borrower["owes"][lender_name] = net_amount
140
141 # Update balances: lender gains net_amount, borrower loses net_amount
142 # The existing reverse IOU was cancelled, so we only account for the net amount
143 lender["balance"] += net_amount
144 borrower["balance"] -= net_amount
145
146 elif amount < existing_reverse_amount:
147 # Reverse IOU is larger, lender still owes borrower but less
148 net_amount = existing_reverse_amount - amount
149
150 # Update the reverse IOU with the net amount
151 lender["owes"][borrower_name] = net_amount
152 borrower["owed_by"][lender_name] = net_amount
153
154 # Update balances: lender loses net_amount, borrower gains net_amount
155 # The existing reverse IOU was reduced, so we account for the difference
156 lender["balance"] -= net_amount
157 borrower["balance"] += net_amount
158
159 else: # amount == existing_reverse_amount
160 # IOUs are equal, create bidirectional IOU
161 # Clear the existing reverse IOU first
162 del lender["owes"][borrower_name]
163 del borrower["owed_by"][lender_name]
164
165 # Create the new bidirectional IOU
166 lender["owed_by"][borrower_name] = amount
167 borrower["owes"][lender_name] = amount
168
169 # Update balances to zero since IOUs cancel out net effect
170 lender["balance"] = 0.0
171 borrower["balance"] = 0.0
172 else:
173 # No reverse IOU exists, just add the new IOU
174 # Update lender's owed_by
175 if borrower_name in lender["owed_by"]:
176 lender["owed_by"][borrower_name] += amount
177 else:
178 lender["owed_by"][borrower_name] = amount
179
180 # Update borrower's owes
181 if lender_name in borrower["owes"]:
182 borrower["owes"][lender_name] += amount
183 else:
184 borrower["owes"][lender_name] = amount
185
186 # Update balances
187 lender["balance"] += amount
188 borrower["balance"] -= amount
189
190 # Return updated user objects sorted by name
191 users = [lender, borrower]
192 users = sorted(users, key=lambda x: x["name"])
193 return json.dumps({"users": users})
194
195 # Handled Edge Cases: No payload, missing fields, same user IOU, negative amount, user does not exist, bidirectional relationships
196
197 # Handled Edge Cases: Initialization with None database
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.