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 self.database = database if database is not None else {"users": []}
12
513 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
14 """
15 Handle GET requests to the API.
16
17 Args:
18 url: The endpoint URL.
19 payload: Optional JSON payload.
20
21 Returns:
22 A JSON string response.
23 """
24 if url == "/users":
25 if payload is None:
26 # Return all users sorted by name
27 users = sorted(self.database["users"], key=lambda u: u["name"])
28 return json.dumps({"users": users})
29 else:
30 # Return specific users sorted by name
31 data = json.loads(payload)
32 user_names = data.get("users", [])
33
34 # Edge Case: Empty user list in payload
35 if not user_names:
36 return json.dumps({"users": []})
37
38 # Filter users by name
39 users = [user for user in self.database["users"] if user["name"] in user_names]
40 users = sorted(users, key=lambda u: u["name"])
41
42 # Edge Case: Some requested users don't exist
43 # (Non-existent users are simply not included in the response)
44
45 return json.dumps({"users": users})
46
47 # Edge Case: Invalid URL
48 return json.dumps({"error": "Invalid URL"})
49
850 def post(self, url: str, payload: str | None = None) -> str:
9 pass
51 """
52 Handle POST requests to the API.
53
54 Args:
55 url: The endpoint URL.
56 payload: JSON payload.
57
58 Returns:
59 A JSON string response.
60 """
61 if payload is None:
62 # Edge Case: Missing payload
63 return json.dumps({"error": "Payload required"})
64
65 data = json.loads(payload)
66
67 if url == "/add":
68 user_name = data.get("user")
69
70 # Edge Case: Missing user name
71 if not user_name:
72 return json.dumps({"error": "User name required"})
73
74 # Edge Case: User already exists
75 for user in self.database["users"]:
76 if user["name"] == user_name:
77 return json.dumps({"error": "User already exists"})
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
88 return json.dumps(new_user)
89
90 elif url == "/iou":
91 lender_name = data.get("lender")
92 borrower_name = data.get("borrower")
93 amount = data.get("amount")
94
95 # Edge Case: Missing required fields
96 if not lender_name or not borrower_name or amount is None:
97 return json.dumps({"error": "Lender, borrower, and amount required"})
98
99 # Edge Case: Lender and borrower are the same
100 if lender_name == borrower_name:
101 return json.dumps({"error": "Lender and borrower cannot be the same"})
102
103 # Edge Case: Negative amount
104 if amount < 0:
105 return json.dumps({"error": "Amount must be non-negative"})
106
107 # Find lender and borrower
108 lender = None
109 borrower = None
110
111 for user in self.database["users"]:
112 if user["name"] == lender_name:
113 lender = user
114 elif user["name"] == borrower_name:
115 borrower = user
116
117 # Edge Case: Lender or borrower doesn't exist
118 if lender is None or borrower is None:
119 return json.dumps({"error": "Lender or borrower does not exist"})
120
121 # Update owes and owed_by with debt offsetting logic
122 # Check if lender already owes borrower (reverse relationship)
123 lender_owes_borrower = 0
124 if borrower_name in lender["owes"]:
125 lender_owes_borrower = lender["owes"][borrower_name]
126
127 if lender_owes_borrower > 0:
128 # Lender owes borrower, so we need to offset
129 if amount >= lender_owes_borrower:
130 # New loan is larger or equal, clear lender's debt and borrower becomes lender
131 del lender["owes"][borrower_name]
132 del borrower["owed_by"][lender_name]
133
134 remaining_amount = amount - lender_owes_borrower
135 if remaining_amount > 0:
136 # Borrower now owes lender the remaining amount
137 if borrower_name in lender["owed_by"]:
138 lender["owed_by"][borrower_name] += remaining_amount
139 else:
140 lender["owed_by"][borrower_name] = remaining_amount
141
142 if lender_name in borrower["owes"]:
143 borrower["owes"][lender_name] += remaining_amount
144 else:
145 borrower["owes"][lender_name] = remaining_amount
146 else:
147 # New loan is smaller, reduce existing debt
148 lender["owes"][borrower_name] -= amount
149 borrower["owed_by"][lender_name] -= amount
150
151 # Clean up if debt becomes zero
152 if lender["owes"][borrower_name] == 0:
153 del lender["owes"][borrower_name]
154 del borrower["owed_by"][lender_name]
155 else:
156 # No existing reverse debt, normal IOU creation
157 # Update lender's owed_by
158 if borrower_name in lender["owed_by"]:
159 lender["owed_by"][borrower_name] += amount
160 else:
161 lender["owed_by"][borrower_name] = amount
162
163 # Update borrower's owes
164 if lender_name in borrower["owes"]:
165 borrower["owes"][lender_name] += amount
166 else:
167 borrower["owes"][lender_name] = amount
168
169 # Recalculate balances
170 lender_total_owed = sum(lender["owed_by"].values())
171 lender_total_owes = sum(lender["owes"].values())
172 lender["balance"] = lender_total_owed - lender_total_owes
173
174 borrower_total_owed = sum(borrower["owed_by"].values())
175 borrower_total_owes = sum(borrower["owes"].values())
176 borrower["balance"] = borrower_total_owed - borrower_total_owes
177
178 # Return updated users sorted by name
179 users = sorted([lender, borrower], key=lambda u: u["name"])
180 return json.dumps({"users": users})
181
182 # Edge Case: Invalid URL
183 return json.dumps({"error": "Invalid URL"})
184
185 # Handled Edge Cases: Invalid URL, Missing payload, Missing user name, User already exists, Missing required fields, Lender and borrower are the same, Negative amount, Lender or borrower doesn't exist, Empty user list in payload, Some requested users don't exist
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.