serverpod-offline-sync/test/crdt/crdt_data_convert_test.dart
2026-01-27 15:59:51 -03:00

123 lines
4 KiB
Dart

import 'package:drift/drift.dart' hide isNotNull, isNull;
import 'package:drift_offline_sync/drift_offline_sync.dart';
import 'package:test/test.dart';
import '../utils/crdt_context.dart';
import '../utils/database.dart';
import '../utils/tables.dart';
void main() {
group('Given a data object with every column type', () {
late CrdtDatabase crdtDb;
late TableWithEveryColumnTypeData createdData;
late Hlc testHlc;
setUp(() async {
createdData = _createTestData();
(_, crdtDb, _) = database.crdtContext;
testHlc = Hlc.now(crdtDb.nodeId);
});
group('when converting the data to CRDT data entries', () {
late Iterable<CrdtDataEntry> entries;
setUp(() async {
entries = crdtDb.convertToCrdtDataEntry(createdData, testHlc);
});
test('then it creates an entry for each column.', () async {
expect(
entries.map((e) => e.columnName).toSet(),
equals(
database.tableWithEveryColumnType.$columns.map((c) => c.$name).toSet(),
),
);
});
test('then each entry has the correct user ID.', () async {
expect(entries.map((e) => e.userId), everyElement(equals(crdtDb.userId)));
});
test('then each entry has the correct table name.', () async {
expect(
entries.map((e) => e.tblName),
everyElement(equals('table_with_every_column_type')),
);
});
test('then each entry has the correct HLC timestamp.', () async {
expect(entries.map((e) => e.hlcTimestamp), everyElement(equals(testHlc)));
});
test('then all entries have expected column names and values.', () async {
final mappedData = await database.tableWithEveryColumnType.fromCrdtDataEntries(
entries,
);
expect(mappedData.single, equals(createdData));
});
});
});
group('Given a changeset converted from a data object with every column type', () {
late OfflineSyncCrdt crdt;
late CrdtDatabase crdtDb;
late TableWithEveryColumnTypeData createdData;
late List<CrdtDataEntry> expectedChangeset;
late Hlc testHlc;
setUp(() async {
createdData = _createTestData();
(crdt, crdtDb, _) = database.crdtContext;
// Perform an insert to the database to get the exact changeset that would
// be generated by the CRDT triggers.
await database.tableWithEveryColumnType.insertOne(createdData);
expectedChangeset = await crdtDb.managers.crdtDataTable
.filter(
(f) => f.columnName.equals(crdtDb.sqlBuilder.isDeletedColumnName).not(),
)
.get();
testHlc = expectedChangeset.first.hlcTimestamp;
await crdtDb.crdtDataTable.deleteAll();
});
group('when saving the changeset to the CRDT database', () {
late Iterable<CrdtDataEntry> entries;
setUp(() async {
entries = crdtDb.convertToCrdtDataEntry(createdData, testHlc);
await crdt.saveChangeset(entries, [testHlc]);
});
test('then the saved data is equal to the generated by CRDT triggers.', () async {
final savedChangeset = await crdtDb.managers.crdtDataTable.get();
// The list object is not equal to the other objects because it is a
// different instance. So we remove it from the list and compare the
// it separately.
final savedListObject = savedChangeset.removeAt(7);
final expectedListObject = expectedChangeset.removeAt(7);
expect(savedChangeset, equals(expectedChangeset));
expect(
savedListObject.unwrappedValue,
equals(expectedListObject.unwrappedValue),
);
});
});
});
}
TableWithEveryColumnTypeData _createTestData() => TableWithEveryColumnTypeData(
id: const RowId(1),
aBool: true,
aDateTime: DateTime.now(),
aText: 'test',
anInt: 1,
anInt64: BigInt.one,
aReal: 1.0,
aBlob: Uint8List.fromList([1, 2, 3]),
anIntEnum: TodoStatus.open,
aTextWithConverter: MyCustomObject('test'),
aUuid: UuidValue.fromString('123e4567-e89b-12d3-a456-426614174000'),
);