perf(species): seed the bundled catalog only once per version

Skip the ~1.5 MB catalog parse and the table scan on every launch — the
main startup cost. `seedBundledIfNeeded` re-seeds only when the recorded
version differs from `speciesCatalogVersion`, writing the version after
the seed commits so an interrupted seed retries next launch.

Adds `parseSpeciesCatalogVersion` and a test keeping the constant in sync
with the asset's `version` field.
This commit is contained in:
vjrj 2026-07-10 15:46:04 +02:00
parent c9c764e624
commit ea5ab0342d
4 changed files with 87 additions and 0 deletions

View file

@ -17,6 +17,16 @@ void main() {
expect(json['version'], 3);
});
test('speciesCatalogVersion mirrors the asset version', () {
// The startup seed guard skips the reseed by comparing this constant to the
// version it last recorded; it must track the asset or a bumped catalog is
// never re-seeded.
expect(
speciesCatalogVersion,
parseSpeciesCatalogVersion(file.readAsStringSync()),
);
});
test('is a broad catalog, not the old starter set', () {
// The expanded catalog holds ~1200 species (was 14 by hand). The floor
// leaves headroom for Wikidata drift across regenerations.

View file

@ -237,4 +237,50 @@ void main() {
// No species named no suggestion.
expect(await repo.classifyLabel('Girasol gigante'), isNull);
});
group('seedBundledIfNeeded', () {
test('seeds on first run and skips the reload once recorded', () async {
String? stored;
var loads = 0;
Future<List<SpeciesSeed>> load() async {
loads++;
return _seeds;
}
await repo.seedBundledIfNeeded(
version: 3,
readVersion: () async => stored,
writeVersion: (v) async => stored = v,
loadSeeds: load,
);
expect(loads, 1);
expect(stored, '3');
expect(await repo.search('Tomato'), isNotEmpty);
// Same version already recorded the ~1.5 MB parse is never loaded again.
await repo.seedBundledIfNeeded(
version: 3,
readVersion: () async => stored,
writeVersion: (v) async => stored = v,
loadSeeds: load,
);
expect(loads, 1);
});
test('reseeds when the catalog version changes', () async {
var stored = '3';
var loads = 0;
await repo.seedBundledIfNeeded(
version: 4,
readVersion: () async => stored,
writeVersion: (v) async => stored = v,
loadSeeds: () async {
loads++;
return _seeds;
},
);
expect(loads, 1);
expect(stored, '4');
});
});
}