Finished
1import json
2
13class RestAPI:
24 def __init__(self, database: dict | None = None):
3 pass
4
5 """
6 Initialize the REST API with a database.
7
8 Args:
9 database: A dictionary containing user data.
10 """
11 # Edge Case: Handle None database by initializing empty users list
12 self.database = database if database is not None else {"users": []}
13
514 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
15 """
16 Handle GET requests to the API.
17
18 Args:
19 url: The endpoint URL.
20 payload: Optional JSON payload.
21
22 Returns:
23 JSON string response.
24 """
25 # Edge Case: Handle /users endpoint
26 if url == "/users":
27 # Edge Case: Handle payload with specific users
28 if payload is not None:
29 data = json.loads(payload)
30 if "users" in data:
31 # Filter users and sort by name
32 users = [user for user in self.database["users"] if user["name"] in data["users"]]
33 users.sort(key=lambda x: x["name"])
34 return json.dumps({"users": users})
35 # Edge Case: Handle request for all users
36 else:
37 # Return all users sorted by name
38 users = sorted(self.database["users"], key=lambda x: x["name"])
39 return json.dumps({"users": users})
40
41 # Handled Edge Cases: /users endpoint with and without payload
42 return json.dumps({"error": "Invalid endpoint"})
43
844 def post(self, url: str, payload: str | None = None) -> str:
9 pass
45 """
46 Handle POST requests to the API.
47
48 Args:
49 url: The endpoint URL.
50 payload: JSON payload.
51
52 Returns:
53 JSON string response.
54 """
55 # Edge Case: Handle None payload
56 if payload is None:
57 return json.dumps({"error": "Payload required"})
58
59 data = json.loads(payload)
60
61 # Edge Case: Handle /add endpoint
62 if url == "/add":
63 # Edge Case: Check if user field exists in payload
64 if "user" not in data:
65 return json.dumps({"error": "User field required"})
66
67 user_name = data["user"]
68
69 # Edge Case: Check if user already exists
70 for user in self.database["users"]:
71 if user["name"] == user_name:
72 return json.dumps({"error": "User already exists"})
73
74 # Create new user
75 new_user = {
76 "name": user_name,
77 "owes": {},
78 "owed_by": {},
79 "balance": 0.0
80 }
81
82 self.database["users"].append(new_user)
83 return json.dumps(new_user)
84
85 # Edge Case: Handle /iou endpoint
86 elif url == "/iou":
87 # Edge Case: Validate required fields
88 required_fields = ["lender", "borrower", "amount"]
89 for field in required_fields:
90 if field not in data:
91 return json.dumps({"error": f"Missing field: {field}"})
92
93 lender_name = data["lender"]
94 borrower_name = data["borrower"]
95 amount = data["amount"]
96
97 # Edge Case: Validate amount is positive
98 if amount <= 0:
99 return json.dumps({"error": "Amount must be positive"})
100
101 # Edge Case: Validate lender and borrower are different
102 if lender_name == borrower_name:
103 return json.dumps({"error": "Lender and borrower must be different"})
104
105 # Edge Case: Validate both users exist
106 lender = None
107 borrower = None
108 for user in self.database["users"]:
109 if user["name"] == lender_name:
110 lender = user
111 elif user["name"] == borrower_name:
112 borrower = user
113
114 # Edge Case: Handle user not found
115 if lender is None or borrower is None:
116 return json.dumps({"error": "User not found"})
117
118 # Handle debt relationships - check if there's existing debt between users
119
120 # Case 1: Lender already owes borrower
121 if borrower_name in lender["owes"]:
122 # Check if new IOU is in the same direction (lender lending to borrower)
123 # If so, add to existing debt; otherwise, offset debts
124 if lender_name == data["lender"] and borrower_name == data["borrower"]:
125 # Same direction - accumulate debt
126 lender["owes"][borrower_name] += amount
127 borrower["owed_by"][lender_name] += amount
128 else:
129 # Opposite direction - offset debts
130 existing_debt = lender["owes"][borrower_name]
131
132 if existing_debt == amount:
133 # Exact cancellation - remove the debt
134 del lender["owes"][borrower_name]
135 del borrower["owed_by"][lender_name]
136 elif existing_debt > amount:
137 # Lender owes more than new loan - reduce existing debt
138 lender["owes"][borrower_name] -= amount
139 borrower["owed_by"][lender_name] -= amount
140 else:
141 # Lender owes less than new loan - convert the debt
142 remaining_amount = amount - existing_debt
143 del lender["owes"][borrower_name]
144 del borrower["owed_by"][lender_name]
145 # Now borrower owes lender the remaining amount
146 if lender_name in borrower["owes"]:
147 borrower["owes"][lender_name] += remaining_amount
148 else:
149 borrower["owes"][lender_name] = remaining_amount
150
151 if borrower_name in lender["owed_by"]:
152 lender["owed_by"][borrower_name] += remaining_amount
153 else:
154 lender["owed_by"][borrower_name] = remaining_amount
155 # Case 2: Borrower already owes lender
156 elif lender_name in borrower["owes"]:
157 # Check if new IOU is in the same direction (borrower lending to lender)
158 # If so, add to existing debt; otherwise, offset debts
159 if borrower_name == data["lender"] and lender_name == data["borrower"]:
160 # Same direction - accumulate debt
161 borrower["owes"][lender_name] += amount
162 lender["owed_by"][borrower_name] += amount
163 else:
164 # Opposite direction - offset debts
165 existing_debt = borrower["owes"][lender_name]
166
167 if existing_debt == amount:
168 # Exact cancellation - remove the debt
169 del borrower["owes"][lender_name]
170 del lender["owed_by"][borrower_name]
171 elif existing_debt > amount:
172 # Borrower owes more than new loan - reduce existing debt
173 borrower["owes"][lender_name] -= amount
174 lender["owed_by"][borrower_name] -= amount
175 else:
176 # Borrower owes less than new loan - convert the debt
177 remaining_amount = amount - existing_debt
178 del borrower["owes"][lender_name]
179 del lender["owed_by"][borrower_name]
180 # Now lender owes borrower the remaining amount
181 if borrower_name in lender["owes"]:
182 lender["owes"][borrower_name] += remaining_amount
183 else:
184 lender["owes"][borrower_name] = remaining_amount
185
186 if lender_name in borrower["owed_by"]:
187 borrower["owed_by"][lender_name] += remaining_amount
188 else:
189 borrower["owed_by"][lender_name] = remaining_amount
190 else:
191 # Normal case - no existing debt
192 # Update lender's owed_by
193 if borrower_name in lender["owed_by"]:
194 lender["owed_by"][borrower_name] += amount
195 else:
196 lender["owed_by"][borrower_name] = amount
197
198 # Update borrower's owes
199 if lender_name in borrower["owes"]:
200 borrower["owes"][lender_name] += amount
201 else:
202 borrower["owes"][lender_name] = amount
203
204 # Recalculate balances
205 lender_total_owed = sum(lender["owed_by"].values())
206 lender_total_owes = sum(lender["owes"].values())
207 lender["balance"] = lender_total_owed - lender_total_owes
208
209 borrower_total_owed = sum(borrower["owed_by"].values())
210 borrower_total_owes = sum(borrower["owes"].values())
211 borrower["balance"] = borrower_total_owed - borrower_total_owes
212
213 # Return updated users sorted by name
214 updated_users = sorted([lender, borrower], key=lambda x: x["name"])
215 return json.dumps({"users": updated_users})
216
217 # Handled Edge Cases: /add and /iou endpoints with all validation checks
218 return json.dumps({"error": "Invalid endpoint"})
219
220# Handled Edge Cases: None database initialization, None payload handling, user already exists,
221# missing user field, invalid endpoints, missing IOU fields, negative amounts,
222# same lender/borrower, user not found, /users endpoint with and without payload
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.