From 5018896396c20dbf1504d83676d230c3781a2aca Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Wed, 26 Nov 2025 15:54:12 -0500 Subject: [PATCH 01/16] Fix scan_rom tests --- .../cassettes/test_fastapi/test_scan_rom.yaml | 966 ++++++++++++++++++ backend/tests/handler/test_fastapi.py | 25 +- 2 files changed, 984 insertions(+), 7 deletions(-) diff --git a/backend/tests/handler/cassettes/test_fastapi/test_scan_rom.yaml b/backend/tests/handler/cassettes/test_fastapi/test_scan_rom.yaml index 4c95f9514..f218541ee 100644 --- a/backend/tests/handler/cassettes/test_fastapi/test_scan_rom.yaml +++ b/backend/tests/handler/cassettes/test_fastapi/test_scan_rom.yaml @@ -263,4 +263,970 @@ interactions: status: code: 200 message: OK + - request: + body: '{"mD5":"7de64234ee20788b9d74d2fdb3462aed","shA1":"77693a00418a9d8971b7a005f2001d997e359bff","crc":"d56d1c89"}' + headers: + accept: + - "*/*" + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - "109" + content-type: + - application/json-patch+json + host: + - hasheous.org + user-agent: + - RomM/development + x-client-api-key: + - JNoFBA-jEh4HbxuxEHM6MVzydKoAXs9eCcp2dvcg5LRCnpp312voiWmjuaIssSzS + method: POST + uri: https://hasheous.org/api/v1/Lookup/ByHash?returnAllSources=true&returnFields=Signatures%2C+Metadata%2C+Attributes + response: + body: + string: !!binary | + H4sIAAAAAAAA/+1Y3W7iOBi971NEuWolKLHzn7sW2gpp6I4GpnuxmgsTG7CaxFHiwLKjefe1XRpI + 4jLbbkdadZGqknw/9ufj4xPb388Mw6TYjAwn8GFPvmUoJeLd/IxyUhgTVFBmKkdJlxniVUFK4f4u + LMJ2z8YZL5i0/KEsxs6jvMunpvYWYYtZJTLoUyM/eoeuBGXLCi11rnJbcpKOZaGu7zdceTVPaLki + hfJaDV9KOMKIoymriliW4jRbjVkhrRA2zAoP0wlcKwBmwxMjTpas2Er/nRhc2XS/BF0dgEkZFzTn + lGWtOOP8pipYTi7EQ9a7LXoj0rspL5rpW4IKmWfqAei6MEmZtN4zPpKPGkyVm2acZJgZfaN+9Bzj + /HrLyXSD8pzgC13ug6gcZbzb75piwrrmp7nfvjzzXU/M8m1BlytNJwVLFe++NayLBC01BBLBQ9m7 + pEhtPwiRAW2q1vRwNPQArg8D2/4pAYzzr9Ori8u157QQpH8pQoLQsS2nSdu4iGUr2PUwiIOwmZhi + Vzp9TDwH2g4h0PKDYB5i38Fwgee240FEcKu3FQIqy/dCG1mWAwIU4iD0wdwXr+4CWhbAYegT2w3n + i0UnG7pedwZKLvRAgm2uSUEXtN3rG+YbkzVJWJ6SjE/r1putIi70Y15xop/m2TZXc/A1e8zYJjN1 + /gnBFHVbTqV5JDSDJt2mlfMTmpNEA8SzNNZCs1fGPdvOWqz71UoJLBu8u1QCzVoInECQ7mWp/P+o + pHH+GRWCu/1hwjJyEs0WUWzPhkFgnUTzJJqvFU31++1sx79aruqN5zMJd0zLJZn6aUNcTJqmFUfz + hIyf2Gg7eyoezMtEreW9J0U8Xk0IXzGVdlVxJkw0PsitRzC+G13v7QnNHqV1xXleRoPBZrO5pEs8 + v4xZOpCaXw60hSao5FMhY/FKZkMLOn3L7UN3Bu0IwMg+KDsjf3ZCPX3ohmYZzZYPjJN6WddOzjhK + 9K6DvM9EjPNAEHaz3QL/RcR1aAvV/Qng92wiDTq4Zyui9uGjeRf043B6fQvMLCtSf0fhBEAf+gHh + /ELE6ruKV1QIilST8pWoSqjcGbAjx48ceARVV0CqD/2AqN7J7/01S1/HUQGR3YdgBtwIBBH0jqPp + 6kM/IJpTTlB6QvI9ePnb3QnHd8DxJqex/ApNudz6nhD994j+Th9prrabJzTfSTHvCopHr/4COX1o + zyw/glbkBsfx9PShvwJP8V+dX5unmtY5oL33XguYLzPCBynKKpQMbBAcbDue7sBh4LpBbatbfz4d + TVesECesQtRldqPud+fYh/FkMlF9jLEm7AtJkLzaeG5UKsc+bI2SSplVeVr6vGVgLvT2l9z/1YF1 + yhqn4rh7tbPK26CXa/vElux4ZLu8e3FBoylPHeCO4j5AOR2swYDK6soBsBzbu4G3Xjh0hq4N3Vs3 + sNxrexjcBtbw+grYo8AJ4bA1JUDcJ4S940PXwnw44rcP8x9XvV9xZz/+BpBiGQ4uGgAA + headers: + CF-RAY: + - 9a4c45a82a9d4f60-SEA + Cache-Control: + - public,max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json; charset=utf-8; x-api-version=1 + Date: + - Wed, 26 Nov 2025 20:52:53 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=AmEyXjNXlqiEp08%2FQDNEeHrla2p9mayZ95eJmTfZxOuA2%2FpDsv39zgpnSpcl2zM8ZgDwWO%2BpoByCPFNkqtYquSViDuCYOqdy6zM%3D"}]}' + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + alt-svc: + - h3=":443"; ma=86400 + api-supported-versions: + - "1.0" + cf-cache-status: + - DYNAMIC + via: + - 1.1 Caddy + x-correlation-id: + - defecc62-caa7-4fc5-b888-936938e4aee1 + status: + code: 200 + message: OK + - request: + body: "" + headers: + accept: + - "*/*" + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-type: + - application/json-patch+json + host: + - hasheous.org + user-agent: + - RomM/development + x-client-api-key: + - JNoFBA-jEh4HbxuxEHM6MVzydKoAXs9eCcp2dvcg5LRCnpp312voiWmjuaIssSzS + method: GET + uri: https://hasheous.org/api/v1/MetadataProxy/IGDB/Game?Id=3340&expandColumns=age_ratings%2C+alternative_names%2C+collections%2C+cover%2C+dlcs%2C+expanded_games%2C+franchise%2C+franchises%2C+game_modes%2C+genres%2C+involved_companies%2C+platforms%2C+ports%2C+remakes%2C+screenshots%2C+similar_games%2C+videos + response: + body: + string: !!binary | + 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 + NE9D8mqrsT/+7p//BfeBP9GQUgEA + headers: + CF-RAY: + - 9a4c45adedae4f60-SEA + Cache-Control: + - public,max-age=604800 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json; charset=utf-8; x-api-version=1 + Date: + - Wed, 26 Nov 2025 20:52:54 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=z%2BSdx55RUYQGYaVnJJGUYgM%2FPmSfCXlPqA5qv36XdOff0WJ3%2BW%2BQIUJmgp%2Feyd4PzVuLCrqKXcHiEeQZWPisgzDjLV6p9294250%3D"}]}' + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + alt-svc: + - h3=":443"; ma=86400 + api-supported-versions: + - "1.0" + cf-cache-status: + - DYNAMIC + via: + - 1.1 Caddy + x-correlation-id: + - 626fab1b-06f5-42a3-a0c9-018328ed6ad3 + status: + code: 200 + message: OK + - request: + body: '{"mD5":"7de64234ee20788b9d74d2fdb3462aed","shA1":"77693a00418a9d8971b7a005f2001d997e359bff","crc":"d56d1c89"}' + headers: + accept: + - "*/*" + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - "109" + content-type: + - application/json-patch+json + host: + - hasheous.org + user-agent: + - RomM/development + x-client-api-key: + - JNoFBA-jEh4HbxuxEHM6MVzydKoAXs9eCcp2dvcg5LRCnpp312voiWmjuaIssSzS + method: POST + uri: https://hasheous.org/api/v1/Lookup/ByHash?returnAllSources=true&returnFields=Signatures%2C+Metadata%2C+Attributes + response: + body: + string: !!binary | + H4sIAAAAAAAA/+1YXW/iOBR976+I8tRKUGLnO28ttBXS0B0NTPdhNQ8mMWA1saPEgWVH89/Xdmkg + ictsux1p1UXqR3Lv9fX18fGJ7e9nhmGSxIwMJ/BhT75RlGHxbn5GOS6MCSoIM5WjJEuKeFXgUri/ + C4uw3bMx5QWTlj+Uxdh5lHf5lGpvEbaYVaIFeUryo3foShFdVmipc5XbkuNsLAt1fb/hyqt5SsoV + LpTXavgyzFGCOJqyqohlKU4za8wKaYWwYVZ4mE7gWgEwG54YcbxkxVb678Tgyqb7JejqgASXcUFy + ThhtxRnnN1XBcnwhHmjvtuiNcO+mvGg232JUyHamHoCuK8EZk9Z7xkfyUYOpchPKMU2Y0TfqR88x + zq+3HE83KM9xcqFr+yAqR5R3+12TBLOu+Wnuty/PfNcTs3xbkOVK00nBMsW7bw3rIkVLDYFE8FD2 + LilS2w9CZECbqjU9HA09gOvDwLZ/SgDj/Ov06uJy7TktBMlfipAgdGzLadI2LmKZJXG9BMRB2GyY + Ja50+gn2HGg7GEPLD4J5mPhOAhfJ3HY8iHDS6m2FgGrle6GNLMsBAQqTIPTB3Bev7gJaFkjC0Me2 + G84Xi05r6HrdGSi50AMJtrnGBVmQdq9vmO8Er3HK8gxTPq2zN7MiLvRjXnGsn+bZNldz8JU+Urah + ps4/wQlB3cyZNI+EZpC0m1o5P6E5TjVAPEtjLTR7Zdyz7azFul+tlMCywbtLJdCshcAJBOlelsr/ + j0oa559RIbjbH6aM4pNotohiezYMAuskmifRfK1oqv/fznb8q+Wq3ng+k3DHtFySqZ81xMUkWVZx + NE/x+ImNtrOn4sG8TNRa3nsyxOPVBPMVU82uKs6EicQHbesRjO9G13t7SuijtK44z8toMNhsNpdk + mcwvY5YNxG+OqND3gbbYFJV8KqQsXskM0IJO33L70J1BOwIwsg9Kp/jPTqinD90QSgldPjCO66Vd + OznjKNW7Dtp9xmKsB6Kwm/HWBLyIug5xobw/Af2eTaRBB/lshdVefDTvAn8cTq9vgZllRernKJwA + 6EM/IJxfsFiBV/GKCFGRilK+ElUJlTsDduT4kQOPoOoKSPWhHxDVO/nNv2bZ6zgqILL7EMyAG4Eg + gt5xNF196AdEc8oxyk5Ivgcvf7s74fgOON7kJJZfoSmX298Tov8e0d/JI8nVlvOE5jsp5l1BktGr + v0BOH9ozy4+gFbnBcTw9feivwFP8VWfY5smmdRZo77/XAuZLivkgQ7RC6cAGwcG24+keHAauG9S2 + OvvzCWm6YoU4ZRWiLrMbdb87yz6MJ5OJ6mOcaMK+4BTJ643npFI59mFrlFbKrMrT0uctA3Oht7/o + /q8OrFPWOBNH3qudVd4IvVzbJ7ZkxyPb5d2LSxpNeeoQdxT3AcrJYA0GRFZXDoDl2N4NvPXCoTN0 + bejeuoHlXtvD4DawhtdXwB4FTgiHrSkB4k4h7B0fuhbmwxG/fZj/uOr9ijv78TfXAoVbMhoAAA== + headers: + CF-RAY: + - 9a4c45d4ae25d469-SEA + Cache-Control: + - public,max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json; charset=utf-8; x-api-version=1 + Date: + - Wed, 26 Nov 2025 20:53:00 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=ybytLODqS7H3HT2uAtDLP1os12VWdlxe6t%2Fgd%2Bw1YaC070sB60CJ39TfhOupJncwPf0pMioyLgnTCs%2FcbF%2BF3AQUZOPQX7hY1Ds%3D"}]}' + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + alt-svc: + - h3=":443"; ma=86400 + api-supported-versions: + - "1.0" + cf-cache-status: + - DYNAMIC + via: + - 1.1 Caddy + x-correlation-id: + - fc44b4f9-611e-4d44-aa5e-0b793fc4c5ab + status: + code: 200 + message: OK + - request: + body: "" + headers: + accept: + - "*/*" + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-type: + - application/json-patch+json + host: + - hasheous.org + user-agent: + - RomM/development + x-client-api-key: + - JNoFBA-jEh4HbxuxEHM6MVzydKoAXs9eCcp2dvcg5LRCnpp312voiWmjuaIssSzS + method: GET + uri: https://hasheous.org/api/v1/MetadataProxy/IGDB/Game?Id=3340&expandColumns=age_ratings%2C+alternative_names%2C+collections%2C+cover%2C+dlcs%2C+expanded_games%2C+franchise%2C+franchises%2C+game_modes%2C+genres%2C+involved_companies%2C+platforms%2C+ports%2C+remakes%2C+screenshots%2C+similar_games%2C+videos + response: + body: + string: !!binary | + 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 + headers: + Age: + - "6" + CF-RAY: + - 9a4c45d67f8ed469-SEA + Cache-Control: + - public,max-age=604800 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 26 Nov 2025 20:53:00 GMT + Nel: + - '{"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}' + Report-To: + - '{"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=87x8jDKQRGXZXBcJ8XojaRzOW%2B7PcM7rREtPuTI1DIDOL7wBGPTwDAcgPa4HBhAkxxQNHo6SbvMzDixBvndz%2FPGZ%2Fz8qgImPJfs%3D"}]}' + Server: + - cloudflare + Strict-Transport-Security: + - max-age=15552000; includeSubDomains; preload + Transfer-Encoding: + - chunked + X-Content-Type-Options: + - nosniff + alt-svc: + - h3=":443"; ma=86400 + cf-cache-status: + - DYNAMIC + via: + - 1.1 Caddy + x-correlation-id: + - 626fab1b-06f5-42a3-a0c9-018328ed6ad3 + status: + code: 200 + message: OK version: 1 diff --git a/backend/tests/handler/test_fastapi.py b/backend/tests/handler/test_fastapi.py index dca103938..b7964d3e0 100644 --- a/backend/tests/handler/test_fastapi.py +++ b/backend/tests/handler/test_fastapi.py @@ -17,6 +17,7 @@ async def test_scan_platform(): assert platform.slug == "n64" assert platform.name == "Nintendo 64" assert platform.igdb_id == 4 + assert platform.hasheous_id == 64 async with initialize_context(): platform = await scan_platform("", []) @@ -25,14 +26,18 @@ async def test_scan_platform(): assert platform.slug == "" assert platform.name == "" assert platform.igdb_id is None + assert platform.hasheous_id is None @pytest.mark.vcr async def test_scan_rom(): - platform = Platform(id=1, slug="n64", fs_slug="n64", name="Nintendo 64", igdb_id=4) + platform = Platform( + id=1, slug="n64", fs_slug="n64", name="Nintendo 64", igdb_id=4, hasheous_id=64 + ) platform = db_platform_handler.add_platform(platform) rom = Rom( + platform_id=platform.id, fs_name="Paper Mario (USA).z64", fs_name_no_tags="Paper Mario", fs_name_no_ext="Paper Mario", @@ -40,6 +45,7 @@ async def test_scan_rom(): fs_path="n64/Paper Mario (USA)", name="Paper Mario", igdb_id=3340, + hasheous_id=4872, fs_size_bytes=1024, tags=[], ) @@ -55,18 +61,22 @@ async def test_scan_rom(): "nested": False, "files": [ RomFile( + rom=rom, file_name="Paper Mario (USA).z64", file_path="n64/Paper Mario (USA)", - file_size_bytes=1024, + file_size_bytes=23175094, last_modified=1620000000, + crc_hash="d56d1c89", + md5_hash="7de64234ee20788b9d74d2fdb3462aed", + sha1_hash="77693a00418a9d8971b7a005f2001d997e359bff", ) ], - "crc_hash": "", - "md5_hash": "", - "sha1_hash": "", + "crc_hash": "d56d1c89", + "md5_hash": "7de64234ee20788b9d74d2fdb3462aed", + "sha1_hash": "77693a00418a9d8971b7a005f2001d997e359bff", "ra_hash": "", }, - metadata_sources=[MetadataSource.IGDB], + metadata_sources=[MetadataSource.HASHEOUS], newly_added=True, ) @@ -75,5 +85,6 @@ async def test_scan_rom(): assert rom.name == "Paper Mario" assert rom.fs_path == "n64/Paper Mario (USA)" assert rom.igdb_id == 3340 - assert rom.fs_size_bytes == 1024 + assert rom.hasheous_id == 4872 + assert rom.fs_size_bytes == 23175094 assert rom.tags == [] From d1903306dc826cb4c0125582737aac45b89c03ae Mon Sep 17 00:00:00 2001 From: Tux00-repo Date: Sat, 29 Nov 2025 20:34:07 +0100 Subject: [PATCH 02/16] Update 0057_multi_notes.py This should be it. the other DB's can be handled by Alembic. don't know about SQLite or MSSQL. but something tells me that we never get to know. tested PostgreSQL and it had no problems. --- backend/alembic/versions/0057_multi_notes.py | 32 ++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/backend/alembic/versions/0057_multi_notes.py b/backend/alembic/versions/0057_multi_notes.py index 8a68daf35..2005b2123 100644 --- a/backend/alembic/versions/0057_multi_notes.py +++ b/backend/alembic/versions/0057_multi_notes.py @@ -53,14 +53,18 @@ def upgrade() -> None: # Create indexes for performance op.create_index("idx_rom_notes_public", "rom_notes", ["is_public"]) op.create_index("idx_rom_notes_rom_user", "rom_notes", ["rom_id", "user_id"]) + op.create_index("idx_rom_notes_title", "rom_notes", ["title"]) # Get connection for manual index creation connection = op.get_bind() + # For MariaDB compatibility, we limit the content index length - connection.execute(text("CREATE INDEX idx_rom_notes_title ON rom_notes (title)")) - connection.execute( - text("CREATE INDEX idx_rom_notes_content ON rom_notes (content(100))") - ) + if connection.dialect.name in ("mysql", "mariadb"): + connection.execute( + text("CREATE INDEX idx_rom_notes_content ON rom_notes (content(100))") + ) + else: + op.create_index("idx_rom_notes_content", "rom_notes", ["content"]) # Add default values to old note columns to prevent insertion errors # This allows new rom_user records to be created without specifying note fields @@ -117,7 +121,9 @@ def upgrade() -> None: def downgrade() -> None: """Drop the rom_notes table and restore note columns to rom_user.""" - + + connection = op.get_bind() + # Add back the old columns to rom_user op.add_column( "rom_user", @@ -131,7 +137,7 @@ def downgrade() -> None: ) # Migrate notes back to rom_user (take first note per user/rom) - connection = op.get_bind() + result = connection.execute( text( """ @@ -161,9 +167,17 @@ def downgrade() -> None: ) # Drop indexes and table - connection = op.get_bind() - connection.execute(text("DROP INDEX IF EXISTS idx_rom_notes_content ON rom_notes")) - connection.execute(text("DROP INDEX IF EXISTS idx_rom_notes_title ON rom_notes")) + if connection.dialect.name in ("mysql", "mariadb"): + # In case Mysql Can't be handled by Alembic + connection.execute(text("DROP INDEX IF EXISTS idx_rom_notes_content ON rom_notes")) + connection.execute(text("DROP INDEX IF EXISTS idx_rom_notes_title ON rom_notes")) + else: + # The other DB's schould be able to use Alembic's drop_index + op.drop_index("idx_rom_notes_content", table_name="rom_notes") + op.drop_index("idx_rom_notes_title", table_name="rom_notes") + op.drop_index("idx_rom_notes_rom_user", table_name="rom_notes") op.drop_index("idx_rom_notes_public", table_name="rom_notes") + + # And its gone op.drop_table("rom_notes") From 2c85efdf68de447a2eda1488701aba1c69df9e97 Mon Sep 17 00:00:00 2001 From: Tux00-repo Date: Sun, 30 Nov 2025 00:00:59 +0100 Subject: [PATCH 03/16] Create test_rom_note_migration.py test for 0057 --- .../database/test_rom_note_migration.py | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 backend/tests/handler/database/test_rom_note_migration.py diff --git a/backend/tests/handler/database/test_rom_note_migration.py b/backend/tests/handler/database/test_rom_note_migration.py new file mode 100644 index 000000000..d21023dd4 --- /dev/null +++ b/backend/tests/handler/database/test_rom_note_migration.py @@ -0,0 +1,215 @@ +""" +Unit tests for rom_notes table migration. +Test file: tests/handler/database/test_rom_note_migration.py +""" + +import pytest +from alembic import command +from alembic.config import Config +from sqlalchemy import create_engine, text, inspect +from sqlalchemy.pool import NullPool +import os + + +@pytest.fixture(params=["postgresql", "mysql"]) +def db_engine(request): + """Create test database engines for different dialects.""" + dialect = request.param + + if dialect == "postgresql": + database_url = os.getenv( + "TEST_POSTGRES_URL", + "postgresql://romm:romm@localhost:5432/romm" + ) + else: # mysql + database_url = os.getenv( + "TEST_MYSQL_URL", + "mysql+pymysql://romm:romm@127.0.0.1/romm" + ) + + engine = create_engine(database_url, poolclass=NullPool) + yield engine, dialect + engine.dispose() + + +@pytest.fixture +def alembic_config(db_engine): + """Create Alembic configuration for testing.""" + engine, dialect = db_engine + config = Config("alembic.ini") + config.set_main_option("sqlalchemy.url", str(engine.url)) + return config, engine, dialect + + +def get_migration_revisions(config): + """Get the rom_notes migration revision IDs.""" + # You'll need to update these to match your actual revision IDs + # You can find them with: alembic history + ROM_NOTES_REVISION = "0057_multi_notes" # Your upgrade revision + PREVIOUS_REVISION = "0056_gamelist_xml" # The revision before rom_notes + return ROM_NOTES_REVISION, PREVIOUS_REVISION + + +def test_upgrade_creates_table_and_indexes(alembic_config): + """Test that upgrade creates rom_notes table with all indexes.""" + config, engine, dialect = alembic_config + ROM_NOTES_REV, PREV_REV = get_migration_revisions(config) + + # Start from previous revision + command.downgrade(config, PREV_REV) + + # Verify table doesn't exist yet + inspector = inspect(engine) + assert "rom_notes" not in inspector.get_table_names() + + # Upgrade to rom_notes revision + command.upgrade(config, ROM_NOTES_REV) + + # Verify table exists + inspector = inspect(engine) + assert "rom_notes" in inspector.get_table_names() + + # Verify columns + columns = {col["name"] for col in inspector.get_columns("rom_notes")} + expected = {"id", "rom_id", "user_id", "title", "content", "is_public", "created_at", "updated_at"} + assert expected.issubset(columns), f"Missing columns: {expected - columns}" + + # Verify indexes + indexes = {idx["name"] for idx in inspector.get_indexes("rom_notes")} + expected_indexes = { + "idx_rom_notes_public", + "idx_rom_notes_rom_user", + "idx_rom_notes_title", + "idx_rom_notes_content" + } + assert expected_indexes.issubset(indexes), f"Missing indexes: {expected_indexes - indexes}" + + +def test_downgrade_removes_table(alembic_config): + """Test that downgrade removes rom_notes table.""" + config, engine, dialect = alembic_config + ROM_NOTES_REV, PREV_REV = get_migration_revisions(config) + + # Ensure we're at rom_notes revision + command.upgrade(config, ROM_NOTES_REV) + + # Verify table exists + inspector = inspect(engine) + assert "rom_notes" in inspector.get_table_names() + + # Downgrade to previous revision + command.downgrade(config, PREV_REV) + + # Verify table is removed + inspector = inspect(engine) + assert "rom_notes" not in inspector.get_table_names() + + # Verify old columns are restored to rom_user + columns = {col["name"] for col in inspector.get_columns("rom_user")} + assert "note_raw_markdown" in columns + assert "note_is_public" in columns + + +def test_upgrade_removes_old_columns_from_rom_user(alembic_config): + """Test that upgrade removes old note columns from rom_user.""" + config, engine, dialect = alembic_config + ROM_NOTES_REV, PREV_REV = get_migration_revisions(config) + + # Start from previous revision (where old columns exist) + command.downgrade(config, PREV_REV) + + inspector = inspect(engine) + columns_before = {col["name"] for col in inspector.get_columns("rom_user")} + + # Old columns should exist + assert "note_raw_markdown" in columns_before + assert "note_is_public" in columns_before + + # Upgrade + command.upgrade(config, ROM_NOTES_REV) + + inspector = inspect(engine) + columns_after = {col["name"] for col in inspector.get_columns("rom_user")} + + # Old columns should be removed (if your upgrade does this) + # Comment out these assertions if your upgrade keeps the old columns + # assert "note_raw_markdown" not in columns_after + # assert "note_is_public" not in columns_after + + +def test_indexes_are_dialect_specific(alembic_config): + """Test that content index works with dialect-specific syntax.""" + config, engine, dialect = alembic_config + ROM_NOTES_REV, PREV_REV = get_migration_revisions(config) + + # Upgrade to rom_notes revision + command.upgrade(config, ROM_NOTES_REV) + + inspector = inspect(engine) + indexes = {idx["name"] for idx in inspector.get_indexes("rom_notes")} + + # Verify content index exists + assert "idx_rom_notes_content" in indexes + + # Test that we can query using content column + with engine.connect() as conn: + # This should work regardless of dialect + result = conn.execute( + text("SELECT COUNT(*) as cnt FROM rom_notes WHERE content = :content"), + {"content": "test"} + ) + count = result.scalar() + assert count is not None # Should be 0, but no error + + +def test_foreign_keys_and_constraints(alembic_config): + """Test that foreign keys and constraints are properly created.""" + config, engine, dialect = alembic_config + ROM_NOTES_REV, PREV_REV = get_migration_revisions(config) + + command.upgrade(config, ROM_NOTES_REV) + + inspector = inspect(engine) + + # Check foreign keys + foreign_keys = inspector.get_foreign_keys("rom_notes") + assert len(foreign_keys) >= 2 + + fk_columns = {fk["constrained_columns"][0] for fk in foreign_keys} + assert "rom_id" in fk_columns or "user_id" in fk_columns + + # Check primary key + pk = inspector.get_pk_constraint("rom_notes") + assert "id" in pk["constrained_columns"] + + # Check NOT NULL constraints + columns = {col["name"]: col for col in inspector.get_columns("rom_notes")} + assert columns["rom_id"]["nullable"] is False + assert columns["user_id"]["nullable"] is False + assert columns["content"]["nullable"] is False + + +def test_full_migration_cycle(alembic_config): + """Test complete upgrade -> downgrade -> upgrade cycle.""" + config, engine, dialect = alembic_config + ROM_NOTES_REV, PREV_REV = get_migration_revisions(config) + + # Start from previous + command.downgrade(config, PREV_REV) + inspector = inspect(engine) + assert "rom_notes" not in inspector.get_table_names() + + # Upgrade + command.upgrade(config, ROM_NOTES_REV) + inspector = inspect(engine) + assert "rom_notes" in inspector.get_table_names() + + # Downgrade + command.downgrade(config, PREV_REV) + inspector = inspect(engine) + assert "rom_notes" not in inspector.get_table_names() + + # Upgrade again + command.upgrade(config, ROM_NOTES_REV) + inspector = inspect(engine) + assert "rom_notes" in inspector.get_table_names() From 12b34bfada67bfea270ce1c203d7797542a0f1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tanguy=20Maz=C3=A9?= Date: Sun, 30 Nov 2025 00:58:58 +0100 Subject: [PATCH 04/16] feat(i18n): fix French translations and add locale-aware date formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- frontend/src/components/Details/Personal.vue | 6 +-- frontend/src/components/Details/Saves.vue | 4 +- frontend/src/components/Details/States.vue | 4 +- frontend/src/components/Details/Title.vue | 4 +- .../Administration/RunningTaskItem.vue | 4 ++ .../Administration/Users/UsersTable.vue | 4 +- .../src/components/common/Game/Card/Flags.vue | 6 ++- .../common/Game/Dialog/Asset/SelectSave.vue | 4 +- .../common/Game/Dialog/Asset/SelectState.vue | 4 +- .../components/common/Game/VirtualTable.vue | 7 +++- frontend/src/console/views/Game.vue | 4 +- frontend/src/locales/cs_CZ/rom.json | 12 +++++- frontend/src/locales/de_DE/rom.json | 12 +++++- frontend/src/locales/en_GB/rom.json | 12 +++++- frontend/src/locales/en_US/rom.json | 12 +++++- frontend/src/locales/es_ES/rom.json | 12 +++++- frontend/src/locales/fr_FR/rom.json | 20 ++++++--- frontend/src/locales/fr_FR/settings.json | 2 +- frontend/src/locales/hu_HU/rom.json | 12 +++++- frontend/src/locales/it_IT/rom.json | 12 +++++- frontend/src/locales/ja_JP/rom.json | 12 +++++- frontend/src/locales/ko_KR/rom.json | 12 +++++- frontend/src/locales/pl_PL/rom.json | 12 +++++- frontend/src/locales/pt_BR/rom.json | 12 +++++- frontend/src/locales/ro_RO/rom.json | 12 +++++- frontend/src/locales/ru_RU/rom.json | 12 +++++- frontend/src/locales/zh_CN/rom.json | 12 +++++- frontend/src/locales/zh_TW/rom.json | 12 +++++- frontend/src/utils/index.ts | 41 +++++++++++++------ frontend/src/views/GameDetails.vue | 2 +- frontend/src/views/Player/EmulatorJS/Base.vue | 14 +++---- 31 files changed, 251 insertions(+), 59 deletions(-) diff --git a/frontend/src/components/Details/Personal.vue b/frontend/src/components/Details/Personal.vue index 0355c14b7..7236f47ae 100644 --- a/frontend/src/components/Details/Personal.vue +++ b/frontend/src/components/Details/Personal.vue @@ -14,7 +14,7 @@ import RSection from "@/components/common/RSection.vue"; import romApi from "@/services/api/rom"; import storeAuth from "@/stores/auth"; import type { DetailedRom } from "@/stores/roms"; -import { getTextForStatus, getEmojiForStatus } from "@/utils"; +import { getTextForStatus, getEmojiForStatus, getI18nKeyForStatus } from "@/utils"; const { t } = useI18n(); const props = defineProps<{ rom: DetailedRom }>(); @@ -301,7 +301,7 @@ watch( getEmojiForStatus(item.raw as RomUserStatus) }}{{ - getTextForStatus(item.raw as RomUserStatus) + t(getI18nKeyForStatus(item.raw as RomUserStatus) || '') }} diff --git a/frontend/src/components/Details/Saves.vue b/frontend/src/components/Details/Saves.vue index 6fc83ea49..4b421262a 100644 --- a/frontend/src/components/Details/Saves.vue +++ b/frontend/src/components/Details/Saves.vue @@ -2,6 +2,7 @@ import type { Emitter } from "mitt"; import { storeToRefs } from "pinia"; import { inject, ref } from "vue"; +import { useI18n } from "vue-i18n"; import type { SaveSchema } from "@/__generated__"; import EmptySaves from "@/components/common/EmptyStates/EmptySaves.vue"; import storeAuth from "@/stores/auth"; @@ -10,6 +11,7 @@ import type { Events } from "@/types/emitter"; import { formatBytes, formatTimestamp } from "@/utils"; import { getEmptyCoverImage } from "@/utils/covers"; +const { t, locale } = useI18n(); const auth = storeAuth(); const { scopes } = storeToRefs(auth); const props = defineProps<{ rom: DetailedRom }>(); @@ -181,7 +183,7 @@ function onCardClick(save: SaveSchema, event: MouseEvent) { - Updated: {{ formatTimestamp(save.updated_at) }} + {{ t("rom.updated") }}: {{ formatTimestamp(save.updated_at, locale) }} diff --git a/frontend/src/components/Details/States.vue b/frontend/src/components/Details/States.vue index c3640d895..0bb4b2b8f 100644 --- a/frontend/src/components/Details/States.vue +++ b/frontend/src/components/Details/States.vue @@ -2,6 +2,7 @@ import type { Emitter } from "mitt"; import { storeToRefs } from "pinia"; import { inject, ref } from "vue"; +import { useI18n } from "vue-i18n"; import type { StateSchema } from "@/__generated__"; import EmptySates from "@/components/common/EmptyStates/EmptyStates.vue"; import storeAuth from "@/stores/auth"; @@ -10,6 +11,7 @@ import type { Events } from "@/types/emitter"; import { formatBytes, formatTimestamp } from "@/utils"; import { getEmptyCoverImage } from "@/utils/covers"; +const { t, locale } = useI18n(); const auth = storeAuth(); const { scopes } = storeToRefs(auth); const props = defineProps<{ rom: DetailedRom }>(); @@ -183,7 +185,7 @@ function onCardClick(state: StateSchema, event: MouseEvent) { - Updated: {{ formatTimestamp(state.updated_at) }} + {{ t("rom.updated") }}: {{ formatTimestamp(state.updated_at, locale) }} diff --git a/frontend/src/components/Details/Title.vue b/frontend/src/components/Details/Title.vue index b4bd9b6f3..77cd9e71a 100644 --- a/frontend/src/components/Details/Title.vue +++ b/frontend/src/components/Details/Title.vue @@ -1,6 +1,7 @@