docs/testing/test_inventory.md: honest, current list of what automated
coverage actually exists (38 backend pytest tests across 2 files, 0 Godot
tests merged yet) and what doesn't (no integration/endpoint tests at all,
no coverage yet for the in-review RS256/moderation work) -- verified
against a genuinely fresh checkout + fresh Postgres, not just read off the
file tree.
scripts/run_checkpoint_tests.py: runs the whole inventory in one shot --
throwaway Postgres, migrations, pytest, then starts the backend server and
runs every scripts/smoke_test_*.gd against it headlessly. Verified working
both with and without a smoke test present (temporarily tested against
PC1's not-yet-merged scripts/api_client.gd + smoke_test_api.gd, removed
before this commit -- that PR carries its own files).
docs/testing/README.md: what running this at a Package Checkpoint review
does and doesn't prove, and how to grow the inventory as real coverage
gets added.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two Godot --script-mode-only quirks, neither a bug in ApiClient itself
(verified working against a real backend, health_check returns 200):
1. Bare autoload identifiers don't resolve at compile time in --script/
SceneTree runs (the node exists at runtime, root.has_node() is true,
but referencing it as a plain global name fails to compile) -- use
get_node() instead.
2. A freshly add_child()'d HTTPRequest isn't reliably "inside tree" yet
at _initialize() time, since --script mode hasn't processed a frame
yet -- one process_frame await fixes it.
Verified: full smoke test now passes end-to-end against a live backend
(docker compose postgres + alembic migrations + uvicorn), confirming
ApiClient.health_check() genuinely round-trips.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
world_visibility and asset_type were each declared twice: once explicitly
via `.create(checkfirst=True)`, and again implicitly by SQLAlchemy when
create_table() compiled the inline postgresql.ENUM() column type (which
defaults create_type=True). On a genuinely empty database this raises
asyncpg.DuplicateObjectError mid-transaction and the migration never
completes -- verified this was true 100% of the time from a fresh
container, meaning the backend has never successfully bootstrapped from
scratch. Fixed by passing create_type=False on the inline column
declarations, since the type is already created explicitly above.
Also parameterized both services' host ports (POSTGRES_HOST_PORT /
REDIS_HOST_PORT, defaulting to the original 5432/6379) so concurrent
agent workspaces sharing this same docker-compose.yml can run isolated
databases without colliding on the host port.
Requesting an explicit ?visibility=PRIVATE (or FRIENDS_ONLY) skipped the
ownership check entirely — any authenticated user could list every other
user's private worlds just by passing the filter. Only the no-filter
default path enforced ownership. Now the ownership check applies whenever
a non-PUBLIC visibility is requested, explicit or not.
Also fixed current_user: CurrentUser = Depends() in assets.py (redundant
empty Depends() on an already-Annotated dependency, inconsistent with
every other endpoint) to plain current_user: CurrentUser per the review
feedback, and reordered params since that made it a non-default arg
following a defaulted one (SyntaxError otherwise).
Verified: py_compile clean, full app imports and loads in a clean venv.
Verified all three options from the task against Godot 4.6.2 directly
(godot --headless), rather than trusting the probe scripts' own printed
output from earlier sessions:
- ScriptServer doesn't exist as a class; no disable_os/disable_file_access/
disable_network project settings exist either. Nothing to configure.
- SubViewport isolates rendering/input only, not script capabilities — a
script inside one has identical OS/FileAccess/network access.
- Expression is not safe by default (can still reach unsafe methods through
a base instance); blocklisting dangerous substrings is trivially bypassed.
Recommendation: don't sandbox GDScript in-process (nothing to sandbox
with) — use a narrow non-Turing-complete content DSL for the common case,
and OS-level process isolation only if genuinely general scripting turns
out to be required.