Nobody has started this yet — be first.
Business impact
The from/to query params on GET /api/audit-log exist for exactly one kind of moment: someone is investigating an incident ("show me everything that happened after the suspicious login at 14:32") or answering a compliance request ("show me all admin actions in Q1"). Right now, asking for entries after a given time returns entries before it, and vice versa -- silently. During an active investigation, that produces a confidently-wrong answer ("nothing happened after the breach," when the tool is actually showing everything before it) exactly when correctness matters most and nobody is inclined to double-check the tool.
Problem
?from=T filters for entries <= T and ?to=T filters for entries >= T -- the two comparison operators are swapped relative to what the parameter names promise.
Current behavior
list_audit_log builds query.filter(AuditLog.created_at <= datetime.fromisoformat(date_from)) for the from param and query.filter(AuditLog.created_at >= datetime.fromisoformat(date_to)) for the to param -- both comparison operators are the opposite of what "from this point onward" and "up to this point" mean in plain English.
Expected behavior
?from=T returns entries at or after T (created_at >= T). ?to=T returns entries at or before T (). (with ) returns entries in the closed range , not entries outside that range and not an empty result when there is real data inside it.
Steps to reproduce
Why this matters
This is the kind of bug a quick manual test with a single boundary value can miss entirely -- if you only ever tried from or to alone against data that happened to be all on one side of the timestamp, both the correct and the swapped version can appear to "work." It takes a test with entries deliberately placed on both sides of the query boundary, checked against both parameters independently, to expose that the operators are transposed rather than just off by a rounding error.
Suggested approach
Look at the two query.filter(...) calls for date_from/date_to in list_audit_log. Read each comparison operator against what the parameter name (from, to) actually promises, not against what the other one does.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_05_audit_log_date_filter_swap.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/audit-log-date-filter-swapFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/audit-log-date-filter-swapSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign increated_at <= T?from=A&to=BA < B[A, B]acmeglobexmake seed
*.localhost subdomains resolve to 127.0.0.1 out of the box in curl and every modern browser -- no /etc/hosts editing needed:
curl -s -X POST http://acme.localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
--cookie-jar /tmp/acme.cookies --cookie /tmp/acme.cookies \
-d '{"email": "owner@acme.test", "password": "owner-acme"}'
curl -s http://acme.localhost:5000/api/tenant/settings --cookie /tmp/acme.cookies
For tooling that cannot fake subdomains (some HTTP clients, CI), send X-Tenant-Slug instead -- honored only when TRUST_TENANT_HEADER=true (the Docker dev setup enables it; production does not):
curl -s http://localhost:5000/api/tenant/settings -H "X-Tenant-Slug: acme"
You do not strictly need Docker to work the tickets, but -- unlike a project that can fake its backing services -- this one genuinely needs a real Postgres and Redis, because the whole point is proving isolation against the real database, not a mock. Without Docker: cp .env.example .env, point DATABASE_URL/ADMIN_DATABASE_URL/TEST_DATABASE_URL/TEST_ADMIN_DATABASE_URL/REDIS_URL at a local Postgres/Redis (docker/init-db.sql documents the exact roles/databases the app expects -- notably the saas_admin/BYPASSRLS role migrations need and the plain saas role every request uses), then:
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/flask db upgrade
.venv/bin/pytest practice_tickets/tests/test_ticket_07_webhook_signature_raw_body.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
./practice_tickets/run_tickets.sh 07 12 13 # just a few, by number
All 15 tests fail at the start; as you fix each ticket in app/ (or, for TICKET-12, in migrations/versions/0002_row_level_security.py, plus re-applying it), its test -- and only its test -- should flip to passing. make test runs the project's own main suite; several of its tests (RBAC, tenant-spoofing, and billing-webhook tests among them) fail at the start for the same underlying reasons as specific tickets -- fixing those tickets fixes those failures too, not a separate task.
Level 1
Fix a bug
Read existing behaviour, correct it.