fix: Fix the conversion between CrdtDataEntry and Insertable
This commit is contained in:
parent
eadf1f7d7c
commit
825fd4d97c
10 changed files with 196 additions and 132 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
library;
|
library;
|
||||||
|
|
||||||
export 'src/crdt.dart';
|
export 'src/crdt.dart';
|
||||||
|
export 'src/crdt/extensions/convert.dart';
|
||||||
export 'src/crdt/extensions/entry.dart';
|
export 'src/crdt/extensions/entry.dart';
|
||||||
export 'src/crdt/extensions/extract.dart';
|
export 'src/crdt/extensions/extract.dart';
|
||||||
export 'src/crdt_base.dart';
|
export 'src/crdt_base.dart';
|
||||||
|
|
|
||||||
59
lib/src/crdt/extensions/convert.dart
Normal file
59
lib/src/crdt/extensions/convert.dart
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
|
|
||||||
|
import '../../database.dart';
|
||||||
|
import '../../hlc/converter.dart';
|
||||||
|
|
||||||
|
/// Extensions for [CrdtDatabase] to convert data to [CrdtDataEntry].
|
||||||
|
extension CrdtDatabaseConvertExtensions on CrdtDatabase {
|
||||||
|
/// Converts the [Insertable] object to a list of [CrdtDataEntry].
|
||||||
|
///
|
||||||
|
/// Note that the generated entries will have the same HLC timestamp for all
|
||||||
|
/// columns and will overwrite individual column updates if newer.
|
||||||
|
///
|
||||||
|
/// Returns a list of [CrdtDataEntry].
|
||||||
|
Iterable<CrdtDataEntry> convertToCrdtDataEntry<T extends Insertable>(
|
||||||
|
T data,
|
||||||
|
Hlc hlcTimestamp,
|
||||||
|
) {
|
||||||
|
final json = data.toJsonForDatabase(_generationContext);
|
||||||
|
final tableInfo = synchronizedTables.whereType<TableInfo<dynamic, T>>().single;
|
||||||
|
final rowIdValues = [for (final c in tableInfo.$primaryKey) json[c.$name]];
|
||||||
|
|
||||||
|
return tableInfo.$columns.map(
|
||||||
|
(c) => CrdtDataEntry(
|
||||||
|
userId: userId,
|
||||||
|
tblName: tableInfo.actualTableName,
|
||||||
|
columnName: c.$name,
|
||||||
|
rowId: rowIdValues.join(sqlBuilder.rowIdSeparator),
|
||||||
|
rawValue: json[c.$name]?.toDriftAny(),
|
||||||
|
hlcTimestamp: hlcTimestamp,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a [GenerationContext] for the database.
|
||||||
|
GenerationContext get _generationContext => GenerationContext.fromDb(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on Insertable {
|
||||||
|
Map<String, Object?> toJsonForDatabase(GenerationContext context) {
|
||||||
|
final expressions = toColumns(false);
|
||||||
|
return {
|
||||||
|
for (final e in expressions.entries) e.key: e.value.extractValue(context),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on Expression {
|
||||||
|
Object? extractValue(GenerationContext context) {
|
||||||
|
final exp = this;
|
||||||
|
if (exp is! Variable) {
|
||||||
|
throw ArgumentError.value(exp, 'exp', 'Expected a Variable');
|
||||||
|
}
|
||||||
|
return exp.mapToSimpleValue(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on Object {
|
||||||
|
DriftAny? toDriftAny() => DriftAny(this);
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
|
|
||||||
import '../../database.dart';
|
import '../../database.dart';
|
||||||
import '../../hlc/converter.dart';
|
import '../../hlc/converter.dart';
|
||||||
|
|
||||||
/// Extensions for [Iterable<CrdtDataEntry>] to get the affected tables and row IDs.
|
/// Extensions for the [CrdtDataEntry] class.
|
||||||
|
extension CrdtDataEntryUnwrapExtensions on CrdtDataEntry {
|
||||||
|
/// Returns the unwrapped raw value, if any.
|
||||||
|
Object? get unwrappedValue => rawValue?.rawSqlValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extensions for an [Iterable] of [CrdtDataEntry].
|
||||||
extension CrdtDataEntryExtensions on Iterable<CrdtDataEntry> {
|
extension CrdtDataEntryExtensions on Iterable<CrdtDataEntry> {
|
||||||
/// Gets the affected tables.
|
/// Gets the affected tables.
|
||||||
Iterable<String> get affectedTables => map((e) => e.tblName).toSet();
|
Iterable<String> get affectedTables => map((e) => e.tblName).toSet();
|
||||||
|
|
@ -14,41 +18,8 @@ extension CrdtDataEntryExtensions on Iterable<CrdtDataEntry> {
|
||||||
/// Gets the entries for a given table.
|
/// Gets the entries for a given table.
|
||||||
Iterable<CrdtDataEntry> forTable(String tableName) =>
|
Iterable<CrdtDataEntry> forTable(String tableName) =>
|
||||||
where((e) => e.tblName == tableName);
|
where((e) => e.tblName == tableName);
|
||||||
}
|
|
||||||
|
/// Returns the entries with the HLC replaced by the [hlcTimestamp].
|
||||||
/// Extensions to convert Drift [Insertable] objects to [CrdtDataEntry].
|
Iterable<CrdtDataEntry> withHlc(Hlc hlcTimestamp) =>
|
||||||
extension CrdtDataEntryConversion<T extends DataClass> on Insertable<T> {
|
map((e) => e.copyWith(hlcTimestamp: hlcTimestamp));
|
||||||
/// Converts the [Insertable] object to a list of [CrdtDataEntry].
|
|
||||||
///
|
|
||||||
/// Note that the generated entries will have the same HLC timestamp for all
|
|
||||||
/// columns and will overwrite individual column updates if newer. The
|
|
||||||
/// [database] parameter is required to find the table information to map
|
|
||||||
/// the columns.
|
|
||||||
///
|
|
||||||
/// Returns a list of [CrdtDataEntry].
|
|
||||||
Iterable<CrdtDataEntry> toCrdtDataEntry(CrdtDatabase database, Hlc hlcTimestamp) {
|
|
||||||
final json = (this as DataClass).toJson();
|
|
||||||
|
|
||||||
final tableInfo =
|
|
||||||
database.synchronizedTables.whereType<TableInfo<dynamic, T>>().single;
|
|
||||||
|
|
||||||
final rowId = [
|
|
||||||
for (final c in tableInfo.$primaryKey) json[c.$name],
|
|
||||||
].join(database.sqlBuilder.rowIdSeparator);
|
|
||||||
|
|
||||||
return tableInfo.$columns.map(
|
|
||||||
(c) => CrdtDataEntry(
|
|
||||||
userId: database.userId,
|
|
||||||
tblName: tableInfo.actualTableName,
|
|
||||||
columnName: c.$name,
|
|
||||||
rowId: rowId,
|
|
||||||
rawValue: (json[c.$name] as Object?)?.toDriftAny(),
|
|
||||||
hlcTimestamp: hlcTimestamp,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension on Object {
|
|
||||||
DriftAny? toDriftAny() => DriftAny(this);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,36 +5,44 @@ import '../../database.dart';
|
||||||
/// Extensions for [CrdtDatabase] to extract data from the CRDT data table.
|
/// Extensions for [CrdtDatabase] to extract data from the CRDT data table.
|
||||||
extension CrdtDatabaseExtractExtensions on CrdtDatabase {
|
extension CrdtDatabaseExtractExtensions on CrdtDatabase {
|
||||||
/// Gets a single data entry from the CRDT data table.
|
/// Gets a single data entry from the CRDT data table.
|
||||||
Future<T?> getSingleFromCrdtData<T extends Insertable>({
|
Future<T?> getSingleFromCrdtData<T extends Insertable>(String rowId) async {
|
||||||
required String tableName,
|
final data = await getFromCrdtData<T>([rowId]);
|
||||||
required String rowId,
|
|
||||||
}) async {
|
|
||||||
final data = await getFromCrdtData<T>(tableName: tableName, rowIds: [rowId]);
|
|
||||||
return data.firstOrNull;
|
return data.firstOrNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a list of data entries from the CRDT data table.
|
/// Gets a list of data entries from the CRDT data table.
|
||||||
Future<List<T>> getFromCrdtData<T extends Insertable>({
|
Future<Iterable<T>> getFromCrdtData<T extends Insertable>([
|
||||||
required String tableName,
|
Iterable<String>? rowIds,
|
||||||
required Iterable<String> rowIds,
|
]) async {
|
||||||
}) async {
|
final tableInfo = synchronizedTables.find<T>();
|
||||||
final tableInfo = synchronizedTables.find(tableName);
|
final tableName = tableInfo.actualTableName;
|
||||||
final crdtDataEntries = await managers.crdtDataTable
|
final crdtDataEntries = await managers.crdtDataTable.filter(
|
||||||
.filter((o) => o.tblName.equals(tableName) & o.rowId.isIn(rowIds))
|
(o) {
|
||||||
.get();
|
var condition = o.tblName.equals(tableName);
|
||||||
|
if (rowIds != null) {
|
||||||
|
condition &= o.rowId.isIn(rowIds);
|
||||||
|
}
|
||||||
|
return condition;
|
||||||
|
},
|
||||||
|
).get();
|
||||||
|
|
||||||
return crdtDataEntries.convert<T>(tableInfo);
|
return tableInfo.fromCrdtDataEntries<T>(crdtDataEntries);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on List<CrdtDataEntry> {
|
/// Extensions for [TableInfo] to convert [CrdtDataEntry] to [Insertable].
|
||||||
Future<List<T>> convert<T extends Insertable>(TableInfo tableInfo) async {
|
extension ConvertCrdtDataEntry on TableInfo {
|
||||||
final foundRowIds = map((e) => e.rowId).toSet();
|
/// Converts [CrdtDataEntry] to [Insertable].
|
||||||
|
Future<Iterable<T>> fromCrdtDataEntries<T extends Insertable>(
|
||||||
|
Iterable<CrdtDataEntry> entries,
|
||||||
|
) async {
|
||||||
|
final foundRowIds = entries.map((e) => e.rowId).toSet();
|
||||||
return [
|
return [
|
||||||
for (final rowId in foundRowIds)
|
for (final rowId in foundRowIds)
|
||||||
await tableInfo.map({
|
await map({
|
||||||
for (final c in tableInfo.$columns)
|
for (final c in $columns)
|
||||||
c.$name: firstWhere((e) => e.rowId == rowId && e.columnName == c.$name)
|
c.$name: entries
|
||||||
|
.firstWhere((e) => e.rowId == rowId && e.columnName == c.$name)
|
||||||
.rawValue
|
.rawValue
|
||||||
?.rawSqlValue,
|
?.rawSqlValue,
|
||||||
}) as T,
|
}) as T,
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class CrdtDatabase extends _$CrdtDatabase {
|
||||||
/// should ever be run on this database to avoid messing with the user schema.
|
/// should ever be run on this database to avoid messing with the user schema.
|
||||||
@override
|
@override
|
||||||
int schemaVersion = 1;
|
int schemaVersion = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// Extension methods for the [List<TableInfo>] class to find a synchronized table.
|
/// Extension methods for the [List<TableInfo>] class to find a synchronized table.
|
||||||
extension FindSynchronizedTable on List<TableInfo> {
|
extension FindSynchronizedTable on List<TableInfo> {
|
||||||
|
|
@ -55,11 +56,17 @@ extension FindSynchronizedTable on List<TableInfo> {
|
||||||
/// Gets the synchronized table information for the given table name.
|
/// Gets the synchronized table information for the given table name.
|
||||||
///
|
///
|
||||||
/// If the table is not found, an [ArgumentError] is thrown.
|
/// If the table is not found, an [ArgumentError] is thrown.
|
||||||
TableInfo find(String tableName) {
|
TableInfo find<T extends Insertable>([String? tableName]) {
|
||||||
|
if (tableName == null && T == dynamic) {
|
||||||
|
throw ArgumentError('Table name is required when T is dynamic.');
|
||||||
|
}
|
||||||
return firstWhere(
|
return firstWhere(
|
||||||
(t) => t.actualTableName == tableName,
|
(t) =>
|
||||||
|
(tableName == null && t is TableInfo<Table, T>) ||
|
||||||
|
t.actualTableName == tableName,
|
||||||
orElse: () => throw ArgumentError(
|
orElse: () => throw ArgumentError(
|
||||||
'Table "$tableName" not found in synchronized tables.',
|
'Table ${tableName != null ? '"$tableName"' : 'for type "$T"'} not '
|
||||||
|
'found in synchronized tables.',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,7 @@ void main() {
|
||||||
late Hlc testHlc;
|
late Hlc testHlc;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
createdData = TableWithEveryColumnTypeData(
|
createdData = _createTestData();
|
||||||
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;
|
(_, crdtDb, _) = database.crdtContext;
|
||||||
testHlc = Hlc.now(crdtDb.nodeId);
|
testHlc = Hlc.now(crdtDb.nodeId);
|
||||||
});
|
});
|
||||||
|
|
@ -35,7 +22,7 @@ void main() {
|
||||||
late Iterable<CrdtDataEntry> entries;
|
late Iterable<CrdtDataEntry> entries;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
entries = createdData.toCrdtDataEntry(crdtDb, testHlc).toList();
|
entries = crdtDb.convertToCrdtDataEntry(createdData, testHlc);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('then it creates an entry for each column.', () async {
|
test('then it creates an entry for each column.', () async {
|
||||||
|
|
@ -63,32 +50,69 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('then all entries have expected column names and values.', () async {
|
test('then all entries have expected column names and values.', () async {
|
||||||
final entriesMap = {
|
final mappedData =
|
||||||
for (final entry in entries) entry.columnName: entry.rawValue,
|
await database.tableWithEveryColumnType.fromCrdtDataEntries(entries);
|
||||||
};
|
expect(mappedData.single, equals(createdData));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(entriesMap['a_bool'], equals(createdData.aBool?.driftAny));
|
group('Given a changeset converted from a data object with every column type', () {
|
||||||
expect(entriesMap['a_date_time'], isNotNull);
|
late OfflineSyncCrdt crdt;
|
||||||
expect(entriesMap['a_text'], equals(createdData.aText?.driftAny));
|
late CrdtDatabase crdtDb;
|
||||||
expect(entriesMap['an_int'], equals(createdData.anInt?.driftAny));
|
late TableWithEveryColumnTypeData createdData;
|
||||||
expect(entriesMap['an_int64'], equals(createdData.anInt64?.driftAny));
|
late List<CrdtDataEntry> expectedChangeset;
|
||||||
expect(entriesMap['a_real'], equals(createdData.aReal?.driftAny));
|
late Hlc testHlc;
|
||||||
expect(entriesMap['a_blob'], equals(createdData.aBlob?.driftAny));
|
|
||||||
|
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.crdtDataTable.all().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(
|
expect(
|
||||||
entriesMap['an_int_enum'],
|
savedListObject.unwrappedValue,
|
||||||
equals(createdData.anIntEnum?.index.driftAny),
|
equals(expectedListObject.unwrappedValue),
|
||||||
);
|
);
|
||||||
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 {
|
TableWithEveryColumnTypeData _createTestData() => TableWithEveryColumnTypeData(
|
||||||
DriftAny? get driftAny => DriftAny(this);
|
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'),
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,7 @@ void main() {
|
||||||
'when getting the data from the CRDT data table of the CRDT database '
|
'when getting the data from the CRDT data table of the CRDT database '
|
||||||
'then it rebuilds the data perfectly.', () async {
|
'then it rebuilds the data perfectly.', () async {
|
||||||
final data = await crdtDb.getSingleFromCrdtData<TableWithEveryColumnTypeData>(
|
final data = await crdtDb.getSingleFromCrdtData<TableWithEveryColumnTypeData>(
|
||||||
tableName: 'table_with_every_column_type',
|
rowId,
|
||||||
rowId: rowId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(data, equals(createdData));
|
expect(data, equals(createdData));
|
||||||
|
|
@ -48,8 +47,7 @@ void main() {
|
||||||
'when trying to get the data from the CRDT data table with a non-existent row id '
|
'when trying to get the data from the CRDT data table with a non-existent row id '
|
||||||
'then it returns null.', () async {
|
'then it returns null.', () async {
|
||||||
final data = await crdtDb.getSingleFromCrdtData<TableWithEveryColumnTypeData>(
|
final data = await crdtDb.getSingleFromCrdtData<TableWithEveryColumnTypeData>(
|
||||||
tableName: 'table_with_every_column_type',
|
'non-existent-row-id',
|
||||||
rowId: 'non-existent-row-id',
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(data, isNull);
|
expect(data, isNull);
|
||||||
|
|
@ -59,8 +57,7 @@ void main() {
|
||||||
'when trying to get the data from the CRDT data table with multiple row ids '
|
'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 {
|
'then it returns the data for the existing row ids.', () async {
|
||||||
final data = await crdtDb.getFromCrdtData<TableWithEveryColumnTypeData>(
|
final data = await crdtDb.getFromCrdtData<TableWithEveryColumnTypeData>(
|
||||||
tableName: 'table_with_every_column_type',
|
[rowId, 'non-existent-row-id'],
|
||||||
rowIds: [rowId, 'non-existent-row-id'],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(data, equals([createdData]));
|
expect(data, equals([createdData]));
|
||||||
|
|
@ -70,11 +67,14 @@ void main() {
|
||||||
'when trying to get the data from the CRDT data table for a non-synchronized table '
|
'when trying to get the data from the CRDT data table for a non-synchronized table '
|
||||||
'then it throws an error.', () async {
|
'then it throws an error.', () async {
|
||||||
expect(
|
expect(
|
||||||
() => crdtDb.getFromCrdtData<TableWithEveryColumnTypeData>(
|
() => crdtDb.getSingleFromCrdtData<WithCustomTypeData>(rowId),
|
||||||
tableName: 'table_without_pk',
|
throwsA(
|
||||||
rowIds: [rowId],
|
isA<ArgumentError>().having(
|
||||||
|
(e) => e.message,
|
||||||
|
'message',
|
||||||
|
'Table for type "WithCustomTypeData" not found in synchronized tables.',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
throwsA(isA<ArgumentError>()),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ void main() {
|
||||||
targetDate: DateTime.now(),
|
targetDate: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
changeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(nodeId));
|
changeset = crdtDb.convertToCrdtDataEntry(todo, Hlc.now(nodeId));
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
|
|
@ -49,10 +49,10 @@ void main() {
|
||||||
targetDate: DateTime.now(),
|
targetDate: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(nodeId));
|
firstChangeset = crdtDb.convertToCrdtDataEntry(todo, Hlc.now(nodeId));
|
||||||
await crdt.merge(firstChangeset);
|
await crdt.merge(firstChangeset);
|
||||||
|
|
||||||
lateOldChangeset = todo.toCrdtDataEntry(crdtDb, Hlc.zero(nodeId));
|
lateOldChangeset = crdtDb.convertToCrdtDataEntry(todo, Hlc.zero(nodeId));
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
test(
|
||||||
|
|
@ -79,11 +79,11 @@ void main() {
|
||||||
targetDate: DateTime.now(),
|
targetDate: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(nodeId));
|
firstChangeset = crdtDb.convertToCrdtDataEntry(todo, Hlc.now(nodeId));
|
||||||
await crdt.merge(firstChangeset);
|
await crdt.merge(firstChangeset);
|
||||||
|
|
||||||
newerChangeset = todo
|
newerChangeset = crdtDb
|
||||||
.toCrdtDataEntry(crdtDb, Hlc.now(nodeId))
|
.convertToCrdtDataEntry(todo, Hlc.now(nodeId))
|
||||||
.where((e) => e.columnName == 'content');
|
.where((e) => e.columnName == 'content');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ void main() {
|
||||||
targetDate: DateTime.now(),
|
targetDate: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
changeset = todo.toCrdtDataEntry(crdtDb, Hlc.now(otherNodeId));
|
changeset = crdtDb.convertToCrdtDataEntry(todo, Hlc.now(otherNodeId));
|
||||||
lastHlc = Hlc.now(otherNodeId);
|
lastHlc = Hlc.now(otherNodeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -65,11 +65,11 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
lastHlc = Hlc.now(otherNodeId);
|
lastHlc = Hlc.now(otherNodeId);
|
||||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, lastHlc);
|
firstChangeset = crdtDb.convertToCrdtDataEntry(todo, lastHlc);
|
||||||
await crdt.saveChangeset(firstChangeset, [lastHlc]);
|
await crdt.saveChangeset(firstChangeset, [lastHlc]);
|
||||||
|
|
||||||
lateOldHlc = Hlc.zero(otherNodeId);
|
lateOldHlc = Hlc.zero(otherNodeId);
|
||||||
lateOldChangeset = todo.toCrdtDataEntry(crdtDb, lateOldHlc);
|
lateOldChangeset = crdtDb.convertToCrdtDataEntry(todo, lateOldHlc);
|
||||||
});
|
});
|
||||||
|
|
||||||
group('when saving a changeset with older HLC', () {
|
group('when saving a changeset with older HLC', () {
|
||||||
|
|
@ -108,12 +108,12 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
lastHlc = Hlc.now(otherNodeId);
|
lastHlc = Hlc.now(otherNodeId);
|
||||||
firstChangeset = todo.toCrdtDataEntry(crdtDb, lastHlc);
|
firstChangeset = crdtDb.convertToCrdtDataEntry(todo, lastHlc);
|
||||||
await crdt.saveChangeset(firstChangeset, [lastHlc]);
|
await crdt.saveChangeset(firstChangeset, [lastHlc]);
|
||||||
|
|
||||||
newerHlc = Hlc.now(otherNodeId);
|
newerHlc = Hlc.now(otherNodeId);
|
||||||
newerChangeset = todo
|
newerChangeset = crdtDb
|
||||||
.toCrdtDataEntry(crdtDb, newerHlc)
|
.convertToCrdtDataEntry(todo, newerHlc)
|
||||||
.where((e) => e.columnName == 'content');
|
.where((e) => e.columnName == 'content');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,14 +56,11 @@ void main() {
|
||||||
|
|
||||||
test('has the raw values inserted correctly.', () async {
|
test('has the raw values inserted correctly.', () async {
|
||||||
final crdtData = allCrdtDataEntries.getField(rowId, 'content');
|
final crdtData = allCrdtDataEntries.getField(rowId, 'content');
|
||||||
expect(
|
expect(crdtData.unwrappedValue, targetContent);
|
||||||
crdtData.rawValue?.rawSqlValue,
|
|
||||||
targetContent,
|
|
||||||
);
|
|
||||||
|
|
||||||
final targetDateCrdtData = allCrdtDataEntries.getField(rowId, 'target_date');
|
final targetDateCrdtData = allCrdtDataEntries.getField(rowId, 'target_date');
|
||||||
expect(
|
expect(
|
||||||
targetDateCrdtData.rawValue?.rawSqlValue,
|
targetDateCrdtData.unwrappedValue,
|
||||||
targetDate.toIso8600StringWithOffset(),
|
targetDate.toIso8600StringWithOffset(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -132,14 +129,11 @@ void main() {
|
||||||
|
|
||||||
test('has the raw values updated correctly.', () async {
|
test('has the raw values updated correctly.', () async {
|
||||||
final crdtData = allCrdtDataEntries.getField(rowId, 'content');
|
final crdtData = allCrdtDataEntries.getField(rowId, 'content');
|
||||||
expect(
|
expect(crdtData.unwrappedValue, targetContent);
|
||||||
crdtData.rawValue?.rawSqlValue,
|
|
||||||
targetContent,
|
|
||||||
);
|
|
||||||
|
|
||||||
final targetDateCrdtData = allCrdtDataEntries.getField(rowId, 'target_date');
|
final targetDateCrdtData = allCrdtDataEntries.getField(rowId, 'target_date');
|
||||||
expect(
|
expect(
|
||||||
targetDateCrdtData.rawValue?.rawSqlValue,
|
targetDateCrdtData.unwrappedValue,
|
||||||
targetDate.toIso8600StringWithOffset(),
|
targetDate.toIso8600StringWithOffset(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
@ -222,7 +216,7 @@ void main() {
|
||||||
|
|
||||||
test('has the deleted flag column set to true.', () async {
|
test('has the deleted flag column set to true.', () async {
|
||||||
final crdtData = allCrdtDataEntries.getField(rowId, '__crdt_is_deleted');
|
final crdtData = allCrdtDataEntries.getField(rowId, '__crdt_is_deleted');
|
||||||
expect(crdtData.rawValue?.rawSqlValue, 1);
|
expect(crdtData.unwrappedValue, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('has the deleted HLC timestamp greater than the created.', () async {
|
test('has the deleted HLC timestamp greater than the created.', () async {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue