feat: add mod editing, revision merges and secure game storage

Add immutable revisions, PGVN editor, asset replacement, merge and rebase workflows, authenticated media delivery and reachability GC. Harden the script interpreter, pin unrpyc and remove tracked build metadata. Include current compatibility checks, Naninovel import and regression tests.
This commit is contained in:
mrleo1nid committed 2026-10-25 04:42:48 +03:00
1 parent 06ff19ac4a
commit c8d83b3fe7
65 files changed
+3825 -68

No files matched your search

+46
View File
@@ -0,0 +1,46 @@
import asyncio
import json
import os
import subprocess
import sys
from uuid import uuid4
import pytest
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
from app.db import engine as configured_engine
@pytest.mark.skipif(not os.getenv("TEST_POSTGRES"), reason="Set TEST_POSTGRES=1 for isolated database migration test")
def test_upgrade_existing_versions_and_downgrade():
async def check():
name = "migration_test_" + uuid4().hex
admin = create_async_engine(configured_engine.url, isolation_level="AUTOCOMMIT")
test_url = configured_engine.url.set(database=name)
env = {**os.environ, "DATABASE_URL": test_url.render_as_string(hide_password=False)}
async def alembic(*args):
proc = await asyncio.to_thread(subprocess.run, [sys.executable, "-m", "alembic", *args], env=env, capture_output=True, text=True)
assert proc.returncode == 0, proc.stderr
async with admin.connect() as conn:
await conn.execute(text(f'CREATE DATABASE "{name}"'))
test_engine = create_async_engine(test_url)
try:
await alembic("upgrade", "0002")
async with test_engine.begin() as conn:
await conn.execute(text("INSERT INTO games(id,title,description,tags,engine) VALUES(1,'Old','','{}','renpy')"))
await conn.execute(text("INSERT INTO game_versions(id,game_id,name,description,status,progress,size_bytes,manifest) VALUES(1,1,'Original','','ready','',0,CAST(:m AS jsonb)),(2,1,'Legacy mod','','ready','',0,CAST(:m AS jsonb))"), {"m": json.dumps({"format": "pgvn/1", "files": {}})})
await conn.execute(text("UPDATE game_versions SET parent_id=1,kind='mod' WHERE id=2"))
await alembic("upgrade", "head")
async with test_engine.connect() as conn:
rows = (await conn.execute(text("SELECT revision_id,base_revision_id FROM game_versions"))).all()
assert len(rows) == 2 and all(r[0] and r[1] is None for r in rows)
assert await conn.scalar(text("SELECT count(*) FROM revisions")) == 2
await alembic("downgrade", "0002")
await alembic("upgrade", "head")
finally:
await test_engine.dispose()
async with admin.connect() as conn:
await conn.execute(text(f'DROP DATABASE "{name}" WITH (FORCE)'))
await admin.dispose()
asyncio.run(check())