Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions app/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from app.db import session_scope
from app.ledger.service import TREASURY_ACCOUNT, format_mrwk, get_balance
from app.ledger_views import account_ledger_transactions
from app.models import Account
from app.models import Account, Wallet
from app.path_params import SQLITE_INTEGER_MAX
from app.serializers import (
accepted_work_for_account,
Expand Down Expand Up @@ -77,27 +77,38 @@ def github_login_from_account(account: str) -> str | None:
return login


def account_transfer_status(account: str) -> str:
def account_transfer_status(account: str, *, wallet_registered: bool = False) -> str:
if account.startswith("github:"):
return "Claim GitHub balances from /me after linking a registered mrwk1 wallet."
if account.startswith(("treasury:", "reserve:")):
return (
"Internal ledger account. MRWK wallet transfers are only available "
"for registered mrwk1 addresses."
)
return "MRWK wallet transfers are enabled for registered mrwk1 addresses."
if account.startswith("mrwk1"):
if wallet_registered:
return "MRWK wallet transfers are enabled for registered mrwk1 addresses."
return "Wallet address is not registered yet. Register this mrwk1 wallet before transfers."
return (
"This account is not eligible for MRWK wallet transfers. Use a github:<login> "
"account or registered mrwk1 wallet."
)


def account_api_context(session: Session, account: str) -> dict[str, Any]:
account = normalized_account(account)
account_row = session.get(Account, account)
wallet_registered = account.startswith("mrwk1") and session.get(Wallet, account) is not None
return {
"account": account,
"ledger_address": account,
"github_login": github_login_from_account(account),
"exists": account_row is not None,
"balance_mrwk": format_mrwk(get_balance(session, account)),
"transfer_status": account_transfer_status(account),
"transfer_status": account_transfer_status(
account,
wallet_registered=wallet_registered,
),
"accepted_work": safe_account_accepted_summary(session, account),
}

Expand Down
81 changes: 80 additions & 1 deletion tests/test_account_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from app.accounts import account_api_context, account_page_context, normalized_account
from app.db import create_schema, session_scope
from app.ledger.service import create_bounty, ensure_genesis, pay_bounty
from app.ledger.service import create_bounty, ensure_genesis, pay_bounty, register_wallet
from app.main import create_app


Expand Down Expand Up @@ -89,6 +89,85 @@ def test_registered_account_routes_preserve_api_and_page_shapes(sqlite_url: str)
assert f'href="/proofs/{proof.hash}"' in page_response.text


def test_unknown_account_transfer_status_does_not_report_wallet_ready(sqlite_url: str) -> None:
create_schema(sqlite_url)
client = TestClient(create_app(database_url=sqlite_url, webhook_secret="secret"))

response = client.get("/api/v1/accounts/not-a-wallet")

assert response.status_code == 200
assert response.json()["exists"] is False
assert response.json()["transfer_status"] == (
"This account is not eligible for MRWK wallet transfers. Use a github:<login> "
"account or registered mrwk1 wallet."
)


def test_unregistered_wallet_transfer_status_requires_registration(sqlite_url: str) -> None:
create_schema(sqlite_url)
client = TestClient(create_app(database_url=sqlite_url, webhook_secret="secret"))
account = "mrwk1" + ("0" * 40)

response = client.get(f"/api/v1/accounts/{account}")

assert response.status_code == 200
assert response.json()["exists"] is False
assert response.json()["transfer_status"] == (
"Wallet address is not registered yet. Register this mrwk1 wallet before transfers."
)


def test_ledger_only_wallet_account_transfer_status_requires_registration(
sqlite_url: str,
) -> None:
create_schema(sqlite_url)
account = "mrwk1" + ("a" * 40)
with session_scope(sqlite_url) as session:
ensure_genesis(session)
bounty = create_bounty(
session,
repo="ramimbo/mergework",
issue_number=179,
issue_url="https://github.com/ramimbo/mergework/issues/179",
title="Ledger-only wallet account",
reward_mrwk="5",
acceptance="Account route should not treat ledger-only wallets as registered.",
)
pay_bounty(
session,
bounty_id=bounty.id,
to_account=account,
submission_url="https://github.com/ramimbo/mergework/pull/179",
accepted_by="maintainer",
verifier_result={"label": "mrwk:accepted"},
)

client = TestClient(create_app(database_url=sqlite_url, webhook_secret="secret"))
response = client.get(f"/api/v1/accounts/{account}")

assert response.status_code == 200
assert response.json()["exists"] is True
assert response.json()["balance_mrwk"] == "5"
assert response.json()["transfer_status"] == (
"Wallet address is not registered yet. Register this mrwk1 wallet before transfers."
)


def test_registered_wallet_transfer_status_reports_ready(sqlite_url: str) -> None:
create_schema(sqlite_url)
with session_scope(sqlite_url) as session:
wallet = register_wallet(session, public_key_hex="1" * 64, label="Main wallet")

client = TestClient(create_app(database_url=sqlite_url, webhook_secret="secret"))
response = client.get(f"/api/v1/accounts/{wallet.address}")

assert response.status_code == 200
assert response.json()["exists"] is True
assert response.json()["transfer_status"] == (
"MRWK wallet transfers are enabled for registered mrwk1 addresses."
)


def test_normalized_account_keeps_existing_account_validation_boundaries() -> None:
assert normalized_account(" Reserve:Bounty:001 ") == "reserve:bounty:1"
assert normalized_account("MRWK1" + ("A" * 40)) == "mrwk1" + ("a" * 40)
Loading