up
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
"""Migration checks on a private schema inside the disposable flow PostgreSQL."""
|
||||
import json
|
||||
import uuid
|
||||
|
||||
|
||||
def check_migration(sql, migrations):
|
||||
schema = 'team_uuid_migration_test'
|
||||
sql(f'CREATE SCHEMA {schema};')
|
||||
|
||||
def query(statement):
|
||||
return sql(f'SET search_path={schema};\n{statement}')
|
||||
|
||||
try:
|
||||
migration = migrations / '20261013120000_team_uuid.sql'
|
||||
for path in sorted(migrations.glob('*.sql')):
|
||||
if path.name >= migration.name:
|
||||
break
|
||||
query('BEGIN;\n' + path.read_text().split('-- +goose Down')[0] + '\nCOMMIT;')
|
||||
query("""
|
||||
INSERT INTO users(id, username, email, password_hash) VALUES(1, 'Test', 'test@example.test', 'unused');
|
||||
INSERT INTO scenarios(id, name, author_id) VALUES(1, 'Test', 1);
|
||||
INSERT INTO games(id, name, start_at, scenario_id) VALUES(1, 'Test', NOW(), 1);
|
||||
INSERT INTO teams(id, name, game_id, creator_id, password) VALUES(1, 'First', 1, 1, 'qr'), (2, 'Second', 1, 1, 'qr2');
|
||||
INSERT INTO actions(team_id, code) VALUES(1, 'p'), (NULL, 'unbound');
|
||||
INSERT INTO applications(team_id, name, image) VALUES(1, 'Clue', 'original.pdf'), (NULL, 'Unbound', 'unbound.pdf');
|
||||
INSERT INTO team_answers(team_id, question_code, answer, score) VALUES(1, 'q', 'Answer', 5);
|
||||
INSERT INTO game_reviews(game_id, team_id, rating, text) VALUES(1, 1, 5, 'Review');
|
||||
INSERT INTO game_map_items(game_id, kind, item_id) VALUES(1, 'team', 1), (1, 'team', 2), (1, 'screen', 3), (1, 'organizer_desk', 1);
|
||||
INSERT INTO game_exports(id, game_id, snapshot, source_server, object_key) VALUES('export', 1, '{}', 'https://example.test', 'old.zip');
|
||||
INSERT INTO game_returns(package_id, sha256, export_id, source_game_id, prepared_at, mappings, local_actors) VALUES('return', 'hash', 'export', 1, 'old', '{}', '{}');
|
||||
INSERT INTO game_imports(id, package_id, source_server, source_game_id, sha256, manifest, original_data, object_keys) VALUES('import', 'package', 'https://example.test', 1, 'hash', '{}', '{}', ARRAY['original.pdf']);
|
||||
""")
|
||||
query('BEGIN;\n' + migration.read_text().split('-- +goose Down')[0] + '\nCOMMIT;')
|
||||
result = json.loads(query("""
|
||||
SELECT json_build_object(
|
||||
'teams', (SELECT json_agg(json_build_object('id', id, 'name', name, 'password', password) ORDER BY name) FROM teams),
|
||||
'linked', (SELECT count(*) FROM teams t JOIN actions a ON a.team_id=t.id JOIN applications p ON p.team_id=t.id JOIN team_answers n ON n.team_id=t.id JOIN game_reviews r ON r.team_id=t.id WHERE t.name='First' AND a.code='p' AND p.image='original.pdf' AND n.answer='Answer' AND n.score=5 AND r.text='Review'),
|
||||
'unbound', (SELECT count(*) FROM actions WHERE team_id IS NULL) + (SELECT count(*) FROM applications WHERE team_id IS NULL),
|
||||
'map', (SELECT json_object_agg(kind || '/' || item_id, item_id) FROM game_map_items),
|
||||
'registries', (SELECT count(*) FROM game_exports) + (SELECT count(*) FROM game_imports) + (SELECT count(*) FROM game_returns),
|
||||
'sequence', to_regclass('teams_id_seq')
|
||||
);
|
||||
""").splitlines()[-1])
|
||||
assert [team['name'] for team in result['teams']] == ['First', 'Second']
|
||||
assert [team['password'] for team in result['teams']] == ['qr', 'qr2']
|
||||
ids = [team['id'] for team in result['teams']]
|
||||
assert len(set(ids)) == 2 and all(uuid.UUID(id).version == 7 for id in ids)
|
||||
assert result['linked'] == 1 and result['unbound'] == 2
|
||||
assert result['registries'] == 0 and result['sequence'] is None
|
||||
assert set(result['map']) == {*(f'team/{id}' for id in ids), 'screen/3', 'organizer_desk/1'}
|
||||
for statement in (
|
||||
f"INSERT INTO team_answers(team_id, question_code) VALUES('{ids[0]}','q');",
|
||||
f"INSERT INTO game_reviews(game_id, team_id, rating, text) VALUES(1,'{ids[0]}',5,'Duplicate');",
|
||||
"INSERT INTO actions(team_id) VALUES('019a1234-5678-7abc-8def-000000000999');",
|
||||
):
|
||||
try:
|
||||
query(statement)
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError('Migration lost a uniqueness or foreign key constraint')
|
||||
query(f"DELETE FROM teams WHERE id='{ids[0]}';")
|
||||
assert query('SELECT (SELECT count(*) FROM team_answers) + (SELECT count(*) FROM game_reviews);').splitlines()[-1] == '0'
|
||||
print('PASS: populated UUID v7 migration preserves data, nullable links, map, unique/FK/cascade constraints and clears legacy transfer registries', flush=True)
|
||||
finally:
|
||||
sql(f'DROP SCHEMA {schema} CASCADE;')
|
||||
Reference in New Issue
Block a user