blob: 6f64f3e099fed4c6d491e1f5f8d17f48a649af98 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert' show json;
import 'dart:typed_data';
// TODO: investigate whether we can get rid of the implementation_imports.
// ignore_for_file: implementation_imports
import 'package:lib.app.dart/logging.dart';
import 'package:sledge/sledge.dart';
import 'package:sledge/src/document/change.dart';
import 'package:sledge/src/uint8list_ops.dart';
import 'package:test/test.dart';
import 'helpers.dart';
Map<String, BaseType> flatSchema() {
return <String, BaseType>{
'someBool': Boolean(),
'someInteger': Integer()
};
}
Map<String, BaseType> schemaWithEmbeddedSchema() {
Schema schema = Schema(flatSchema());
return <String, BaseType>{'foo': schema, 'bar': LastOneWinsString()};
}
void main() {
setupLogger();
test('Create flat schema', () {
Schema(flatSchema());
});
test('Create schema with embedded schema.', () {
Schema(schemaWithEmbeddedSchema());
});
test('Serialize and deserialize flat schema', () {
Schema schema1 = Schema(flatSchema());
final json1 = json.encode(schema1);
Schema schema2 = Schema.fromJson(json.decode(json1));
final json2 = json.encode(schema2);
expect(json1, equals(json2));
});
test('Serialize and deserialize schema with embedded schema', () {
Schema schema1 = Schema(schemaWithEmbeddedSchema());
final json1 = json.encode(schema1);
Schema schema2 = Schema.fromJson(json.decode(json1));
final json2 = json.encode(schema2);
expect(json1, equals(json2));
});
test('Verify that two identical schemes result in identical hashes', () {
Map<String, BaseType> schemaDescription1 = <String, BaseType>{
'someBool': Boolean(),
'someInteger': Integer()
};
Map<String, BaseType> schemaDescription2 = <String, BaseType>{
'someInteger': Integer(),
'someBool': Boolean(),
};
Schema schema1 = Schema(schemaDescription1);
Schema schema2 = Schema(schemaDescription2);
expect(schema1.hash, equals(schema2.hash));
});
test('Verify that two different schemes result in different hashes', () {
Map<String, BaseType> schemaDescription1 = <String, BaseType>{
'someBool': Boolean(),
'someInteger': Integer()
};
Map<String, BaseType> schemaDescription2 = <String, BaseType>{
'someBool': Boolean(),
};
Map<String, BaseType> schemaDescription3 = <String, BaseType>{
'someBool_': Boolean(),
'someInteger': Integer()
};
Schema schema1 = Schema(schemaDescription1);
Schema schema2 = Schema(schemaDescription2);
Schema schema3 = Schema(schemaDescription3);
expect(schema1.hash, isNot(equals(schema2.hash)));
expect(schema1.hash, isNot(equals(schema3.hash)));
});
test('Verify that schemas can not be modified after their creation', () {
Map<String, BaseType> schemaDescription = <String, BaseType>{
'someBool': Boolean(),
};
Schema schema = Schema(schemaDescription);
Uint8List hash1 = schema.hash;
schemaDescription['foo'] = Integer();
Uint8List hash2 = schema.hash;
expect(hash1, equals(hash2));
});
test('Verify exception with invalid field names', () {
Map<String, BaseType> schemaDescription = <String, BaseType>{
'foo.bar': Boolean(),
};
expect(() {
Schema(schemaDescription);
}, throwsA(const TypeMatcher<ArgumentError>()));
});
test('Verify fieldAtPath', () {
final schema = Schema(schemaWithEmbeddedSchema());
expect(schema.fieldAtPath(null), equals(null));
expect(schema.fieldAtPath(''), equals(null));
expect(schema.fieldAtPath('.'), equals(null));
expect(schema.fieldAtPath('..'), equals(null));
expect(schema.fieldAtPath('xyz'), equals(null));
expect(schema.fieldAtPath('foo'), TypeMatcher<Schema>());
expect(schema.fieldAtPath('bar'), TypeMatcher<LastOneWinsString>());
expect(schema.fieldAtPath('foo.'), equals(null));
expect(schema.fieldAtPath('bar.'), equals(null));
expect(schema.fieldAtPath('foo.someBool'), TypeMatcher<Boolean>());
expect(schema.fieldAtPath('foo.someBool.'), equals(null));
});
test('Instantiate and initialize a Sledge document', () async {
// Create schemas.
Map<String, BaseType> schemaDescription = <String, BaseType>{
'someBool': Boolean(),
'someInteger': Integer()
};
Schema schema = Schema(schemaDescription);
Map<String, BaseType> schemaDescription2 = <String, BaseType>{
'foo': schema
};
Schema schema2 = Schema(schemaDescription2);
// Create a new Sledge document.
Sledge sledge = newSledgeForTesting();
Document doc;
await sledge.runInTransaction(() async {
doc = await sledge.getDocument(DocumentId(schema2));
});
// Read and write properties of a Sledge document.
expect(doc['foo']['someBool'].value, equals(false));
expect(doc['foo']['someInteger'].value, equals(0));
await sledge.runInTransaction(() async {
doc['foo']['someBool'].value = true;
doc['foo']['someInteger'].value = 42;
});
expect(doc['foo']['someBool'].value, equals(true));
expect(doc['foo']['someInteger'].value, equals(42));
});
test('Last One Wins basic types.', () async {
// Create schemas.
Map<String, BaseType> schemaDescription = <String, BaseType>{
'someBool': Boolean(),
'someInteger': Integer(),
'someDouble': Double(),
'someString': LastOneWinsString()
};
Schema schema = Schema(schemaDescription);
// Create a new Sledge document.
Sledge sledge = newSledgeForTesting();
Document doc;
await sledge.runInTransaction(() async {
doc = await sledge.getDocument(DocumentId(schema));
});
// Read and write properties of a Sledge document.
expect(doc['someBool'].value, equals(false));
expect(doc['someInteger'].value, equals(0));
expect(doc['someDouble'].value, equals(0.0));
expect(doc['someString'].value, equals(''));
await sledge.runInTransaction(() async {
doc['someBool'].value = true;
doc['someInteger'].value = 42;
doc['someDouble'].value = 10.5;
doc['someString'].value = 'abacaba';
});
expect(doc['someBool'].value, equals(true));
expect(doc['someInteger'].value, equals(42));
expect(doc['someDouble'].value, equals(10.5));
expect(doc['someString'].value, equals('abacaba'));
});
test('Integration of PosNegCounter with Sledge', () async {
// Create Schema.
Map<String, BaseType> schemaDescription = <String, BaseType>{
'cnt': IntCounter(),
'cnt_d': DoubleCounter()
};
Schema schema = Schema(schemaDescription);
// Create a new Sledge document.
Sledge sledge = newSledgeForTesting();
Document doc;
await sledge.runInTransaction(() async {
doc = await sledge.getDocument(DocumentId(schema));
});
// Modify and get value of PosNegCounter.
expect(doc['cnt'].value, equals(0));
expect(doc['cnt_d'].value, equals(0.0));
await sledge.runInTransaction(() async {
doc['cnt'].add(5);
});
expect(doc['cnt'].value, equals(5));
await sledge.runInTransaction(() async {
doc['cnt'].add(-3);
doc['cnt_d'].add(-5.2);
});
expect(doc['cnt'].value, equals(2));
expect(doc['cnt_d'].value, equals(-5.2));
await sledge.runInTransaction(() async {
doc['cnt_d'].add(3.12);
});
expect(doc['cnt_d'].value, equals(-2.08));
});
// TODO: add tests for BytelistMap and BytelistSet.
test('Integration of OrderedList with Sledge', () async {
Map<String, BaseType> schemaDescription = <String, BaseType>{
'list': OrderedList()
};
Schema schema = Schema(schemaDescription);
// Create a new Sledge document.
Sledge sledge = newSledgeForTesting();
Document doc;
await sledge.runInTransaction(() async {
doc = await sledge.getDocument(DocumentId(schema));
});
// Apply modifications to OrderedList.
expect(doc['list'].toList(), equals([]));
await sledge.runInTransaction(() async {
doc['list'].insert(0, Uint8List.fromList([1]));
});
expect(doc['list'].toList().length, equals(1));
expect(doc['list'][0].toList(), equals([1]));
await sledge.runInTransaction(() async {
doc['list'].insert(1, Uint8List.fromList([3]));
doc['list'].insert(1, Uint8List.fromList([2]));
});
expect(doc['list'].toList().length, equals(3));
expect(doc['list'][0].toList(), equals([1]));
expect(doc['list'][1].toList(), equals([2]));
expect(doc['list'][2].toList(), equals([3]));
});
test('get and apply changes', () async {
// Create Schema.
Map<String, BaseType> schemaDescription = <String, BaseType>{
'name': LastOneWinsString(),
'number': Integer(),
'cnt': IntCounter()
};
Schema schema = Schema(schemaDescription);
// Create two Sledge documents
Sledge sledgeA = newSledgeForTesting(), sledgeB = newSledgeForTesting();
Document docA, docB;
await sledgeA.runInTransaction(() async {
docA = await sledgeA.getDocument(DocumentId(schema));
});
await sledgeB.runInTransaction(() async {
docB = await sledgeB.getDocument(DocumentId(schema));
});
Change c1;
await sledgeA.runInTransaction(() async {
docA
..['name'].value = 'value + counter'
..['number'].value = 5
..['cnt'].add(1);
c1 = docA.getChange();
});
docB.applyChange(c1);
expect(docB['name'].value, equals('value + counter'));
expect(docB['number'].value, equals(5));
expect(docB['cnt'].value, equals(1));
});
test('put large list into set', () async {
// Create Schema.
Map<String, BaseType> schemaDescription = <String, BaseType>{
'names': BytelistSet(),
};
Schema schema = Schema(schemaDescription);
// Create two Sledge documents
Sledge sledgeA = newSledgeForTesting(), sledgeB = newSledgeForTesting();
Document docA, docB;
await sledgeA.runInTransaction(() async {
docA = await sledgeA.getDocument(DocumentId(schema));
});
await sledgeB.runInTransaction(() async {
docB = await sledgeB.getDocument(DocumentId(schema));
});
final largeList = randomUint8List(10000);
Change c1;
await sledgeA.runInTransaction(() async {
docA['names'].add(largeList);
c1 = docA.getChange();
});
docB.applyChange(c1);
expect(docB['names'].single, equals(largeList));
});
group('Rollback', () {
test('rollback LastOneWinsValue', () async {
// Create schemas.
Map<String, BaseType> schemaDescription = <String, BaseType>{
'someBool': Boolean(),
'someInteger': Integer()
};
Schema schema = Schema(schemaDescription);
// Create a new Sledge document.
SledgeForTesting sledge = newSledgeForTesting();
Document doc;
await sledge.runInTransaction(() async {
doc = await sledge.getDocument(DocumentId(schema));
});
// Read and write properties of a Sledge document.
bool transactionSucceed = await sledge.runInTransaction(() async {
doc['someInteger'].value = 14;
});
expect(transactionSucceed, true);
expect(doc['someInteger'].value, equals(14));
// Test case when transaction fails.
transactionSucceed = await sledge.runInTransaction(() async {
doc['someBool'].value = true;
doc['someInteger'].value = 42;
sledge.abortAndRollback();
});
expect(transactionSucceed, false);
expect(doc['someBool'].value, equals(false));
expect(doc['someInteger'].value, equals(14));
// Check that after failed transaction we can get successful one.
transactionSucceed = await sledge.runInTransaction(() async {
doc['someInteger'].value = 8;
});
expect(transactionSucceed, true);
expect(doc['someInteger'].value, equals(8));
});
test('rollback BytelistMap', () async {
// Create schemas.
Map<String, BaseType> schemaDescription = <String, BaseType>{
'map': BytelistMap(),
};
Schema schema = Schema(schemaDescription);
// Create a new Sledge document.
SledgeForTesting sledge = newSledgeForTesting();
Document doc;
await sledge.runInTransaction(() async {
doc = await sledge.getDocument(DocumentId(schema));
});
// Read and write properties of a Sledge document.
bool transactionSucceed = await sledge.runInTransaction(() async {
doc['map']['a'] = Uint8List.fromList([1, 2, 3]);
});
expect(transactionSucceed, true);
expect(doc['map'].length, equals(1));
// Test case when transaction fails.
transactionSucceed = await sledge.runInTransaction(() async {
doc['map']['a'] = Uint8List.fromList([4]);
doc['map']['foo'] = Uint8List.fromList([1, 3]);
sledge.abortAndRollback();
});
expect(transactionSucceed, false);
expect(doc['map'].length, equals(1));
expect(doc['map']['a'], equals([1, 2, 3]));
transactionSucceed = await sledge.runInTransaction(() async {
doc['map']['foo'] = Uint8List.fromList([1, 3]);
});
expect(transactionSucceed, true);
expect(doc['map'].length, equals(2));
expect(doc['map']['a'], equals([1, 2, 3]));
expect(doc['map']['foo'], equals([1, 3]));
transactionSucceed = await sledge.runInTransaction(() async {
doc['map']['a'] = Uint8List.fromList([3, 4]);
});
expect(transactionSucceed, true);
expect(doc['map']['a'], equals([3, 4]));
});
});
}