import 'package:flutter_test/flutter_test.dart'; import 'package:tane/db/database.dart'; import 'package:tane/db/enums.dart'; import '../support/test_support.dart'; /// A Plantare is a reproduction commitment (data-model §2.7): create it, list /// it, mark it returned/forgiven, and remove it (soft-delete). void main() { late AppDatabase db; setUp(() => db = newTestDatabase()); tearDown(() => db.close()); test('records a commitment and lists it (globally and per variety)', () async { final repo = newTestRepository(db); final vid = await repo.addQuickVariety(label: 'Tomate rosa'); final id = await repo.createPlantare( direction: PlantareDirection.iReturn, varietyId: vid, counterparty: 'Ana', owedDescription: 'un puñado la próxima temporada', ); final all = await repo.watchPlantares().first; expect(all, hasLength(1)); expect(all.single.id, id); expect(all.single.direction, PlantareDirection.iReturn); expect(all.single.counterparty, 'Ana'); expect(all.single.status, PlantareStatus.open); expect(all.single.madeOn, greaterThan(0)); final forVariety = await repo.watchPlantaresForVariety(vid).first; expect(forVariety.single.id, id); // A different variety has none. final other = await repo.addQuickVariety(label: 'Judía'); expect(await repo.watchPlantaresForVariety(other).first, isEmpty); }); test('marking it returned stamps the settle time; forgiving keeps it settled', () async { final repo = newTestRepository(db); final id = await repo.createPlantare(direction: PlantareDirection.owedToMe); await repo.setPlantareStatus(id, PlantareStatus.returned); var row = (await repo.watchPlantares().first).single; expect(row.status, PlantareStatus.returned); expect(row.settledOn, isNotNull); // Reopening clears the settle stamp. await repo.setPlantareStatus(id, PlantareStatus.open); row = (await repo.watchPlantares().first).single; expect(row.status, PlantareStatus.open); expect(row.settledOn, isNull); }); test('deleting a commitment tombstones it (drops from the list)', () async { final repo = newTestRepository(db); final id = await repo.createPlantare(direction: PlantareDirection.iReturn); expect(await repo.watchPlantares().first, hasLength(1)); await repo.deletePlantare(id); expect(await repo.watchPlantares().first, isEmpty); }); test('watchPlantareViews joins the linked variety label (null when unlinked)', () async { final repo = newTestRepository(db); final vid = await repo.addQuickVariety(label: 'Tomate rosa'); await repo.createPlantare( direction: PlantareDirection.iReturn, varietyId: vid, ); await repo.createPlantare(direction: PlantareDirection.owedToMe); final views = await repo.watchPlantareViews().first; expect(views, hasLength(2)); final linked = views.firstWhere((v) => v.plantare.varietyId == vid); expect(linked.varietyLabel, 'Tomate rosa'); final unlinked = views.firstWhere((v) => v.plantare.varietyId == null); expect(unlinked.varietyLabel, isNull); }); test('a return-by date round-trips through create', () async { final repo = newTestRepository(db); final due = DateTime(2027, 3, 1).millisecondsSinceEpoch; await repo.createPlantare( direction: PlantareDirection.iReturn, dueBy: due, ); expect((await repo.watchPlantares().first).single.dueBy, due); }); test('a bilateral pledge stores keys/signatures and looks up by pledgeId', () async { final repo = newTestRepository(db); await repo.createPlantare( direction: PlantareDirection.iReturn, counterparty: 'Ana', pledgeId: 'pledge-1', debtorKey: 'dkey', creditorKey: 'ckey', creditorSignature: 'csig', remoteState: PlantareRemoteState.proposed, returnKind: PlantareReturnKind.workHours, workHours: 3, ); final row = await repo.plantareByPledgeId('pledge-1'); expect(row, isNotNull); expect(row!.debtorKey, 'dkey'); expect(row.creditorKey, 'ckey'); expect(row.creditorSignature, 'csig'); expect(row.debtorSignature, isNull); expect(row.remoteState, PlantareRemoteState.proposed); expect(row.returnKind, PlantareReturnKind.workHours); expect(row.workHours, 3); expect(await repo.plantareByPledgeId('nope'), isNull); }); test('accepting a proposal adds the counter-stub and flips to accepted', () async { final repo = newTestRepository(db); await repo.createPlantare( direction: PlantareDirection.owedToMe, pledgeId: 'pledge-2', debtorKey: 'dkey', creditorKey: 'ckey', creditorSignature: 'csig', remoteState: PlantareRemoteState.proposed, ); await repo.applyPlantareSignatures( pledgeId: 'pledge-2', debtorSignature: 'dsig', remoteState: PlantareRemoteState.accepted, movementId: 'mov-1', ); final row = await repo.plantareByPledgeId('pledge-2'); expect(row!.debtorSignature, 'dsig'); expect(row.creditorSignature, 'csig'); // untouched expect(row.remoteState, PlantareRemoteState.accepted); expect(row.movementId, 'mov-1'); }); test('a v1 local note keeps the bilateral columns null (defaults)', () async { final repo = newTestRepository(db); final id = await repo.createPlantare(direction: PlantareDirection.iReturn); final row = (await repo.watchPlantares().first).single; expect(row.id, id); expect(row.pledgeId, isNull); expect(row.debtorKey, isNull); expect(row.remoteState, isNull); expect(row.returnKind, PlantareReturnKind.similar); // default }); test('commitments survive a backup round-trip (in exportInventory/import)', () async { final repoA = newTestRepository(db); final vid = await repoA.addQuickVariety(label: 'Maíz'); await repoA.createPlantare( direction: PlantareDirection.iReturn, varietyId: vid, counterparty: 'Colectivo semillero', owedDescription: 'una mazorca', pledgeId: 'pledge-3', debtorKey: 'dkey', creditorKey: 'ckey', debtorSignature: 'dsig', creditorSignature: 'csig', remoteState: PlantareRemoteState.accepted, ); final snapshot = await repoA.exportInventory(); expect(snapshot.plantares, hasLength(1)); final dbB = newTestDatabase(); addTearDown(dbB.close); final repoB = newTestRepository(dbB); await repoB.importInventory(snapshot); final onB = await repoB.watchPlantares().first; expect(onB.single.counterparty, 'Colectivo semillero'); expect(onB.single.owedDescription, 'una mazorca'); // The bilateral form rides the backup too. expect(onB.single.pledgeId, 'pledge-3'); expect(onB.single.debtorSignature, 'dsig'); expect(onB.single.creditorSignature, 'csig'); expect(onB.single.remoteState, PlantareRemoteState.accepted); }); }