autowisp.tests.test_database_migration module
Class Inheritance Diagram

Unit tests for project database migration.
These need only a throwaway SQLite database, not a full project, so they
subclass unittest.TestCase directly.
- class autowisp.tests.test_database_migration.BackendMixin[source]
Bases:
object
Supplies clean project databases on whichever backend is under test.
SQLite gets a fresh file per engine; a server has only the one database, so it is emptied before each test instead.
- static add_stray_index(engine, name)[source]
Add an index the models do not declare, to produce drift.
Drift is provoked by adding something rather than removing it: the index this branch introduces cannot be dropped on MySQL (see
create_legacy_schema()).
- static create_legacy_schema(engine)[source]
Build the pre-migration schema: everything but the new index.
Not create_all-then-drop. InnoDB refuses to drop
image_observing_sessionbecause it is the index backing image’s foreign key onobserving_session_id– MySQL indexes a foreign key column whether or not anyone asked. Leaving the index out of the metadata is both portable and a truer picture of a 1.8.1 database, which never had it.
- make_engine(name='project.db')[source]
An engine for a clean project database on the current backend.
- reset_backend()[source]
Return the backend to empty part way through a test.
The asymmetry
setUplives with, reached one level in: a test that builds several schemas in turn – one per released version, say – gets a fresh file per engine on SQLite and the same database every time on a server, where the second iteration would otherwise start from whatever the first left behind.
- class autowisp.tests.test_database_migration.TestAdditiveMigrations(methodName='runTest')[source]
Bases:
BackendMixin,TestCase
The pre-Alembic helper still adds missing nullable columns.
It is private now and has exactly one caller – the baseline path of
migrate_project()– but it is what every legacy project passes through, so it keeps its own tests.- _make_old_project()[source]
Simulate an existing project: a
pipeline_runtable from beforecode_versionand theerrortable existed, holding a row.
- class autowisp.tests.test_database_migration.TestCheckProjectSchema(methodName='runTest')[source]
Bases:
BackendMixin,TestCase
The read-only gate every project open – and every worker – runs.
- test_does_not_mutate_a_stale_database()[source]
The gate must never issue DDL: workers run it concurrently.
- class autowisp.tests.test_database_migration.TestConcurrentMigration(methodName='runTest')[source]
Bases:
BackendMixin,TestCase
Two migrators racing on one database must not corrupt or crash it.
Exercises whichever lock the backend uses:
BEGIN IMMEDIATEon SQLite,GET_LOCKon a server. Alembic supplies neither.
- class autowisp.tests.test_database_migration.TestMigrateProject(methodName='runTest')[source]
Bases:
BackendMixin,TestCase
The three database states
migrate_project()has to handle.- test_backup_is_taken_before_migrating()[source]
SQLite is copied aside; a server is the administrator’s job.
- test_crash_window_between_ddl_and_version_update()[source]
DDL applied but the stamp lost – the state MySQL can crash into.
MySQL commits DDL implicitly, so a failure between the schema change and the
alembic_versionupdate leaves exactly this. Re-running must reach head rather than failing on an index that already exists.
- test_fresh_database_is_stamped_without_running_revisions()[source]
create_allbuilds the current schema, so nothing is applied.
- test_legacy_database_is_baselined_then_upgraded()[source]
An unstamped project reaches head in one pass, no user action.
- test_older_database_reaches_the_same_state()[source]
One missing a pre-baseline column still lands at head.
- class autowisp.tests.test_database_migration.TestRevisionChain(methodName='runTest')[source]
Bases:
TestCase
The revision chain is linear, ordered, and consistently named.
A fork is what actually breaks
upgrade head: two branches each adding a revision both point at the same parent, and Alembic then refuses to upgrade because the head is ambiguous. Catching that here means catching it in CI rather than at a user’s next project open.- test_numbers_increase_along_the_chain()[source]
Numeric prefixes strictly increase from base to head.
Catches a duplicate number and a revision merged out of order.
- test_revision_ids_fit_the_version_table()[source]
Ids stay within Alembic’s
VARCHAR(32)version column.SQLite ignores a declared length, so an over-long id passes there and only fails on MySQL, mid-upgrade, with “Data too long for column ‘version_num’” – after some revisions have already been applied. Cheaper to catch here.
- test_the_build_installs_every_revision()[source]
The
meson.buildbeside the revisions lists each one of them.The build installs only the files it lists. A revision left out works from a checkout, where the file is, and is absent from an installed package, which then stops one revision short and stamps projects at the wrong head. Nothing can tell that from the installed package: a chain that ends early is still a chain. So this compares the list with the directory, where a checkout has both.
- class autowisp.tests.test_database_migration.TestSchemaDrift(methodName='runTest')[source]
Bases:
BackendMixin,TestCase
The revision chain and the ORM models describe the same schema.
A revision may not import the models – it has to keep meaning the same schema forever, while the models move – so anything a revision creates is declared twice, once in
data_modeland once in the revision. Nothing keeps the two in step except this check, which is why the plan calls for it rather than for sharing the definitions.Worth running per backend: the comparison is over reflected types, and what MySQL reports for a column is not what SQLite does.
- test_created_database_matches_the_models()[source]
Control: create_all builds from the models, so it must agree.
- test_drift_is_actually_detected()[source]
The check discriminates – an empty result means agreement.
Without this the two tests above would pass just as happily if get_schema_drift() always returned nothing.
- test_migrated_database_matches_the_models()[source]
A database the revisions built agrees with the models.
Note this cannot catch a model changed with no revision to match: the “before” state here comes from today’s metadata too, so both sides move together.
TestUpgradeFromReleaseis the test that catches that, by building the “before” state from released code.
- class autowisp.tests.test_database_migration.TestSqliteMigrationLock(methodName='runTest')[source]
Bases:
TestCase
The SQLite half of the migration lock really does exclude a second writer.
Alembic does no locking of its own, so without this two migrators can both read the same revision and both try to apply it. On SQLite the protection is
BEGIN IMMEDIATE, which pysqlite does not issue by default – it defersBEGINuntil the first write, leaving a window in which both have already read. The server equivalent isGET_LOCK, covered byTestConcurrentMigrationwhen pointed at one.- test_begin_immediate_excludes_a_second_writer()[source]
With the guard, the write lock is held from the start.
- class autowisp.tests.test_database_migration.TestTimestampTriggers(methodName='runTest')[source]
Bases:
BackendMixin,TestCase
Every table carrying
timestampkeeps a trigger maintaining it.Nothing else checks this.
get_schema_driftis alembic’s comparison of tables, columns, indexes and constraints, and a trigger is none of those – so the schema checks elsewhere in this file pass unchanged with every trigger in the database dropped.- test_creating_the_schema_installs_all_of_them()[source]
Creation covers the provenance tables, not just data_model’s.
The triggers used to be attached to each class
import_table_definitionsdiscovered, and that discovery is a glob which does not descend intodata_model/provenance– so twelve tables carried atimestampcolumn that nothing ever updated. Comparing against the metadata catches a repeat.
- autowisp.tests.test_database_migration.expected_timestamp_triggers()[source]
The triggers the models install when they create the schema.
Derived from the metadata rather than listed, so a table added later is covered without anyone remembering to extend a literal here.
- autowisp.tests.test_database_migration.on_server()[source]
Whether this run is pointed at a MySQL/MariaDB server.
The same switch the rest of the suite uses (see
autowisp.tests), so one variable turns everything onto a server rather than each part having its own idea of where to look. Setting it runs these scenarios against one too, covering the backend-specific paths – GET_LOCK, implicitly committed DDL, type comparison in the drift check – with the tests that already exist rather than a parallel copy that would drift out of step.