Skip to content

Extract account helpers from app.main#359

Closed
TUPM96 wants to merge 2 commits into
ramimbo:mainfrom
TUPM96:codex/bounty-320-account-boundary
Closed

Extract account helpers from app.main#359
TUPM96 wants to merge 2 commits into
ramimbo:mainfrom
TUPM96:codex/bounty-320-account-boundary

Conversation

@TUPM96
Copy link
Copy Markdown
Contributor

@TUPM96 TUPM96 commented May 26, 2026

Refs #320

Summary

  • Move account normalization, GitHub-login extraction, transfer status text, and account response shaping into app.accounts.
  • Keep app.main behavior stable while replacing inline account validation and account API response assembly with focused helpers.
  • Add direct account-helper tests covering canonicalization, invalid accounts, and API summary shaping.
  • Preserve the existing OAuth encoded-backslash next-path safety expectation required by the test suite.

Complexity reduced

  • app.main no longer owns account-address parsing rules, reserve/treasury/wallet/GitHub normalization, transfer status wording, or account response layout. Account behavior can now be tested without routing through FastAPI, while API/page/MCP callers share one boundary.

Tests

  • python -m ruff check .
  • python -m pytest tests\test_wallet_api.py::test_github_login_stores_safe_default_for_backslash_next tests\test_accounts.py tests\test_account_validation.py -q
  • python -m pytest -q

Copilot AI review requested due to automatic review settings May 26, 2026 01:49
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Warning

Review limit reached

@TUPM96, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 1 minute and 32 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0c755283-e3e3-4532-b5d0-f64908489b19

📥 Commits

Reviewing files that changed from the base of the PR and between ac99d79 and 14ae298.

📒 Files selected for processing (3)
  • app/accounts.py
  • app/main.py
  • tests/test_accounts.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR centralizes account parsing/validation and account-related response shaping into a dedicated app.accounts module, then updates the API to use those helpers while adding unit tests for the new behavior.

Changes:

  • Added app/accounts.py with normalize_account, github_login_from_account, transfer_status_for_account, and account_summary.
  • Refactored /api/v1/accounts/{account} and related helpers in app/main.py to use the new accounts module.
  • Added pytest coverage for normalization and summary shaping in tests/test_accounts.py.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
tests/test_accounts.py Adds tests for account normalization and API summary shaping.
app/main.py Replaces inline account parsing/summary logic with app.accounts helpers and hardens next_path decoding checks.
app/accounts.py Introduces a single source of truth for account normalization and summary metadata.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/main.py
Comment on lines 285 to +296
def _safe_next_path(next_path: str | None) -> str:
decoded_next_path = unquote(next_path) if next_path else ""
if (
not next_path
or not next_path.startswith("/")
or next_path.startswith("//")
or len(next_path) > 2048
or "\\" in next_path
or decoded_next_path.startswith("//")
or "\\" in decoded_next_path
or any(ord(char) < 32 or 127 <= ord(char) < 160 for char in next_path)
or any(ord(char) < 32 or 127 <= ord(char) < 160 for char in decoded_next_path)
Comment thread app/accounts.py
Comment on lines +83 to +96
def _normalize_reserve_account(lower_account: str) -> str:
reserve_prefix = "reserve:bounty:"
if not lower_account.startswith(reserve_prefix):
raise AccountError("reserve account must use reserve:bounty:<id>")
bounty_id = lower_account.removeprefix(reserve_prefix)
try:
normalized_bounty_id = int(bounty_id) if bounty_id.isdigit() else 0
except ValueError as exc:
raise AccountError("reserve bounty id is too large") from exc
if normalized_bounty_id <= 0:
raise AccountError("reserve bounty id must be positive")
if normalized_bounty_id > SQLITE_INTEGER_MAX:
raise AccountError("reserve bounty id is too large")
return f"{reserve_prefix}{normalized_bounty_id}"
Comment thread app/accounts.py
Comment on lines +53 to +55
if not account.startswith("github:"):
return None
login = account.removeprefix("github:")
Comment thread app/accounts.py
Comment on lines +94 to +95
if normalized_bounty_id > SQLITE_INTEGER_MAX:
raise AccountError("reserve bounty id is too large")
Copy link
Copy Markdown
Contributor

@tolga-tom-nook tolga-tom-nook left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers found on PR #359 at head 14ae298.

Evidence checked:

  • Inspected the account-helper extraction from app/main.py into app/accounts.py; _normalized_account() still maps helper AccountError failures to HTTP 400, and canonicalization of treasury, reserve, GitHub, and mrwk1 wallet accounts is preserved.
  • Verified account_summary() preserves the existing /api/v1/accounts/{account} response shape, including ledger_address, GitHub login extraction for normalized github: accounts, balance string, transfer status text, and accepted-work payload.
  • Checked the OAuth next path hardening added in app/main.py; percent-decoded // and backslash paths now fall back to /me while normal relative paths remain allowed.
  • Reviewed the Copilot comments: the raw-case github_login_from_account() concern is not a blocker because public account callers normalize before summary construction, and the reserve upper bound is enforced by SQLITE_INTEGER_MAX even though an extra explicit unit vector could further document it.

Commands run locally:

  • ./.venv/bin/python -m pytest tests/test_accounts.py tests/test_wallet_api.py -q → 42 passed
  • ./.venv/bin/python -m ruff check app/accounts.py app/main.py tests/test_accounts.py → passed
  • ./.venv/bin/python -m ruff format --check app/accounts.py app/main.py tests/test_accounts.py → passed
  • ./.venv/bin/python -m mypy app → passed
  • git diff --check origin/main...HEAD → passed

Copy link
Copy Markdown
Contributor

@newmattock newmattock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers found on current head 14ae298.

Evidence checked:

  • Inspected app/accounts.py, account route delegation in app/main.py, and tests/test_accounts.py.
  • Verified normalize_account() preserves the old account boundary behavior for treasury, reserve bounty, GitHub, wallet, and unknown ledger account strings, including lower-casing/canonicalizing known account types and surfacing invalid values through the existing HTTP 400 wrapper.
  • Verified account_summary() preserves the /api/v1/accounts/{account} payload shape: ledger address, GitHub login extraction, existence flag, formatted balance, transfer status text, and accepted-work summary.
  • Rechecked the branch's encoded OAuth next hardening so decoded backslash/redirect-like payloads fall back to /me while normal relative paths remain allowed.

Validation run locally:

  • uv run --extra dev python -m pytest tests/test_accounts.py tests/test_api_mcp.py::test_account_api_reports_internal_ledger_account_transfer_status tests/test_security.py::test_oauth_next_path_rejects_external_or_headerlike_paths tests/test_wallet_api.py::test_oauth_next_path_rejects_redirect_ambiguity tests/test_wallet_api.py::test_github_login_stores_safe_default_for_backslash_next -q -> 28 passed.
  • uv run --extra dev python -m mypy app/accounts.py app/main.py -> success.
  • uv run --extra dev python -m ruff check app/accounts.py app/main.py tests/test_accounts.py -> passed.
  • uv run --extra dev python -m ruff format --check app/accounts.py app/main.py tests/test_accounts.py -> 3 files already formatted.
  • git diff --check origin/main...HEAD -> clean.

@ramimbo ramimbo added the mrwk:needs-info More information needed label May 26, 2026
@ramimbo
Copy link
Copy Markdown
Owner

ramimbo commented May 26, 2026

Maintainer note after #320 filled: this PR still references closed bounty #320. If you want it considered for the current code-health round, rebase against main, update the bounty reference to #377, and keep the scope distinct from already accepted #328, #337, #364, #375, and #376. This is not accepted or paid as-is.

@ramimbo
Copy link
Copy Markdown
Owner

ramimbo commented May 26, 2026

Closing this stale bounty submission. It still references a closed/filled bounty round and is dirty or unstable against current main after the route/test extraction batches. A future attempt needs a fresh PR against current main, a currently open bounty reference, and scope distinct from work already accepted.

@ramimbo ramimbo added the mrwk:rejected Submission rejected label May 26, 2026
@ramimbo ramimbo closed this May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mrwk:needs-info More information needed mrwk:rejected Submission rejected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants