Commit graph

123 commits

Author SHA1 Message Date
c3a501e3b2
Extract availability logic to AvailabilityService
- Create AvailabilityService with get_availability_for_range(), set_availability_for_date(), and copy_availability()
- Move slot validation logic to service
- Update routes/availability.py to use AvailabilityService
- Remove all direct database queries from routes
2025-12-25 18:31:13 +01:00
badb45da59
Extract price logic to PriceService
- Create PriceService with get_recent_prices() and fetch_and_store_price()
- Update routes/audit.py to use PriceService instead of direct queries
- Use PriceHistoryMapper consistently
- Update test to patch services.price.fetch_btc_eur_price
2025-12-25 18:30:26 +01:00
168b67acee
refactors 2025-12-25 18:27:59 +01:00
f46d2ae8b3
refactors 2025-12-25 00:59:57 +01:00
139a5fbef3
parallel tests 2025-12-25 00:48:22 +01:00
73a45b81cc
fast back 2025-12-25 00:33:05 +01:00
4be45f8f7c
lots of stuff 2025-12-23 17:03:51 +01:00
43a58565c0
Fix test to use different dates for multiple trades 2025-12-23 15:57:43 +01:00
0c75583930
Add endpoint to get a single trade by ID 2025-12-23 15:52:02 +01:00
04192799ab
Add validation to prevent booking two trades on the same day 2025-12-23 15:50:14 +01:00
8936d802a6
Add Lightning amount threshold validation 2025-12-23 14:46:03 +01:00
28e8ba218f
Update create_exchange endpoint to accept and validate bitcoin_transfer_method 2025-12-23 14:40:42 +01:00
d82829ab40
Add bitcoin_transfer_method to ExchangeRequest and response schemas 2025-12-23 14:36:39 +01:00
e1c7c5209f
Add bitcoin_transfer_method field to Exchange model with default value 2025-12-23 14:34:22 +01:00
0669f951bf
Add bitcoin_transfer_methods to constants endpoint 2025-12-23 14:28:28 +01:00
cecb8b33a7
Update constants validation to check bitcoinTransferMethods and lightningMaxEur 2025-12-23 14:26:24 +01:00
25698188e7
Add BitcoinTransferMethod enum to backend models 2025-12-23 14:09:57 +01:00
2efbd2c665
fix: derive valid slot minutes from SLOT_DURATION_MINUTES constant
Replace hardcoded (0, 15, 30, 45) tuple with computed range based on
the SLOT_DURATION_MINUTES constant. This ensures the validation stays
in sync if the slot duration is ever changed.

Add test to verify slot minute boundary validation.
2025-12-23 12:17:40 +01:00
ca3a08a236
test: improve e2e tests and add COMPLETE_EXCHANGE permission tests
- Fix E2E test assertion for buy/sell direction change
- Add data-testid to date buttons for reliable e2e selection
- Update e2e tests to use data-testid instead of fragile weekday matching
- Add tests for regular user cannot complete/no-show trades (COMPLETE_EXCHANGE permission)
2025-12-23 11:00:32 +01:00
ef01a970d5
feat: add /api/admin/users/search endpoint
- Add endpoint to search users by email (case-insensitive)
- Limit results to 10 for autocomplete purposes
- Require VIEW_ALL_EXCHANGES permission (admin only)
- Add tests for search functionality and access control
2025-12-23 10:55:44 +01:00
ce8f5a1183
test: Add test for cancelled slot becoming available again
Verifies that when a user cancels their trade, the slot becomes
available for booking by other users.
2025-12-23 10:41:08 +01:00
29b0438416
fix: Prevent user from cancelling trades after slot time has passed
Users can no longer cancel trades once the slot time has passed.
Added test to verify this behavior.
2025-12-23 10:39:09 +01:00
3a22534c04
fix: Use COMPLETE_EXCHANGE permission for complete/no-show endpoints
The complete_trade and mark_no_show endpoints now use the dedicated
COMPLETE_EXCHANGE permission instead of CANCEL_ANY_EXCHANGE, which
better reflects the semantics of these operations.
2025-12-23 10:37:32 +01:00
bf57fc6b77
fix: Remove agreed_price from price API response
The agreed_price depends on trade direction (buy/sell) and must be
calculated on the frontend. Returning a buy-side-only agreed_price
from the API was misleading and unused.

Frontend already calculates the direction-aware price correctly.
2025-12-23 10:36:18 +01:00
1008eea2d9
Fix: Update permissions and add missing /api/exchange/slots endpoint
- Updated auth-context.tsx to use new exchange permissions
  (CREATE_EXCHANGE, VIEW_OWN_EXCHANGES, etc.) instead of old
  appointment permissions (BOOK_APPOINTMENT, etc.)

- Updated exchange/page.tsx, trades/page.tsx, admin/trades/page.tsx
  to use correct permission constants

- Updated profile/page.test.tsx mock permissions

- Updated admin/availability/page.tsx to use constants.exchange
  instead of constants.booking

- Added /api/exchange/slots endpoint to return available slots
  for a date, filtering out already booked slots

- Fixed E2E tests:
  - exchange.spec.ts: Wait for button to be enabled before clicking
  - permissions.spec.ts: Use more specific heading selector
  - price-history.spec.ts: Expect /exchange redirect for regular users
2025-12-22 21:42:42 +01:00
fa07490b7b
Fix: Update permission names, models and constants
Permission renames:
- BOOK_APPOINTMENT -> CREATE_EXCHANGE
- VIEW_OWN_APPOINTMENTS -> VIEW_OWN_EXCHANGES
- CANCEL_OWN_APPOINTMENT -> CANCEL_OWN_EXCHANGE
- VIEW_ALL_APPOINTMENTS -> VIEW_ALL_EXCHANGES
- CANCEL_ANY_APPOINTMENT -> CANCEL_ANY_EXCHANGE
- Add COMPLETE_EXCHANGE permission

Model changes:
- Delete AppointmentStatus enum
- Delete Appointment model

Schema changes:
- Delete BookingRequest (was for old booking)
- Delete AppointmentResponse, PaginatedAppointments
- Delete BookableSlot, AvailableSlotsResponse (unused)

Constants changes:
- Remove appointmentStatuses from shared/constants.json
- Merge booking constants into exchange section
- Update shared_constants.py and validate_constants.py
2025-12-22 20:28:21 +01:00
743129b11d
Fix: Update test_permissions.py to use new exchange permissions 2025-12-22 20:23:41 +01:00
edc292986f
Fix: Delete deprecated test_booking.py 2025-12-22 20:23:09 +01:00
3f06103a67
Fix: Remove booking import from main.py 2025-12-22 20:19:23 +01:00
bbd9fae763
Phase 7: Final cleanup - Remove deprecated booking/appointment code
Deleted deprecated files:
- backend/routes/booking.py
- frontend/app/admin/appointments/, booking/, appointments/, sum/, audit/
- frontend/app/utils/appointment.ts
- frontend/e2e/booking.spec.ts, appointments.spec.ts

Updated references:
- exchange/page.tsx: Use /api/exchange/slots instead of /api/booking/slots
- useRequireAuth.ts: Redirect to /admin/trades and /exchange
- profile.tsx, invites.tsx: Update fallback redirect
- E2E tests: Update all /audit references to /admin/trades
- profile.test.tsx: Update admin redirect test
2025-12-22 20:18:33 +01:00
811fdf2663
Phase 2.5: Add exchange endpoint tests
Comprehensive test coverage for exchange endpoints:
- Price endpoint: permission checks, price retrieval, staleness, config
- Create exchange: buy/sell, double booking, validation, stale price
- User trades: list trades, cancel own trade, cancel restrictions
- Admin trades: view upcoming/past, complete, no-show, cancel

Tests mock the Bitfinex price fetcher to ensure deterministic results.
2025-12-22 18:48:23 +01:00
d39ada1bef
Phase 2.4: Add admin exchange endpoints
Admin trade management:
- GET /api/admin/trades/upcoming: Upcoming booked trades (sorted by time)
- GET /api/admin/trades/past: Past trades with filters
  - status, start_date, end_date, user_search
- POST /api/admin/trades/{id}/complete: Mark as completed (after slot time)
- POST /api/admin/trades/{id}/no-show: Mark as no-show (after slot time)
- POST /api/admin/trades/{id}/cancel: Admin cancel trade

AdminExchangeResponse includes user contact info for admin view.
2025-12-22 18:34:56 +01:00
ce9159c5b0
Phase 2.1-2.3: Add exchange endpoints
Add exchange trading endpoints:
- POST /api/exchange: Create exchange trade
  - Validates slot, price staleness, EUR amount limits
  - Calculates sats from EUR and agreed price
  - Direction-specific premium (buy=+5%, sell=-5%)

- GET /api/trades: List user's exchanges
- POST /api/trades/{id}/cancel: Cancel user's exchange

Add schemas:
- ExchangeRequest, ExchangeResponse
- ExchangeUserContact, AdminExchangeResponse (for Phase 2.4)
- PaginatedExchanges, PaginatedAdminExchanges
2025-12-22 18:28:56 +01:00
2702b66fd2
Phase 1.3: Create price endpoint for users
Add GET /api/exchange/price endpoint:
- Available to regular users (BOOK_APPOINTMENT permission)
- Returns current BTC/EUR price with admin premium applied
- Uses cached price from PriceHistory if not stale
- Fetches fresh price from Bitfinex if needed
- Returns is_stale flag when price is older than 5 minutes
- Includes exchange configuration (min/max EUR, increment)
- Handles fetch failures gracefully (returns stale price with error)
2025-12-22 18:22:46 +01:00
61e95e56d5
Phase 1.2: Create Exchange model
Add Exchange model with:
- user_id / user relationship
- slot_start / slot_end (datetime)
- direction (TradeDirection enum: buy/sell)
- eur_amount (int, cents), sats_amount (int)
- market_price_eur, agreed_price_eur (float)
- premium_percentage (int, snapshot)
- status (ExchangeStatus enum)
- created_at, cancelled_at, completed_at timestamps
- unique constraint on slot_start
2025-12-22 18:19:26 +01:00
30e5d0828e
Phase 1.1: Add exchange configuration
- Add exchange constants to shared/constants.json:
  - eurTradeMin: 100, eurTradeMax: 3000, eurTradeIncrement: 20
  - premiumPercentage: 5
  - priceRefreshSeconds: 60, priceStalenessSeconds: 300
- Add exchangeStatuses and tradeDirections to shared constants
- Add ExchangeStatus and TradeDirection enums to models.py
- Update shared_constants.py to export new exchange constants
- Update validate_constants.py to validate new enums and fields
2025-12-22 18:16:35 +01:00
5bad1e7e17
Phase 0.1: Remove backend deprecated code
- Delete routes: counter.py, sum.py
- Delete jobs.py and worker.py
- Delete tests: test_counter.py, test_jobs.py
- Update audit.py: keep only price-history endpoints
- Update models.py: remove VIEW_COUNTER, INCREMENT_COUNTER, USE_SUM permissions
- Update models.py: remove Counter, SumRecord, CounterRecord, RandomNumberOutcome models
- Update schemas.py: remove sum/counter related schemas
- Update main.py: remove deleted router imports
- Update test_permissions.py: remove tests for deprecated features
- Update test_price_history.py: remove worker-related tests
- Update conftest.py: remove mock_enqueue_job fixture
- Update auth.py: fix example in docstring
2025-12-22 18:07:14 +01:00
1af0854d80
fix: improve worker task failure handling
Use asyncio.wait with FIRST_EXCEPTION to:
- Properly name tasks for better error logging
- Cancel remaining tasks when one fails
- Log which specific manager failed before propagating the exception
2025-12-22 16:24:40 +01:00
3806361fac
feat: add FETCH_PRICE permission for manual price fetch endpoint
The POST /api/audit/price-history/fetch endpoint now requires
FETCH_PRICE permission instead of VIEW_AUDIT, which is more
semantically correct since it's a write operation.
2025-12-22 16:22:54 +01:00
54709888e1
refactor: extract httpx mock helpers in price history tests
- create_mock_httpx_client() for mocking AsyncClient with various configs
- create_bitfinex_ticker_response() for creating ticker response arrays

Reduces test boilerplate significantly.
2025-12-22 16:21:18 +01:00
de12300593
fix: use Python datetime for created_at in worker
Consistent with model default which uses datetime.now(UTC) rather than
SQL NOW() for created_at field.
2025-12-22 16:18:51 +01:00
b0d5d51a21
refactor: extract _to_price_history_response helper function
Consistent with other response conversion functions in the codebase
(_to_counter_record_response, _to_invite_response, etc.)
2025-12-22 16:17:43 +01:00
3abc7b18c6
test: add test for network errors (httpx.ConnectError)
The docstring of fetch_btc_eur_price mentions it raises httpx.RequestError
on network errors, but this wasn't tested. Add test for ConnectError.
2025-12-22 16:11:00 +01:00
13d2e4adeb
refactor: move JOB_FETCH_BITCOIN_PRICE constant to worker.py
Unlike JOB_RANDOM_NUMBER which is used for external job enqueueing,
JOB_FETCH_BITCOIN_PRICE is only used internally by the scheduler.
Move it to worker.py to clarify it's not part of the public job API.
2025-12-22 16:10:13 +01:00
a5488fd20b
fix: handle unique constraint violation in manual fetch endpoint
When a duplicate timestamp occurs (rare but possible), return the
existing record instead of failing with a 500 error. This matches
the worker's ON CONFLICT DO NOTHING behavior.

Added test for duplicate timestamp handling.
2025-12-22 16:09:05 +01:00
1c9761e559
feat: add explicit 30 second timeout for Bitfinex API requests
Previously used httpx's implicit default (5 seconds). Explicit timeout
makes behavior clear and gives more time for slow responses.
2025-12-22 16:07:33 +01:00
ec835a2935
refactor: extract 'bitfinex' and 'BTC/EUR' magic strings to constants
Add SOURCE_BITFINEX and PAIR_BTC_EUR constants in price_fetcher.py and
use them consistently in routes/audit.py, worker.py, and tests.
2025-12-22 16:06:56 +01:00
9db43c474e
test: add unit tests for scheduled Bitcoin price job handler 2025-12-22 15:53:05 +01:00
cd2285395d
feat: add scheduled Bitcoin price fetch job running every minute 2025-12-22 15:51:57 +01:00
4e96b9ee28
feat: add JOB_FETCH_BITCOIN_PRICE constant 2025-12-22 15:49:57 +01:00