refactor: Improve code organization and reutilization
This commit is contained in:
parent
df0f798166
commit
eadf1f7d7c
5 changed files with 291 additions and 137 deletions
94
test/crdt/crdt_data_convert_test.dart
Normal file
94
test/crdt/crdt_data_convert_test.dart
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import 'package:drift/drift.dart' hide isNotNull, isNull;
|
||||
import 'package:drift_offline_first/drift_offline_first.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 = 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'),
|
||||
);
|
||||
|
||||
(_, crdtDb, _) = database.crdtContext;
|
||||
testHlc = Hlc.now(crdtDb.nodeId);
|
||||
});
|
||||
|
||||
group('when converting the data to CRDT data entries', () {
|
||||
late Iterable<CrdtDataEntry> entries;
|
||||
|
||||
setUp(() async {
|
||||
entries = createdData.toCrdtDataEntry(crdtDb, testHlc).toList();
|
||||
});
|
||||
|
||||
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 entriesMap = {
|
||||
for (final entry in entries) entry.columnName: entry.rawValue,
|
||||
};
|
||||
|
||||
expect(entriesMap['a_bool'], equals(createdData.aBool?.driftAny));
|
||||
expect(entriesMap['a_date_time'], isNotNull);
|
||||
expect(entriesMap['a_text'], equals(createdData.aText?.driftAny));
|
||||
expect(entriesMap['an_int'], equals(createdData.anInt?.driftAny));
|
||||
expect(entriesMap['an_int64'], equals(createdData.anInt64?.driftAny));
|
||||
expect(entriesMap['a_real'], equals(createdData.aReal?.driftAny));
|
||||
expect(entriesMap['a_blob'], equals(createdData.aBlob?.driftAny));
|
||||
expect(
|
||||
entriesMap['an_int_enum'],
|
||||
equals(createdData.anIntEnum?.index.driftAny),
|
||||
);
|
||||
expect(
|
||||
// aTextWithConverter uses 'insert' as column name
|
||||
entriesMap['insert']?.rawSqlValue,
|
||||
equals(const DriftAny({'data': 'test'}).rawSqlValue),
|
||||
);
|
||||
expect(entriesMap['a_uuid'], createdData.aUuid?.driftAny);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
extension on Object {
|
||||
DriftAny? get driftAny => DriftAny(this);
|
||||
}
|
||||
81
test/crdt/crdt_data_extract_test.dart
Normal file
81
test/crdt/crdt_data_extract_test.dart
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import 'package:drift/drift.dart' hide isNotNull, isNull;
|
||||
import 'package:drift_offline_first/drift_offline_first.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../utils/crdt_context.dart';
|
||||
import '../utils/database.dart';
|
||||
import '../utils/tables.dart';
|
||||
|
||||
void main() {
|
||||
group('Given CRDT database for a database with data with every column type', () {
|
||||
late CrdtDatabase crdtDb;
|
||||
late String rowId;
|
||||
late TableWithEveryColumnTypeData createdData;
|
||||
|
||||
setUp(() async {
|
||||
final createdRowId = await database.managers.tableWithEveryColumnType.create(
|
||||
(t) => t(
|
||||
aBool: const Value(true),
|
||||
aDateTime: Value(DateTime.now()),
|
||||
aText: const Value('test'),
|
||||
anInt: const Value(1),
|
||||
anInt64: Value(BigInt.one),
|
||||
aReal: const Value(1.0),
|
||||
aBlob: Value(Uint8List.fromList([1, 2, 3])),
|
||||
anIntEnum: const Value(TodoStatus.open),
|
||||
aTextWithConverter: Value(MyCustomObject('test')),
|
||||
aUuid: Value(UuidValue.fromString('123e4567-e89b-12d3-a456-426614174000')),
|
||||
),
|
||||
);
|
||||
|
||||
(_, crdtDb, _) = database.crdtContext;
|
||||
rowId = createdRowId.toString();
|
||||
createdData = await database.managers.tableWithEveryColumnType.getSingle();
|
||||
});
|
||||
|
||||
test(
|
||||
'when getting the data from the CRDT data table of the CRDT database '
|
||||
'then it rebuilds the data perfectly.', () async {
|
||||
final data = await crdtDb.getSingleFromCrdtData<TableWithEveryColumnTypeData>(
|
||||
tableName: 'table_with_every_column_type',
|
||||
rowId: rowId,
|
||||
);
|
||||
|
||||
expect(data, equals(createdData));
|
||||
});
|
||||
|
||||
test(
|
||||
'when trying to get the data from the CRDT data table with a non-existent row id '
|
||||
'then it returns null.', () async {
|
||||
final data = await crdtDb.getSingleFromCrdtData<TableWithEveryColumnTypeData>(
|
||||
tableName: 'table_with_every_column_type',
|
||||
rowId: 'non-existent-row-id',
|
||||
);
|
||||
|
||||
expect(data, isNull);
|
||||
});
|
||||
|
||||
test(
|
||||
'when trying to get the data from the CRDT data table with multiple row ids '
|
||||
'then it returns the data for the existing row ids.', () async {
|
||||
final data = await crdtDb.getFromCrdtData<TableWithEveryColumnTypeData>(
|
||||
tableName: 'table_with_every_column_type',
|
||||
rowIds: [rowId, 'non-existent-row-id'],
|
||||
);
|
||||
|
||||
expect(data, equals([createdData]));
|
||||
});
|
||||
|
||||
test(
|
||||
'when trying to get the data from the CRDT data table for a non-synchronized table '
|
||||
'then it throws an error.', () async {
|
||||
expect(
|
||||
() => crdtDb.getFromCrdtData<TableWithEveryColumnTypeData>(
|
||||
tableName: 'table_without_pk',
|
||||
rowIds: [rowId],
|
||||
),
|
||||
throwsA(isA<ArgumentError>()),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:drift/drift.dart' hide isNotNull, isNull;
|
||||
import 'package:drift_offline_first/drift_offline_first.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
@ -23,7 +22,7 @@ void main() {
|
|||
targetDate: DateTime.now(),
|
||||
);
|
||||
|
||||
changeset = todo.toCrdtDataEntry(nodeId);
|
||||
changeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(nodeId));
|
||||
});
|
||||
|
||||
test(
|
||||
|
|
@ -50,10 +49,10 @@ void main() {
|
|||
targetDate: DateTime.now(),
|
||||
);
|
||||
|
||||
firstChangeset = todo.toCrdtDataEntry(nodeId);
|
||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(nodeId));
|
||||
await crdt.merge(firstChangeset);
|
||||
|
||||
lateOldChangeset = todo.toCrdtDataEntry(nodeId, hlc: Hlc.zero(nodeId));
|
||||
lateOldChangeset = todo.toCrdtDataEntry(crdtDb, Hlc.zero(nodeId));
|
||||
});
|
||||
|
||||
test(
|
||||
|
|
@ -80,11 +79,12 @@ void main() {
|
|||
targetDate: DateTime.now(),
|
||||
);
|
||||
|
||||
firstChangeset = todo.toCrdtDataEntry(nodeId);
|
||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(nodeId));
|
||||
await crdt.merge(firstChangeset);
|
||||
|
||||
newerChangeset =
|
||||
todo.toCrdtDataEntry(nodeId).where((e) => e.columnName == 'content');
|
||||
newerChangeset = todo
|
||||
.toCrdtDataEntry(crdtDb, Hlc.now(nodeId))
|
||||
.where((e) => e.columnName == 'content');
|
||||
});
|
||||
|
||||
test(
|
||||
|
|
@ -101,26 +101,3 @@ void main() {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
extension on TodoEntry {
|
||||
Iterable<CrdtDataEntry> toCrdtDataEntry(String nodeId, {Hlc? hlc}) {
|
||||
final hlcTimestamp = hlc ?? Hlc.now(nodeId);
|
||||
|
||||
return database.todosTable.$columns.map(
|
||||
(c) => CrdtDataEntry(
|
||||
userId: 'test',
|
||||
tblName: 'todos',
|
||||
columnName: c.$name,
|
||||
rowId: id.toString(),
|
||||
rawValue: (toJson()[c.$name] as Object?)?.toDriftAny(),
|
||||
hlcTimestamp: hlcTimestamp,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on Object {
|
||||
DriftAny? toDriftAny() {
|
||||
return DriftAny(this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:drift/drift.dart' hide isNotNull, isNull;
|
||||
import 'package:drift_offline_first/drift_offline_first.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
|
@ -26,7 +25,7 @@ void main() {
|
|||
targetDate: DateTime.now(),
|
||||
);
|
||||
|
||||
changeset = todo.toCrdtDataEntry(otherNodeId);
|
||||
changeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(otherNodeId));
|
||||
lastHlc = Hlc.now(otherNodeId);
|
||||
});
|
||||
|
||||
|
|
@ -66,11 +65,11 @@ void main() {
|
|||
);
|
||||
|
||||
lastHlc = Hlc.now(otherNodeId);
|
||||
firstChangeset = todo.toCrdtDataEntry(otherNodeId, lastHlc);
|
||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, lastHlc);
|
||||
await crdt.saveChangeset(firstChangeset, [lastHlc]);
|
||||
|
||||
lateOldHlc = Hlc.zero(otherNodeId);
|
||||
lateOldChangeset = todo.toCrdtDataEntry(otherNodeId, lateOldHlc);
|
||||
lateOldChangeset = todo.toCrdtDataEntry(crdtDb, lateOldHlc);
|
||||
});
|
||||
|
||||
group('when saving a changeset with older HLC', () {
|
||||
|
|
@ -109,12 +108,12 @@ void main() {
|
|||
);
|
||||
|
||||
lastHlc = Hlc.now(otherNodeId);
|
||||
firstChangeset = todo.toCrdtDataEntry(otherNodeId);
|
||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, lastHlc);
|
||||
await crdt.saveChangeset(firstChangeset, [lastHlc]);
|
||||
|
||||
newerHlc = Hlc.now(otherNodeId);
|
||||
newerChangeset = todo
|
||||
.toCrdtDataEntry(otherNodeId, newerHlc)
|
||||
.toCrdtDataEntry(crdtDb, newerHlc)
|
||||
.where((e) => e.columnName == 'content');
|
||||
});
|
||||
|
||||
|
|
@ -139,26 +138,3 @@ void main() {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
extension on TodoEntry {
|
||||
Iterable<CrdtDataEntry> toCrdtDataEntry(String nodeId, [Hlc? hlc]) {
|
||||
final hlcTimestamp = hlc ?? Hlc.now(nodeId);
|
||||
|
||||
return database.todosTable.$columns.map(
|
||||
(c) => CrdtDataEntry(
|
||||
userId: 'test',
|
||||
tblName: 'todos',
|
||||
columnName: c.$name,
|
||||
rowId: id.toString(),
|
||||
rawValue: (toJson()[c.$name] as Object?)?.toDriftAny(),
|
||||
hlcTimestamp: hlcTimestamp,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on Object {
|
||||
DriftAny? toDriftAny() {
|
||||
return DriftAny(this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ void main() {
|
|||
late List<String> triggers;
|
||||
|
||||
setUp(() async {
|
||||
final generator = Sqlite3OfflineSyncTriggers(migrator);
|
||||
final generator = Sqlite3OfflineSyncTriggers(migrator.crdtDb);
|
||||
triggers = generator.generateCreateTriggerStatements(database.todosTable);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue