docs: add block/mute user relations, emoji-sync CLI, and uid indexes
DevPlace CI / test (push) Failing after 2m7s
DevPlace CI / test (push) Failing after 2m7s
- Add `/block`, `/mute` endpoints with block/unblock and mute/unmute functionality in `routers/relations.py`, hiding blocked users' content everywhere except their own profile while muting only suppresses notifications - Introduce `devplace emoji-sync` CLI command to regenerate `static/js/emoji-shortcodes.js` from the emoji library, documented in `CLAUDE.md` and wired in `cli.py` - Create `get_blocked_uids()` database helper and apply it in `content.py` `load_detail()` to filter blocked users' posts from detail views - Implement `_uid_index()` and `_drop_index()` helpers in `database.py` for unique uid indexes across tables, with `user_relations` added to `SOFT_DELETE_TABLES` - Document new routes in `AGENTS.md` and `README.md`, including emoji shortcodes rendering behavior distinct from the emoji picker
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import asyncio
|
||||
import types
|
||||
|
||||
from devplacepy.services.bot import bot as bot_module
|
||||
from devplacepy.services.bot.bot import DevPlaceBot
|
||||
|
||||
STALE_KEY = "019ec5b5-410c-7660-a129-c80589859080"
|
||||
OWN_KEY = "019ed655-b699-7fa0-bf25-dd77910fb6d4"
|
||||
|
||||
|
||||
def _fake_bot(cached_key, fetched_key):
|
||||
fake = types.SimpleNamespace()
|
||||
fake.state = types.SimpleNamespace(account_api_key=cached_key)
|
||||
fake.llm = types.SimpleNamespace(api_key=cached_key)
|
||||
fake.saves = []
|
||||
fake.logs = []
|
||||
|
||||
async def _fetch():
|
||||
return fetched_key
|
||||
|
||||
fake._fetch_account_api_key = _fetch
|
||||
fake._save = lambda: fake.saves.append(fake.state.account_api_key)
|
||||
fake._log = lambda *args, **kwargs: fake.logs.append(args[0] if args else "")
|
||||
return fake
|
||||
|
||||
|
||||
def test_adopt_replaces_stale_cached_key_from_recycled_slot():
|
||||
fake = _fake_bot(cached_key=STALE_KEY, fetched_key=OWN_KEY)
|
||||
result = asyncio.run(DevPlaceBot._adopt_account_api_key(fake))
|
||||
assert result is True
|
||||
assert fake.llm.api_key == OWN_KEY
|
||||
assert fake.state.account_api_key == OWN_KEY
|
||||
assert fake.saves == [OWN_KEY]
|
||||
|
||||
|
||||
def test_adopt_keeps_matching_key_without_resaving():
|
||||
fake = _fake_bot(cached_key=OWN_KEY, fetched_key=OWN_KEY)
|
||||
result = asyncio.run(DevPlaceBot._adopt_account_api_key(fake))
|
||||
assert result is True
|
||||
assert fake.llm.api_key == OWN_KEY
|
||||
assert fake.saves == []
|
||||
|
||||
|
||||
def test_adopt_fails_when_own_key_unavailable(monkeypatch):
|
||||
async def _instant_sleep(_seconds):
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(bot_module.asyncio, "sleep", _instant_sleep)
|
||||
fake = _fake_bot(cached_key=STALE_KEY, fetched_key="")
|
||||
result = asyncio.run(DevPlaceBot._adopt_account_api_key(fake))
|
||||
assert result is False
|
||||
assert fake.llm.api_key == STALE_KEY
|
||||
assert fake.saves == []
|
||||
@@ -46,3 +46,34 @@ def test_seo_report_is_public_read_only_http_action():
|
||||
assert "seo_report" not in CONFIRM_REQUIRED
|
||||
names = {p.name for p in action.params}
|
||||
assert "uid" in names
|
||||
|
||||
|
||||
def test_block_mute_tools_exist_as_http_actions():
|
||||
expected = {
|
||||
"block_user": "/block/{username}",
|
||||
"unblock_user": "/block/unblock/{username}",
|
||||
"mute_user": "/mute/{username}",
|
||||
"unmute_user": "/mute/unmute/{username}",
|
||||
}
|
||||
for name, path in expected.items():
|
||||
action = BY_NAME[name]
|
||||
assert action.handler == "http"
|
||||
assert action.method == "POST"
|
||||
assert action.path == path
|
||||
assert action.requires_auth is True
|
||||
assert action.requires_admin is False
|
||||
assert "username" in {p.name for p in action.params}
|
||||
|
||||
|
||||
def test_block_mute_tools_visible_to_authenticated_user():
|
||||
schemas = PLATFORM_CATALOG.tool_schemas_for(authenticated=True, is_admin=False)
|
||||
names = {s["function"]["name"] for s in schemas}
|
||||
for name in ("block_user", "unblock_user", "mute_user", "unmute_user"):
|
||||
assert name in names
|
||||
|
||||
|
||||
def test_block_mute_tools_hidden_from_guests():
|
||||
schemas = PLATFORM_CATALOG.tool_schemas_for(authenticated=False)
|
||||
names = {s["function"]["name"] for s in schemas}
|
||||
for name in ("block_user", "unblock_user", "mute_user", "unmute_user"):
|
||||
assert name not in names
|
||||
|
||||
Reference in New Issue
Block a user