fortitude

Finished
1import json
2from typing import Dict, List, Optional, Any
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: Optional initial database dictionary containing users data
11 """
12 if database and 'users' in database:
13 # Edge Case: Handle initial database with users
14 self.users = {user['name']: user for user in database['users']}
15 else:
16 # Edge Case: Handle empty or None database
17 self.users = {}
18
19 def _get_user(self, name: str) -> Dict[str, Any]:
20 """
21 Get a user object by name, creating it if it doesn't exist.
22
23 Args:
24 name: Name of the user
25
26 Returns:
27 User object
28 """
29 # Edge Case: Create user if not exists
30 if name not in self.users:
31 self.users[name] = {
32 'name': name,
33 'owes': {},
34 'owed_by': {},
35 'balance': 0.0
36 }
37 return self.users[name]
38
39 def _calculate_balance(self, user: Dict[str, Any]) -> float:
40 """
41 Calculate the balance for a user.
42
43 Args:
44 user: User object
45
46 Returns:
47 Calculated balance
48 """
49 # Edge Case: Handle calculation when owes or owed_by might be missing
50 total_owed_by = sum(user.get('owed_by', {}).values())
51 total_owes = sum(user.get('owes', {}).values())
52 return round(total_owed_by - total_owes, 2)
53
54 def _update_user_balances(self, user_name: str) -> None:
55 """
56 Update the balance for a specific user.
57
58 Args:
59 user_name: Name of the user to update
60 """
61 user = self._get_user(user_name)
62 user['balance'] = self._calculate_balance(user)
63
564 def get(self, url: str, payload: str | None = None) -> str:
6 pass
7
65 """
66 Handle GET requests to the API.
67
68 Args:
69 url: Endpoint URL
70 payload: Optional JSON payload
71
72 Returns:
73 JSON response string
74 """
75 # Edge Case: Handle /users endpoint
76 if url == '/users':
77 if payload is None:
78 # Return all users sorted by name
79 users_list = [user for name, user in sorted(self.users.items())]
80 return json.dumps({'users': users_list})
81 else:
82 # Return specific users sorted by name
83 payload_data = json.loads(payload)
84 user_names = payload_data.get('users', [])
85 users_list = [self.users[name] for name in sorted(user_names) if name in self.users]
86 return json.dumps({'users': users_list})
87
88 # Edge Case: Handle unknown endpoint
89 return json.dumps({"error": "Not found"})
90
891 def post(self, url: str, payload: str | None = None) -> str:
9 pass
92 """
93 Handle POST requests to the API.
94
95 Args:
96 url: Endpoint URL
97 payload: JSON payload
98
99 Returns:
100 JSON response string
101 """
102 # Edge Case: Handle None payload
103 if payload is None:
104 return json.dumps({"error": "Payload required"})
105
106 payload_data = json.loads(payload)
107
108 # Edge Case: Handle /add endpoint
109 if url == '/add':
110 user_name = payload_data.get('user')
111 # Edge Case: Handle missing user in payload
112 if not user_name:
113 return json.dumps({"error": "User name required"})
114 # Edge Case: Handle duplicate user
115 if user_name in self.users:
116 return json.dumps({"error": "User already exists"})
117
118 new_user = {
119 'name': user_name,
120 'owes': {},
121 'owed_by': {},
122 'balance': 0.0
123 }
124 self.users[user_name] = new_user
125 return json.dumps(new_user)
126
127 # Edge Case: Handle /iou endpoint
128 elif url == '/iou':
129 lender_name = payload_data.get('lender')
130 borrower_name = payload_data.get('borrower')
131 amount = payload_data.get('amount')
132
133 # Edge Case: Handle missing required fields
134 if not lender_name or not borrower_name or amount is None:
135 return json.dumps({"error": "Lender, borrower, and amount required"})
136
137 # Edge Case: Handle invalid amount type
138 if not isinstance(amount, (int, float)):
139 return json.dumps({"error": "Amount must be a number"})
140
141 # Edge Case: Handle negative amount
142 if amount < 0:
143 return json.dumps({"error": "Amount must be positive"})
144
145 # Edge Case: Handle same lender and borrower
146 if lender_name == borrower_name:
147 return json.dumps({"error": "Cannot create IOU between same user"})
148
149 # Get or create users
150 lender = self._get_user(lender_name)
151 borrower = self._get_user(borrower_name)
152
153 # Update lender's owed_by
154 if borrower_name not in lender['owed_by']:
155 lender['owed_by'][borrower_name] = 0.0
156 lender['owed_by'][borrower_name] = round(lender['owed_by'][borrower_name] + amount, 2)
157
158 # Update borrower's owes
159 if lender_name not in borrower['owes']:
160 borrower['owes'][lender_name] = 0.0
161 borrower['owes'][lender_name] = round(borrower['owes'][lender_name] + amount, 2)
162
163 # Update balances
164 self._update_user_balances(lender_name)
165 self._update_user_balances(borrower_name)
166
167 # Return updated users sorted by name
168 updated_users = sorted([lender, borrower], key=lambda u: u['name'])
169 return json.dumps({'users': updated_users})
170
171 # Edge Case: Handle unknown endpoint
172 return json.dumps({"error": "Not found"})
173 # Handled Edge Cases: None payload, /add endpoint, /iou endpoint, missing user in payload, duplicate user, missing required fields, invalid amount type, negative amount, same lender and borrower, unknown endpoint
174# Handled Edge Cases: Initial database with users, empty or None database, /users endpoint, unknown endpoint
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.