DevPlace CI / test (push) Failing after 44m45s
Continuous Delivery could never be turned off once a crop was planted. Add a one-shot per-plot "stop after harvest" flag (POST /game/plot/stop, Devii tool game_plot_stop, game_plots.stop_after_harvest), cleared by the next plant, autoreplant claim or Refactor. Autoreplant now also respects crop locks through the shared _crop_lock predicate. Plan and measurements in happyplanting.md. Also includes the pending community sync, node network and related work in the tree; the full suite passed (4999) with all of it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
165 lines
5.5 KiB
Python
165 lines
5.5 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from devplacepy.services.devii.actions.catalog import PLATFORM_CATALOG
|
|
from devplacepy.services.devii.actions.dispatcher import (
|
|
CONFIRM_REQUIRED,
|
|
MUTATING_METHODS,
|
|
)
|
|
|
|
BY_NAME = PLATFORM_CATALOG.by_name()
|
|
|
|
|
|
def _records_mutation(name: str) -> bool:
|
|
action = BY_NAME[name]
|
|
return action.method in MUTATING_METHODS and not action.is_read_only
|
|
|
|
|
|
def test_post_query_tools_are_read_only_so_the_plan_gate_keeps_their_rows():
|
|
for name in ("db_query", "db_design_query"):
|
|
assert BY_NAME[name].is_read_only is True
|
|
assert _records_mutation(name) is False
|
|
|
|
|
|
def test_db_get_tools_are_read_only():
|
|
for name in ("db_list_tables", "db_table_schema", "db_list_rows", "db_get_row"):
|
|
assert BY_NAME[name].is_read_only is True
|
|
assert _records_mutation(name) is False
|
|
|
|
|
|
def test_db_has_no_mutation_tools():
|
|
for name in ("db_insert_row", "db_update_row", "db_delete_row", "db_restore_row"):
|
|
assert name not in BY_NAME
|
|
assert name not in CONFIRM_REQUIRED
|
|
|
|
|
|
def test_seo_report_is_public_read_only_http_action():
|
|
action = BY_NAME["seo_report"]
|
|
assert action.handler == "http"
|
|
assert action.method == "GET"
|
|
assert action.path == "/tools/seo/{uid}/report"
|
|
assert action.requires_auth is False
|
|
assert action.is_read_only is True
|
|
assert _records_mutation("seo_report") is False
|
|
assert "seo_report" not in CONFIRM_REQUIRED
|
|
names = {p.name for p in action.params}
|
|
assert "uid" in names
|
|
|
|
|
|
def test_ui_prompt_is_local_interaction_action():
|
|
from devplacepy.services.devii.registry import CATALOG
|
|
|
|
by_name = CATALOG.by_name()
|
|
action = by_name["ui_prompt"]
|
|
assert action.handler == "interaction"
|
|
assert action.requires_auth is False
|
|
names = {p.name for p in action.params}
|
|
assert "title" in names
|
|
assert "widgets" in names
|
|
assert "ui_cancel" in by_name
|
|
assert "ui_notify" in by_name
|
|
|
|
|
|
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
|
|
|
|
|
|
def test_game_actions_registered_with_correct_auth():
|
|
public = {"game_state", "game_leaderboard", "game_view_farm"}
|
|
auth = {
|
|
"game_plant",
|
|
"game_harvest",
|
|
"game_plot_stop",
|
|
"game_buy_plot",
|
|
"game_upgrade_ci",
|
|
"game_water",
|
|
"game_fertilize",
|
|
"game_daily",
|
|
"game_upgrade_perk",
|
|
"game_claim_quest",
|
|
"game_prestige",
|
|
}
|
|
for name in public | auth:
|
|
assert name in BY_NAME, name
|
|
assert BY_NAME["game_state"].requires_auth is True
|
|
assert BY_NAME["game_leaderboard"].requires_auth is False
|
|
assert BY_NAME["game_view_farm"].requires_auth is False
|
|
for name in auth:
|
|
assert BY_NAME[name].requires_auth is True, name
|
|
|
|
|
|
def test_game_read_actions_are_read_only():
|
|
for name in ("game_state", "game_leaderboard", "game_view_farm"):
|
|
assert BY_NAME[name].is_read_only is True
|
|
|
|
|
|
def test_scheduling_tools_need_an_account_but_not_an_administrator():
|
|
from devplacepy.services.devii.registry import CATALOG
|
|
|
|
by_name = CATALOG.by_name()
|
|
for name in ("create_task", "update_task", "run_task_now"):
|
|
action = by_name[name]
|
|
assert action.requires_auth is True
|
|
assert action.requires_admin is False
|
|
|
|
|
|
def test_scheduling_tools_are_offered_to_a_member():
|
|
from devplacepy.services.devii.registry import CATALOG
|
|
|
|
member = {
|
|
s["function"]["name"]
|
|
for s in CATALOG.tool_schemas_for(authenticated=True, is_admin=False)
|
|
}
|
|
for name in ("create_task", "update_task", "run_task_now", "list_tasks"):
|
|
assert name in member
|
|
|
|
|
|
def test_scheduling_tools_are_hidden_from_guests():
|
|
from devplacepy.services.devii.registry import CATALOG
|
|
|
|
guest = {
|
|
s["function"]["name"] for s in CATALOG.tool_schemas_for(authenticated=False)
|
|
}
|
|
for name in ("create_task", "update_task", "run_task_now"):
|
|
assert name not in guest
|
|
|
|
|
|
def test_sync_tools_are_admin_only_and_match_the_routes():
|
|
from devplacepy.main import app
|
|
from devplacepy.services.sync.spec import KINDS
|
|
|
|
paths = set(app.openapi()["paths"])
|
|
for kind in KINDS:
|
|
action = BY_NAME[f"sync_{kind}"]
|
|
assert action.method == "POST" and action.path == f"/sync/{kind}"
|
|
assert action.requires_admin is True
|
|
assert action.path in paths
|
|
assert {param.name for param in action.params} == {"origin", "records", "dry_run"}
|
|
assert _records_mutation(action.name) is True
|