Files
kooltab/test/bar_tab_service_test.dart
T

922 lines
31 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:drift/drift.dart' hide isNull, isNotNull;
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:kooltap/database/app_database.dart';
import 'package:kooltap/models/product.dart';
import 'package:kooltap/services/bar_tab_service.dart';
import 'package:kooltap/services/product_service.dart';
void main() {
late AppDatabase database;
late DriftBarTabService service;
setUp(() async {
database = AppDatabase(NativeDatabase.memory());
service = DriftBarTabService(database: database);
await database
.into(database.products)
.insert(
ProductsCompanion.insert(
id: 'prod-1',
name: 'Test Beer',
category: 'Drinks',
stockQuantity: 100,
lowStockThreshold: 10,
priceInCents: 400,
),
);
});
tearDown(() async {
await database.close();
});
Product createTestProduct({
String id = 'prod-1',
String name = 'Test Beer',
int priceInCents = 400,
}) {
return Product(
id: id,
name: name,
category: 'Drinks',
stockQuantity: 100,
lowStockThreshold: 10,
priceInCents: priceInCents,
);
}
group('DriftBarTabService', () {
group('createTab', () {
test('creates an empty tab at version 1', () async {
final tab = await service.createTab(customerName: 'John');
expect(tab.customerName, 'John');
expect(tab.version, 1);
expect(tab.items, isEmpty);
});
test('rejects a blank customer name', () async {
await expectLater(
service.createTab(customerName: ' '),
throwsA(isA<ArgumentError>()),
);
});
test('creates tab with unique id', () async {
final tab1 = await service.createTab(customerName: 'John');
final tab2 = await service.createTab(customerName: 'Jane');
expect(tab1.id, isNot(equals(tab2.id)));
});
});
group('getOpenTabs', () {
test('returns empty list when no tabs', () async {
final tabs = await service.getOpenTabs();
expect(tabs, isEmpty);
});
test('tabs sorted by openedAt descending', () async {
final now = DateTime.now();
await database
.into(database.barTabs)
.insert(
BarTabsCompanion.insert(
id: 'tab-first',
customerName: 'First',
openedAt: now.subtract(const Duration(hours: 1)),
),
);
await database
.into(database.barTabs)
.insert(
BarTabsCompanion.insert(
id: 'tab-second',
customerName: 'Second',
openedAt: now,
),
);
final tabs = await service.getOpenTabs();
expect(tabs.first.customerName, 'Second');
expect(tabs.last.customerName, 'First');
});
});
group('getTabById', () {
test('returns tab when exists', () async {
final created = await service.createTab(customerName: 'Test');
final tab = await service.getTabById(created.id);
expect(tab, isNotNull);
expect(tab!.customerName, 'Test');
});
test('returns null when not exists', () async {
final tab = await service.getTabById('nonexistent');
expect(tab, isNull);
});
});
group('renameTab', () {
test('updates the tab customer name', () async {
final tab = await service.createTab(customerName: 'John');
await service.renameTab(tabId: tab.id, customerName: ' Jane ');
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.customerName, 'Jane');
});
});
group('deleteTab', () {
test('deletes the tab and restores item stock', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
await service.deleteTab(tab.id);
expect(await service.getTabById(tab.id), isNull);
final updatedProduct = await (database.select(
database.products,
)..where((row) => row.id.equals(product.id))).getSingle();
expect(updatedProduct.stockQuantity, 100);
expect(await database.select(database.tabItems).get(), isEmpty);
expect(await database.select(database.tabItemPurchases).get(), isEmpty);
});
});
group('addProductToTab', () {
test('adds new product to tab', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items.length, 1);
expect(updatedTab.items.first.productName, 'Test Beer');
expect(updatedTab.items.first.quantity, 1);
});
test('increments quantity when product already in tab', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items.length, 1);
expect(updatedTab.items.first.quantity, 3);
});
test('records each addition as a separate purchase event', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
final barTab = (await service.getOpenTabs()).first;
final historyTab = (await service.getOpenTabs(
includeRemoved: true,
)).first;
expect(barTab.items.first.quantity, 2);
expect(barTab.items.first.purchases, hasLength(2));
expect(historyTab.items.first.purchases, hasLength(2));
expect(
historyTab.items.first.purchases.every(
(purchase) => !purchase.isRemoved,
),
isTrue,
);
});
test('adding product calculates correct line total', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct(priceInCents: 450);
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items.first.lineTotalInCents, 900);
});
test('decreases stock as part of adding the item', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final storedProduct = await (database.select(
database.products,
)..where((row) => row.id.equals(product.id))).getSingle();
expect(storedProduct.stockQuantity, 99);
});
test('still sells when the stock is at zero', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await (database.update(database.products)
..where((row) => row.id.equals(product.id)))
.write(const ProductsCompanion(stockQuantity: Value(0)));
await service.addProductToTab(tabId: tab.id, product: product);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items.single.quantity, 1);
final storedProduct = await (database.select(
database.products,
)..where((row) => row.id.equals(product.id))).getSingle();
expect(storedProduct.stockQuantity, -1);
});
});
// The website changes prices: drinks already on a tab keep theirs, the
// next one is charged the new price
group('addProductToTab after a price or name change', () {
test(
'a new price starts a new line; earlier drinks keep theirs',
() async {
final tab = await service.createTab(customerName: 'John');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(priceInCents: 450),
);
final items = (await service.getTabById(tab.id))!.items;
expect([
for (final item in items) (item.unitPriceInCents, item.quantity),
], unorderedEquals([(400, 2), (450, 1)]));
expect((await service.getTabById(tab.id))!.totalInCents, 1250);
},
);
test('more drinks at the new price go on the new line', () async {
final tab = await service.createTab(customerName: 'John');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
for (var i = 0; i < 3; i++) {
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(priceInCents: 450),
);
}
final items = (await service.getTabById(tab.id))!.items;
expect(items, hasLength(2));
expect(
items.firstWhere((item) => item.unitPriceInCents == 450).quantity,
3,
);
});
test('a price going back joins the line at that price again', () async {
final tab = await service.createTab(customerName: 'John');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(priceInCents: 300),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
final items = (await service.getTabById(tab.id))!.items;
expect([
for (final item in items) (item.unitPriceInCents, item.quantity),
], unorderedEquals([(400, 2), (300, 1)]));
});
test('a renamed product starts a new line under its new name', () async {
final tab = await service.createTab(customerName: 'John');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(name: 'Pils'),
);
final items = (await service.getTabById(tab.id))!.items;
expect([
for (final item in items) item.productName,
], unorderedEquals(['Test Beer', 'Pils']));
});
test('both lines count down the stock of the one product', () async {
final tab = await service.createTab(customerName: 'John');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(priceInCents: 450),
);
final stored = await (database.select(
database.products,
)..where((row) => row.id.equals('prod-1'))).getSingle();
expect(stored.stockQuantity, 98);
});
test('settling charges each line at its own price', () async {
final tab = await service.createTab(customerName: 'John');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(priceInCents: 450),
);
await service.closeTab(tab.id);
final closed = (await service.getClosedTabs()).single;
expect(closed.totalInCents, 850);
expect([
for (final item in closed.items) item.unitPriceInCents,
], unorderedEquals([400, 450]));
});
});
// Foo has 2 at the old price; the website changes the price; staff tap +
// on Foo's line. The 2 stay at the old price, every new one is charged
// the new price.
group('+ on a line after a price change', () {
Future<void> setPrice(int priceInCents, {String? name}) async {
await (database.update(
database.products,
)..where((row) => row.id.equals('prod-1'))).write(
ProductsCompanion(
priceInCents: Value(priceInCents),
name: name == null ? const Value.absent() : Value(name),
),
);
}
Future<String> fooWithTwoAtOldPrice() async {
final tab = await service.createTab(customerName: 'Foo');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await setPrice(450);
return tab.id;
}
Future<List<(int, int)>> lines(String tabId) async {
return [
for (final item in (await service.getTabById(tabId))!.items)
(item.unitPriceInCents, item.quantity),
];
}
Future<String> lineAt(String tabId, int priceInCents) async {
return (await service.getTabById(tabId))!.items
.firstWhere((item) => item.unitPriceInCents == priceInCents)
.id;
}
test(
'the old drinks keep their price, the new one gets the new price',
() async {
final tabId = await fooWithTwoAtOldPrice();
final added = await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 1,
);
expect(added, 1);
expect(await lines(tabId), unorderedEquals([(400, 2), (450, 1)]));
expect((await service.getTabById(tabId))!.totalInCents, 1250);
},
);
test(
'tapping + on the old line again keeps adding at the new price',
() async {
final tabId = await fooWithTwoAtOldPrice();
final oldLine = await lineAt(tabId, 400);
await service.adjustTabItemQuantity(tabItemId: oldLine, delta: 1);
await service.adjustTabItemQuantity(tabItemId: oldLine, delta: 1);
expect(await lines(tabId), unorderedEquals([(400, 2), (450, 2)]));
},
);
test('+ on the line at the new price adds there', () async {
final tabId = await fooWithTwoAtOldPrice();
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 1,
);
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 450),
delta: 1,
);
expect(await lines(tabId), unorderedEquals([(400, 2), (450, 2)]));
});
test('the tile and + end up on the same new line', () async {
final tabId = await fooWithTwoAtOldPrice();
await service.addProductToTab(
tabId: tabId,
product: createTestProduct(priceInCents: 450),
);
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 1,
);
expect(await lines(tabId), unorderedEquals([(400, 2), (450, 2)]));
});
test('+ several at once adds them all at the new price', () async {
final tabId = await fooWithTwoAtOldPrice();
final added = await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 3,
);
expect(added, 3);
expect(await lines(tabId), unorderedEquals([(400, 2), (450, 3)]));
});
test('− on the old line takes off a drink at the old price', () async {
final tabId = await fooWithTwoAtOldPrice();
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 1,
);
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: -1,
);
expect(await lines(tabId), unorderedEquals([(400, 1), (450, 1)]));
});
test('a price going back makes + add to the old line again', () async {
final tabId = await fooWithTwoAtOldPrice();
await setPrice(400);
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 1,
);
expect(await lines(tabId), [(400, 3)]);
});
test('a renamed product gets a new line under its new name', () async {
final tab = await service.createTab(customerName: 'Foo');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await setPrice(400, name: 'Pils');
await service.adjustTabItemQuantity(
tabItemId: (await service.getTabById(tab.id))!.items.single.id,
delta: 1,
);
final names = [
for (final item in (await service.getTabById(tab.id))!.items)
(item.productName, item.quantity),
];
expect(names, unorderedEquals([('Test Beer', 1), ('Pils', 1)]));
});
test(
'a hidden (archived) product is still charged its latest price',
() async {
final tabId = await fooWithTwoAtOldPrice();
await (database.update(database.products)
..where((row) => row.id.equals('prod-1')))
.write(const ProductsCompanion(active: Value(false)));
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 1,
);
expect(await lines(tabId), unorderedEquals([(400, 2), (450, 1)]));
},
);
test('a line whose product is gone keeps its own price', () async {
final tab = await service.createTab(customerName: 'Foo');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await database.delete(database.products).go();
await service.adjustTabItemQuantity(
tabItemId: (await service.getTabById(tab.id))!.items.single.id,
delta: 1,
);
expect(await lines(tab.id), [(400, 2)]);
});
test('another tab without the product gets the new price', () async {
await fooWithTwoAtOldPrice();
final bar = await service.createTab(customerName: 'Bar');
await service.addProductToTab(
tabId: bar.id,
product: createTestProduct(priceInCents: 450),
);
expect(await lines(bar.id), [(450, 1)]);
});
test('every drink counts down the stock of the one product', () async {
final tabId = await fooWithTwoAtOldPrice();
await service.adjustTabItemQuantity(
tabItemId: await lineAt(tabId, 400),
delta: 2,
);
final stored = await (database.select(
database.products,
)..where((row) => row.id.equals('prod-1'))).getSingle();
expect(stored.stockQuantity, 96);
});
});
group('adjustTabItemQuantity', () {
test('updates item quantity', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final tabItemId = (await service.getTabById(tab.id))!.items.first.id;
await service.adjustTabItemQuantity(tabItemId: tabItemId, delta: 4);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items.first.quantity, 5);
});
test('hides item when the delta removes all quantity', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final tabItemId = (await service.getTabById(tab.id))!.items.first.id;
await service.adjustTabItemQuantity(tabItemId: tabItemId, delta: -1);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items, isEmpty);
});
test('restores stock when quantity decreases', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final tabItemId = (await service.getTabById(tab.id))!.items.first.id;
await service.adjustTabItemQuantity(tabItemId: tabItemId, delta: -1);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items, isEmpty);
final updatedProduct = await (database.select(
database.products,
)..where((row) => row.id.equals(product.id))).getSingle();
expect(updatedProduct.stockQuantity, 100);
});
test('keeps a removed purchase in the history audit trail', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
final tabItemId = (await service.getTabById(tab.id))!.items.first.id;
await service.adjustTabItemQuantity(tabItemId: tabItemId, delta: -1);
final historyTab = (await service.getOpenTabs(
includeRemoved: true,
)).first;
final item = historyTab.items.first;
expect(item.quantity, 1);
expect(
item.purchases.where((purchase) => purchase.isRemoved),
hasLength(1),
);
expect(
item.purchases.where((purchase) => !purchase.isRemoved),
hasLength(1),
);
});
test(
'applies concurrent deltas without losing stock consistency',
() async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final tabItemId = (await service.getTabById(tab.id))!.items.first.id;
final deltas = await Future.wait([
service.adjustTabItemQuantity(tabItemId: tabItemId, delta: 1),
service.adjustTabItemQuantity(tabItemId: tabItemId, delta: 1),
]);
expect(deltas, [1, 1]);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items.first.quantity, 3);
final updatedProduct = await (database.select(
database.products,
)..where((row) => row.id.equals(product.id))).getSingle();
expect(updatedProduct.stockQuantity, 97);
},
);
});
group('closeTab', () {
test('archives tab items to closed tabs', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
await service.closeTab(tab.id);
final closedTabs = await service.getClosedTabs();
expect(closedTabs.length, 1);
expect(closedTabs.first.customerName, 'John');
expect(closedTabs.first.items.length, 2);
expect(
closedTabs.first.items.every((item) => item.quantity == 1),
true,
);
expect(
closedTabs.first.items.every((item) => item.purchasedAt != null),
true,
);
});
test(
'keeps removed purchases visible but excludes them from totals',
() async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.addProductToTab(tabId: tab.id, product: product);
final tabItemId = (await service.getTabById(tab.id))!.items.first.id;
await service.adjustTabItemQuantity(tabItemId: tabItemId, delta: -1);
await service.closeTab(tab.id);
final closedTab = (await service.getClosedTabs()).first;
expect(closedTab.items, hasLength(2));
expect(closedTab.items.where((item) => item.isRemoved), hasLength(1));
expect(closedTab.itemCount, 1);
expect(closedTab.totalInCents, 400);
},
);
test('clears tab items after closing', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.closeTab(tab.id);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.items, isEmpty);
});
test(
'keeps the tab under the same name and drops its purchases',
() async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await service.closeTab(tab.id);
final updatedTab = await service.getTabById(tab.id);
expect(updatedTab!.customerName, 'John');
expect(updatedTab.items, isEmpty);
expect(
await database.select(database.tabItemPurchases).get(),
isEmpty,
);
},
);
test('throws when the tab changed since the expected version', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
await expectLater(
service.closeTab(tab.id, expectedVersion: tab.version),
throwsA(isA<TabVersionConflictException>()),
);
expect((await service.getTabById(tab.id))!.items, hasLength(1));
expect(await service.getClosedTabs(), isEmpty);
});
test('closes when the expected version matches', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final current = await service.getTabById(tab.id);
await service.closeTab(tab.id, expectedVersion: current!.version);
expect(await service.getClosedTabs(), hasLength(1));
});
test('throws for an unknown tab', () async {
await expectLater(
service.closeTab('missing'),
throwsA(isA<TabNotFoundException>()),
);
});
test('closed tab preserves item prices', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct(priceInCents: 550);
await service.addProductToTab(tabId: tab.id, product: product);
await service.closeTab(tab.id);
final closedTabs = await service.getClosedTabs();
expect(closedTabs.first.items.first.unitPriceInCents, 550);
});
});
group('versioning', () {
test('every change bumps the tab version', () async {
final tab = await service.createTab(customerName: 'John');
final product = createTestProduct();
await service.addProductToTab(tabId: tab.id, product: product);
final afterAdd = (await service.getTabById(tab.id))!;
await service.adjustTabItemQuantity(
tabItemId: afterAdd.items.first.id,
delta: 1,
);
final afterAdjust = (await service.getTabById(tab.id))!;
await service.renameTab(tabId: tab.id, customerName: 'Jane');
final afterRename = (await service.getTabById(tab.id))!;
expect(afterAdd.version, tab.version + 1);
expect(afterAdjust.version, afterAdd.version + 1);
expect(afterRename.version, afterAdjust.version + 1);
});
});
group('watchOpenTabs', () {
test('emits again when an item is added', () async {
final tab = await service.createTab(customerName: 'John');
final emissions = service.watchOpenTabs();
final expectation = expectLater(
emissions.map((tabs) => tabs.single.itemCount),
emitsThrough(1),
);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await expectation;
});
});
group('getDistinctCustomerNames', () {
test('returns each closed customer once, sorted', () async {
for (final name in ['Zoe', 'Adam', 'Zoe']) {
final tab = await service.createTab(customerName: name);
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await service.closeTab(tab.id);
}
expect(await service.getDistinctCustomerNames(), ['Adam', 'Zoe']);
});
});
group('getClosedTabs', () {
test('returns empty when no closed tabs', () async {
final tabs = await service.getClosedTabs();
expect(tabs, isEmpty);
});
test('returns closed tabs sorted by closedAt descending', () async {
final now = DateTime.now();
await database
.into(database.closedTabs)
.insert(
ClosedTabsCompanion.insert(
id: 'closed-first',
originalTabId: 'original-1',
customerName: 'First',
closedAt: now.subtract(const Duration(hours: 1)),
),
);
await database
.into(database.closedTabs)
.insert(
ClosedTabsCompanion.insert(
id: 'closed-second',
originalTabId: 'original-2',
customerName: 'Second',
closedAt: now,
),
);
final closedTabs = await service.getClosedTabs();
expect(closedTabs.first.customerName, 'Second');
expect(closedTabs.last.customerName, 'First');
});
});
group('hidden products', () {
test('a tab holding a deleted product can still be changed', () async {
final tab = await service.createTab(customerName: 'John');
await service.addProductToTab(
tabId: tab.id,
product: createTestProduct(),
);
await DriftProductService(database: database).deleteProduct('prod-1');
final item = (await service.getTabById(tab.id))!.items.single;
await service.adjustTabItemQuantity(tabItemId: item.id, delta: -1);
await service.deleteTab(tab.id);
expect(await service.getTabById(tab.id), isNull);
});
});
});
}