import 'package:commons_core/commons_core.dart'; import 'package:drift/drift.dart' hide isNull; import 'package:flutter_test/flutter_test.dart'; import 'package:tane/data/variety_repository.dart'; import 'package:tane/db/database.dart'; import 'package:tane/db/enums.dart'; import '../support/test_support.dart'; void main() { late AppDatabase db; late VarietyRepository repo; late String varietyId; setUp(() async { db = newTestDatabase(); repo = newTestRepository(db); varietyId = await repo.addQuickVariety(label: 'Tomate rosa'); }); tearDown(() => db.close()); Future movementById(String id) => (db.select(db.movements)..where((m) => m.id.equals(id))).getSingle(); Future lotById(String id) => (db.select(db.lots)..where((l) => l.id.equals(id))).getSingle(); test('a plain gift records just the given movement', () async { final lotId = await repo.addLot(varietyId: varietyId); final result = await repo.recordHandover( lotId: lotId, direction: HandoverDirection.iGave, counterparty: 'María', ); expect(result.saleId, isNull); expect(result.plantareId, isNull); final movement = await movementById(result.movementId); expect(movement.type, MovementType.given); expect(movement.lotId, lotId); expect(movement.notes, 'María'); expect(movement.plantareId, isNull); expect(await repo.watchSales().first, isEmpty); expect(await repo.watchPlantares().first, isEmpty); }); test( 'giving all depletes the lot, privatizes it and drops its price', () async { final lotId = await repo.addLot( varietyId: varietyId, quantity: const Quantity(kind: QuantityKind.grams, count: 40), abundance: Abundance.plentyToShare, offerStatus: OfferStatus.sell, priceAmount: 3, priceCurrency: '€', ); final result = await repo.recordHandover( lotId: lotId, direction: HandoverDirection.iGave, gaveAll: true, ); // The movement carries what actually moved: the lot's whole quantity. final movement = await movementById(result.movementId); expect(movement.quantityKind, QuantityKind.grams.name); expect(movement.quantityPrecise, 40); // The lot stays as an empty jar, withdrawn from the market. final lot = await lotById(lotId); expect(lot.quantityPrecise, 0); expect(lot.quantityKind, QuantityKind.grams.name); expect(lot.abundance, isNull); expect(lot.offerStatus, OfferStatus.private); expect(lot.priceAmount, isNull); expect(lot.priceCurrency, isNull); expect( (await repo.shareableLots()).where((l) => l.lotId == lotId), isEmpty, ); }, ); test('giving part keeps the lot open and offered', () async { final lotId = await repo.addLot( varietyId: varietyId, quantity: const Quantity(kind: QuantityKind.grams, count: 40), offerStatus: OfferStatus.shared, ); final result = await repo.recordHandover( lotId: lotId, direction: HandoverDirection.iGave, quantity: const Quantity(kind: QuantityKind.grams, count: 10), ); final movement = await movementById(result.movementId); expect(movement.quantityPrecise, 10); final lot = await lotById(lotId); expect(lot.quantityPrecise, 40); // untouched: the log holds the history expect(lot.offerStatus, OfferStatus.shared); expect( (await repo.shareableLots()).where((l) => l.lotId == lotId), hasLength(1), ); }); test('a payment on a give creates the matching sale record', () async { final lotId = await repo.addLot(varietyId: varietyId); final result = await repo.recordHandover( lotId: lotId, direction: HandoverDirection.iGave, counterparty: 'María', withPayment: true, paymentAmount: 2.5, paymentCurrency: 'Ğ1', ); final sales = await repo.watchSales().first; expect(sales, hasLength(1)); expect(sales.single.id, result.saleId); expect(sales.single.direction, SaleDirection.iSold); expect(sales.single.varietyId, varietyId); expect(sales.single.counterparty, 'María'); expect(sales.single.amount, 2.5); expect(sales.single.currency, 'Ğ1'); }); test('a promise on a give creates a plantare owed to me, linked from the ' 'movement', () async { final lotId = await repo.addLot(varietyId: varietyId); final result = await repo.recordHandover( lotId: lotId, direction: HandoverDirection.iGave, counterparty: 'María', withPromise: true, promiseOwedDescription: 'un puñado la próxima temporada', ); final plantares = await repo.watchPlantares().first; expect(plantares, hasLength(1)); expect(plantares.single.id, result.plantareId); expect(plantares.single.direction, PlantareDirection.owedToMe); expect(plantares.single.varietyId, varietyId); expect(plantares.single.counterparty, 'María'); expect(plantares.single.owedDescription, 'un puñado la próxima temporada'); // The link the model reserved: the movement carries the plantare id. final movement = await movementById(result.movementId); expect(movement.plantareId, result.plantareId); }); test('receiving with payment and promise mirrors the directions', () async { final lotId = await repo.addLot( varietyId: varietyId, quantity: const Quantity(kind: QuantityKind.grams, count: 40), ); final result = await repo.recordHandover( lotId: lotId, direction: HandoverDirection.iReceived, // Meaningless on a receive; must not deplete anything. gaveAll: true, withPayment: true, paymentAmount: 5, paymentCurrency: '€', withPromise: true, ); final movement = await movementById(result.movementId); expect(movement.type, MovementType.received); final lot = await lotById(lotId); expect(lot.quantityPrecise, 40); final sales = await repo.watchSales().first; expect(sales.single.direction, SaleDirection.iBought); final plantares = await repo.watchPlantares().first; expect(plantares.single.direction, PlantareDirection.iReturn); }); }