test(store): migration harness, restore path and a 5k-task check

Phase 6 of docs/OWN-STORE.md. MigrationTestHelper is wired against the
committed v1 schema, so the first real migration only has to add its own
case; the class KDoc says where it goes. app/schemas/ is added to the
androidTest assets — the schema location comes from the KSP arg, not the
Room Gradle plugin, so nothing wired the test assets automatically.

The restore tests state the WAL premise directly rather than around it: a
backup of the .db alone must lose whatever is still in the -wal, carrying
the sidecars must keep it, and checkpointing first must make the .db
alone sufficient. If the premise is wrong the first test fails instead of
passing vacuously.

Performance: 5,000 tasks and 20 FREQ=DAILY series — daily on purpose, so
the per-series occurrence cap is the case being measured — through one
full smart-list read. The ceiling is loose and the numbers are printed,
because nobody has run this on hardware yet.

Also fixes a lint error I introduced in the backup rules two commits ago.
Naming any <include> makes everything else excluded by default, so the
<exclude> for tasks.db.imported sat under no included path and
FullBackupContent rejected it — lintDebug has been failing at HEAD since,
and CI runs it.

The same defect had a second, quieter half: those explicit includes had
silently stopped DataStore being backed up at all, since it was only ever
covered by the old file's "everything by default". Settings are listed
back in explicitly.
This commit is contained in:
2026-08-13 16:43:13 +02:00
parent 1ed192f150
commit 5abcbfc956
6 changed files with 353 additions and 12 deletions

View File

@@ -5,17 +5,18 @@
files without checkpointing, so the `-wal` sidecar can hold writes the
`.db` alone does not — all three go in together, and the app checkpoints
on ON_STOP so a restore is consistent either way.
Naming any <include> makes everything else excluded by default, so the
archived dmfs database (`tasks.db.imported`, kept one release as the
import's rollback path) is already left out. An explicit <exclude> for it
would be redundant *and* rejected — lint's FullBackupContent check errors
on an exclude that sits under no included path.
Settings live in DataStore, which this exclusion now also covers, so its
sharedpref file is listed back in.
-->
<include domain="database" path="agendula-tasks.db" />
<include domain="database" path="agendula-tasks.db-wal" />
<include domain="database" path="agendula-tasks.db-shm" />
<!--
The archived dmfs database. Kept on the device for one release as the
import's rollback path, but it is a copy of data that has already been
imported, so backing it up would double every task in the archive.
-->
<exclude domain="database" path="tasks.db.imported" />
<!-- Settings live in DataStore, which is backed up by default. -->
<include domain="file" path="datastore/" />
</full-backup-content>

View File

@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<!--
See backup_rules.xml: the WAL sidecars travel with the database, and
naming any <include> makes everything else excluded by default — which is
what keeps the archived `tasks.db.imported` out without an <exclude> that
lint would reject.
-->
<cloud-backup>
<!-- See backup_rules.xml: the WAL sidecars travel with the database. -->
<include domain="database" path="agendula-tasks.db" />
<include domain="database" path="agendula-tasks.db-wal" />
<include domain="database" path="agendula-tasks.db-shm" />
<exclude domain="database" path="tasks.db.imported" />
<include domain="file" path="datastore/" />
</cloud-backup>
<device-transfer>
<include domain="database" path="agendula-tasks.db" />
<include domain="database" path="agendula-tasks.db-wal" />
<include domain="database" path="agendula-tasks.db-shm" />
<exclude domain="database" path="tasks.db.imported" />
<include domain="file" path="datastore/" />
</device-transfer>
</data-extraction-rules>