testAppwriteMigrationUpsertSameSpecRecreateTolerates exercises the new
spec-match guard added in utopia-php/migration c8d1789. Source drops +
recreates a column with the EXACT same spec as before; createdAt
advances but specs match → action is forced to Tolerate. Asserts dest
column's $createdAt stays at first-migration value (proving Tolerate,
not DropAndRecreate). Row pass under Upsert still propagates source's
new row value.
Companion to testAppwriteMigrationUpsertAttributeRecreateDropsAndRecreates
which exercises the spec-DIFFERS path: same precondition (drop + recreate),
different outcome (DropAndRecreate vs Tolerate) gated on spec equality.
composer.lock: utopia-php/migration 24fd23b -> c8d1789 (spec-match guard).
Two coverage gaps closed:
- testAppwriteMigrationUpsertOneWayRelationshipDropAndRecreate exercises
the path that updateRelationshipInPlace gates off: one-way + onDelete
change → returns false → falls through to DropAndRecreate via
deleteRelationship. Coverage was lost when
testAppwriteMigrationUpsertUpdatesRelationshipOnDeleteInPlace was
converted to two-way to actually hit the in-place path.
- testAppwriteMigrationUpsertAttributeRecreateDropsAndRecreates pins
the createdAt-different leaf path: source drops + recreates the
attribute (createdAt advances), re-migration must DropAndRecreate
on dest and re-flow the row data through the row pass. Companion to
testAppwriteMigrationUpsertUpdatesAttributeInPlace which covers the
same-createdAt + newer-updatedAt path.
Migration package already at 09c1b21 (the maintainability commit) from
the previous lock bump — no further composer.lock change needed.
testAppwriteMigrationUpsertTwoWayRecreateSkipsPartnerSide exercises
the DropAndRecreate path on a two-way relationship that the partner-
side pair-key dedup guards. Source recreates the relationship between
runs, forcing parent-side createdAt diff. Test asserts the migration
completes cleanly and partner-table rows survive — without dedup, the
partner pass re-fires DropAndRecreate and destroys those rows.
composer.lock: utopia-php/migration c76de9a -> c13e77d (partner-side
pair-key dedup restored).
The previous version of this test created a one-way relationship,
which falls through to DropAndRecreate (one-way + onDelete change is
gated off in updateRelationshipInPlace because utopia's
updateRelationship partner-cascade throws on one-way). It never
exercised the in-place path it was named for.
Converted to two-way (parents.kids ↔ children.parent), and asserted
both parent- and partner-side onDelete on dest. Partner-side
assertion is the regression guard for the partner-meta refresh that
was missing from updateRelationshipInPlace.
composer.lock: utopia-php/migration a36d95f -> c76de9a (partner-side
onDelete sync fix).
Three new e2e tests in MigrationsBase covering the schema reconciliation
paths added in utopia-php/migration:
- testAppwriteMigrationUpsertUpdatesAttributeInPlace: PATCH source
required/default (SDK-reachable), assert dest reflects change and the
pre-existing row's column data is preserved (drop+recreate would have
wiped it).
- testAppwriteMigrationSkipPreservesAttributeDrift: leaf-level analog
of the existing container-drift Skip test — guards Skip from ever
consulting timestamps.
- testAppwriteMigrationUpsertUpdatesRelationshipOnDeleteInPlace: PATCH
source onDelete cascade->restrict (SDK-reachable), assert dest
reflects change and structural fields (relationType, twoWay) untouched.
composer.lock: utopia-php/migration 6e6f825 -> a36d95f (mechanical
helpers replacement, parseTimestamp dedup, match dispatch, comment trim).
testAppwriteMigrationUpsertDropsOrphanColumn: adds a column directly on
destination (simulating post-rename orphan or dest-only drift), runs
Upsert, asserts the orphan is dropped and source-declared column
survives. Covers the per-table orphan cleanup fired inside
createRecord before rows land.
testAppwriteMigrationSkipKeepsOrphanColumn: same setup, Skip mode.
Asserts the orphan survives, proving the cleanup is correctly gated
to Upsert only.
Two new E2E tests exercising the schema-tolerance UpdateInPlace path
added in utopia-php/migration's DestinationAppwrite.
testAppwriteMigrationUpsertUpdatesContainerMetadata (positive):
- Fresh migration copies source database + table + column + row to dest.
- Mutates source database name (PUT /databases/:id) and table
name/permissions/rowSecurity/enabled (PUT /tablesdb/:db/tables/:id).
- One-second sleep before mutation ensures source's $updatedAt is
strictly greater than dest's at second granularity (strtotime
comparison).
- Upsert re-migration asserts:
- 'completed' status.
- dest database name matches source's new name.
- dest table name / enabled / rowSecurity match source's new values.
- child row's 'name' attribute is untouched — UpdateInPlace only
rewrites container metadata, not rows.
testAppwriteMigrationSkipPreservesContainerDrift (negative):
- Fresh migration, then mutate BOTH dest (simulating ops tightening
permissions post-migration) and source (divergence).
- Skip re-migration asserts dest kept its tightened values — Skip's
strict "don't touch" contract protects dev→prod cutover workflows
from accidentally wiping ops-side drift on schema re-sync.
Both tests use performMigrationSync for strict 'completed' assertions.
Runtime ~18s combined. Existing testAppwriteMigrationRowsOnDuplicate
and testAppwriteMigrationReRunIsIdempotent regression-tested locally.
utopia-php/migration's DestinationAppwrite now handles schema tolerance on
re-migration (PR #171 on feat/skip-duplicates): it pre-checks destination
`_metadata` for each database / table / column / index and tolerates in
Skip/Upsert mode. Re-runs no longer produce schema-level errors, so the
E2E tests can drop the status-tolerant workaround and assert strict
'completed' outcomes.
Changes:
- composer.json: pin utopia-php/migration to dev-feat/skip-duplicates (aliased
to 1.9.99 for stability resolution). Will be replaced with a fixed 1.10.0
tag once the migration PR lands.
- testAppwriteMigrationRowsOnDuplicate: replace the tolerant
runMigrationAssertingRowSuccess helper with performMigrationSync on the
Skip and Upsert re-runs. Asserts 'completed' status on every run,
destination row content matches the expected value per mode (Mutated
preserved on Skip, Original restored on Upsert). Helper method removed.
- testAppwriteMigrationReRunIsIdempotent (new): seeds two rows on source,
runs the migration three times back-to-back (fresh, Skip re-run, Upsert
re-run) against unchanged source data, asserts strict 'completed' on every
run and row content is stable across all three. Exercises the
schema-tolerance path end-to-end: every database/table/column on
destination already exists with a matching spec, so DestinationAppwrite's
pre-check returns Tolerate for every resource.