mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-08-03 04:06:11 -04:00
flutter unit/integration tests
This commit is contained in:
parent
d586748333
commit
6a8c0830d2
18 changed files with 1357 additions and 520 deletions
|
@ -2,39 +2,72 @@ import 'package:flutter_test/flutter_test.dart';
|
|||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'fixtures.dart';
|
||||
import 'test_crypto.dart';
|
||||
import 'test_routing_context.dart';
|
||||
import 'test_table_db.dart';
|
||||
import 'test_veilid_config.dart';
|
||||
import 'test_dht.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
final fixture = DefaultFixture();
|
||||
|
||||
group('VeilidConfig', () {
|
||||
final fixture = DefaultFixture();
|
||||
setUp(fixture.setUp);
|
||||
tearDown(fixture.tearDown);
|
||||
|
||||
test('test VeilidConfig defaults', testVeilidConfigDefaults);
|
||||
group('Unstarted Tests', () {
|
||||
test('veilid config defaults', testVeilidConfigDefaults);
|
||||
});
|
||||
|
||||
// group('end-to-end test', () {
|
||||
// testWidgets('tap on the floating action button, verify counter',
|
||||
// (tester) async {
|
||||
// // Load app widget.
|
||||
// await tester.pumpWidget(const MyApp());
|
||||
group('Started Tests', () {
|
||||
setUpAll(fixture.setUp);
|
||||
tearDownAll(fixture.tearDown);
|
||||
|
||||
// // Verify the counter starts at 0.
|
||||
// expect(find.text('0'), findsOneWidget);
|
||||
group('Crypto Tests', () {
|
||||
test('best cryptosystem', testBestCryptoSystem);
|
||||
test('get cryptosystem', testGetCryptoSystem);
|
||||
test('get cryptosystem invalid', testGetCryptoSystemInvalid);
|
||||
test('hash and verify password', testHashAndVerifyPassword);
|
||||
});
|
||||
|
||||
// // Finds the floating action button to tap on.
|
||||
// final fab = find.byKey(const Key('increment'));
|
||||
group('Table DB Tests', () {
|
||||
test('delete table db nonexistent', testDeleteTableDbNonExistent);
|
||||
test('open delete table db', testOpenDeleteTableDb);
|
||||
test('open twice table db', testOpenTwiceTableDb);
|
||||
test('open twice table db store load', testOpenTwiceTableDbStoreLoad);
|
||||
test('open twice table db store delete load',
|
||||
testOpenTwiceTableDbStoreDeleteLoad);
|
||||
test('resize table db', testResizeTableDb);
|
||||
});
|
||||
|
||||
// // Emulate a tap on the floating action button.
|
||||
// await tester.tap(fab);
|
||||
group('Attached Tests', () {
|
||||
setUpAll(fixture.attach);
|
||||
tearDownAll(fixture.detach);
|
||||
|
||||
// // Trigger a frame.
|
||||
// await tester.pumpAndSettle();
|
||||
group('Routing Contexts', () {
|
||||
test('routing contexts', testRoutingContexts);
|
||||
test('app message loopback',
|
||||
() => testAppMessageLoopback(fixture.updateStream));
|
||||
test('app call loopback',
|
||||
() => testAppCallLoopback(fixture.updateStream));
|
||||
test('app message loopback big packets',
|
||||
() => testAppMessageLoopbackBigPackets(fixture.updateStream));
|
||||
test('app call loopback big packets',
|
||||
() => testAppCallLoopbackBigPackets(fixture.updateStream));
|
||||
});
|
||||
|
||||
// // Verify the counter increments by 1.
|
||||
// expect(find.text('1'), findsOneWidget);
|
||||
// });
|
||||
// });
|
||||
group('Veilid DHT', () {
|
||||
test('get dht value unopened', testGetDHTValueUnopened);
|
||||
test('open dht record nonexistent no writer',
|
||||
testOpenDHTRecordNonexistentNoWriter);
|
||||
test('close dht record nonexistent', testCloseDHTRecordNonexistent);
|
||||
test('delete dht record nonexistent', testDeleteDHTRecordNonexistent);
|
||||
test(
|
||||
'create delete dht record simple', testCreateDeleteDHTRecordSimple);
|
||||
test('create delete dht record no close',
|
||||
testCreateDeleteDHTRecordNoClose);
|
||||
test('get dht value nonexistent', testGetDHTValueNonexistent);
|
||||
test('set get dht value', testSetGetDHTValue);
|
||||
test('open writer dht value', testOpenWriterDHTValue);
|
||||
test('inspect dht record', testInspectDHTRecord);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,15 +7,17 @@ import 'package:veilid/veilid.dart';
|
|||
class DefaultFixture {
|
||||
DefaultFixture();
|
||||
|
||||
StreamSubscription<VeilidUpdate>? _updateSubscription;
|
||||
Stream<VeilidUpdate>? _updateStream;
|
||||
StreamSubscription<VeilidUpdate>? _veilidUpdateSubscription;
|
||||
Stream<VeilidUpdate>? _veilidUpdateStream;
|
||||
final StreamController<VeilidUpdate> _updateStreamController =
|
||||
StreamController.broadcast();
|
||||
|
||||
static final _fixtureMutex = Mutex();
|
||||
|
||||
Future<void> setUp() async {
|
||||
await _fixtureMutex.acquire();
|
||||
|
||||
assert(_updateStream == null, 'should not set up fixture twice');
|
||||
assert(_veilidUpdateStream == null, 'should not set up fixture twice');
|
||||
|
||||
final Map<String, dynamic> platformConfigJson;
|
||||
if (kIsWeb) {
|
||||
|
@ -53,16 +55,26 @@ class DefaultFixture {
|
|||
}
|
||||
Veilid.instance.initializeVeilidCore(platformConfigJson);
|
||||
|
||||
final defaultConfig = await getDefaultVeilidConfig(
|
||||
isWeb: kIsWeb, programName: 'Veilid Tests');
|
||||
var config = await getDefaultVeilidConfig(
|
||||
isWeb: kIsWeb,
|
||||
programName: 'Veilid Tests',
|
||||
// ignore: avoid_redundant_argument_values, do_not_use_environment
|
||||
bootstrap: const String.fromEnvironment('BOOTSTRAP'),
|
||||
// ignore: avoid_redundant_argument_values, do_not_use_environment
|
||||
networkKeyPassword: const String.fromEnvironment('NETWORK_KEY'),
|
||||
);
|
||||
|
||||
final updateStream =
|
||||
_updateStream = await Veilid.instance.startupVeilidCore(defaultConfig);
|
||||
if (_updateStream == null) {
|
||||
throw Exception('failed to start up veilid core');
|
||||
}
|
||||
config =
|
||||
config.copyWith(tableStore: config.tableStore.copyWith(delete: true));
|
||||
config = config.copyWith(
|
||||
protectedStore: config.protectedStore.copyWith(delete: true));
|
||||
config =
|
||||
config.copyWith(blockStore: config.blockStore.copyWith(delete: true));
|
||||
|
||||
_updateSubscription = updateStream.listen((update) {
|
||||
final us =
|
||||
_veilidUpdateStream = await Veilid.instance.startupVeilidCore(config);
|
||||
|
||||
_veilidUpdateSubscription = us.listen((update) {
|
||||
if (update is VeilidLog) {
|
||||
} else if (update is VeilidUpdateAttachment) {
|
||||
} else if (update is VeilidUpdateConfig) {
|
||||
|
@ -70,21 +82,56 @@ class DefaultFixture {
|
|||
} else if (update is VeilidAppMessage) {
|
||||
} else if (update is VeilidAppCall) {
|
||||
} else if (update is VeilidUpdateValueChange) {
|
||||
} else if (update is VeilidUpdateRouteChange) {
|
||||
} else {
|
||||
throw Exception('unexpected update: $update');
|
||||
}
|
||||
_updateStreamController.sink.add(update);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> tearDown() async {
|
||||
assert(_updateStream != null, 'should not tearDown without setUp');
|
||||
Stream<VeilidUpdate> get updateStream => _updateStreamController.stream;
|
||||
|
||||
final cancelFut = _updateSubscription?.cancel();
|
||||
Future<void> attach() async {
|
||||
await Veilid.instance.attach();
|
||||
|
||||
// Wait for attached state
|
||||
while (true) {
|
||||
final state = await Veilid.instance.getVeilidState();
|
||||
var done = false;
|
||||
if (state.attachment.publicInternetReady) {
|
||||
switch (state.attachment.state) {
|
||||
case AttachmentState.detached:
|
||||
break;
|
||||
case AttachmentState.attaching:
|
||||
break;
|
||||
case AttachmentState.detaching:
|
||||
break;
|
||||
default:
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> detach() async {
|
||||
await Veilid.instance.detach();
|
||||
}
|
||||
|
||||
Future<void> tearDown() async {
|
||||
assert(_veilidUpdateStream != null, 'should not tearDown without setUp');
|
||||
|
||||
final cancelFut = _veilidUpdateSubscription?.cancel();
|
||||
await Veilid.instance.shutdownVeilidCore();
|
||||
await cancelFut;
|
||||
|
||||
_updateSubscription = null;
|
||||
_updateStream = null;
|
||||
_veilidUpdateSubscription = null;
|
||||
_veilidUpdateStream = null;
|
||||
|
||||
_fixtureMutex.release();
|
||||
}
|
||||
|
|
34
veilid-flutter/example/integration_test/test_crypto.dart
Normal file
34
veilid-flutter/example/integration_test/test_crypto.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
Future<void> testBestCryptoSystem() async {
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
expect(await cs.defaultSaltLength(), equals(16));
|
||||
}
|
||||
|
||||
Future<void> testGetCryptoSystem() async {
|
||||
final cs = await Veilid.instance.getCryptoSystem(cryptoKindVLD0);
|
||||
expect(await cs.defaultSaltLength(), equals(16));
|
||||
}
|
||||
|
||||
Future<void> testGetCryptoSystemInvalid() async {
|
||||
await expectLater(
|
||||
() async => await Veilid.instance.getCryptoSystem(cryptoKindNONE),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
}
|
||||
|
||||
Future<void> testHashAndVerifyPassword() async {
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
final nonce = await cs.randomNonce();
|
||||
final salt = nonce.decode();
|
||||
|
||||
// Password match
|
||||
final phash = await cs.hashPassword(utf8.encode("abc123"), salt);
|
||||
expect(await cs.verifyPassword(utf8.encode("abc123"), phash), isTrue);
|
||||
|
||||
// Password mismatch
|
||||
await cs.hashPassword(utf8.encode("abc1234"), salt);
|
||||
expect(await cs.verifyPassword(utf8.encode("abc1235"), phash), isFalse);
|
||||
}
|
250
veilid-flutter/example/integration_test/test_dht.dart
Normal file
250
veilid-flutter/example/integration_test/test_dht.dart
Normal file
|
@ -0,0 +1,250 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
final bogusKey =
|
||||
TypedKey.fromString("VLD0:qD10lHHPD1_Qr23_Qy-1JnxTht12eaWwENVG_m2v7II");
|
||||
|
||||
Future<void> testGetDHTValueUnopened() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(
|
||||
() async => await rc.getDHTValue(bogusKey, 0, forceRefresh: false),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testOpenDHTRecordNonexistentNoWriter() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(() async => await rc.openDHTRecord(bogusKey),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCloseDHTRecordNonexistent() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(() async => await rc.closeDHTRecord(bogusKey),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testDeleteDHTRecordNonexistent() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
await expectLater(() async => await rc.deleteDHTRecord(bogusKey),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCreateDeleteDHTRecordSimple() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
await rc.closeDHTRecord(rec.key);
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testCreateDeleteDHTRecordNoClose() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testGetDHTValueNonexistent() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 1));
|
||||
expect(await rc.getDHTValue(rec.key, 0), isNull);
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testSetGetDHTValue() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
final rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2));
|
||||
expect(await rc.setDHTValue(rec.key, 0, utf8.encode("BLAH BLAH BLAH")),
|
||||
isNull);
|
||||
final vd2 = await rc.getDHTValue(rec.key, 0);
|
||||
expect(vd2, isNotNull);
|
||||
|
||||
final vd3 = await rc.getDHTValue(rec.key, 0, forceRefresh: true);
|
||||
expect(vd3, isNotNull);
|
||||
|
||||
final vd4 = await rc.getDHTValue(rec.key, 1);
|
||||
expect(vd4, isNull);
|
||||
|
||||
expect(vd2, equals(vd3));
|
||||
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testOpenWriterDHTValue() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
var rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2));
|
||||
final key = rec.key;
|
||||
final owner = rec.owner;
|
||||
final secret = rec.ownerSecret!;
|
||||
|
||||
final cs = await Veilid.instance.getCryptoSystem(rec.key.kind);
|
||||
expect(await cs.validateKeyPair(owner, secret), isTrue);
|
||||
final otherKeyPair = await cs.generateKeyPair();
|
||||
|
||||
final va = utf8.encode("Qwertyuiop Asdfghjkl Zxcvbnm");
|
||||
final vb = utf8.encode("1234567890");
|
||||
final vc = utf8.encode("!@#\$%^&*()");
|
||||
|
||||
// Test subkey writes
|
||||
expect(await rc.setDHTValue(key, 1, va), isNull);
|
||||
|
||||
var vdtemp = await rc.getDHTValue(key, 1);
|
||||
expect(vdtemp, isNotNull);
|
||||
expect(vdtemp!.data, equals(va));
|
||||
expect(vdtemp.seq, equals(0));
|
||||
expect(vdtemp.writer, equals(owner));
|
||||
|
||||
expect(await rc.getDHTValue(key, 0), isNull);
|
||||
|
||||
expect(await rc.setDHTValue(key, 0, vb), isNull);
|
||||
|
||||
expect(
|
||||
await rc.getDHTValue(key, 0, forceRefresh: true),
|
||||
equals(ValueData(
|
||||
data: vb,
|
||||
seq: 0,
|
||||
writer: owner,
|
||||
)));
|
||||
|
||||
expect(
|
||||
await rc.getDHTValue(key, 1, forceRefresh: true),
|
||||
equals(ValueData(
|
||||
data: va,
|
||||
seq: 0,
|
||||
writer: owner,
|
||||
)));
|
||||
|
||||
// Equal value should not trigger sequence number update
|
||||
expect(await rc.setDHTValue(key, 1, va), isNull);
|
||||
|
||||
// Different value should trigger sequence number update
|
||||
expect(await rc.setDHTValue(key, 1, vb), isNull);
|
||||
|
||||
// Now that we initialized some subkeys
|
||||
// and verified they stored correctly
|
||||
// Delete things locally and reopen and see if we can write
|
||||
// with the same writer key
|
||||
//
|
||||
|
||||
await rc.closeDHTRecord(key);
|
||||
await rc.deleteDHTRecord(key);
|
||||
|
||||
rec = await rc.openDHTRecord(key,
|
||||
writer: KeyPair(key: owner, secret: secret));
|
||||
expect(rec, isNotNull);
|
||||
expect(rec.key, equals(key));
|
||||
expect(rec.owner, equals(owner));
|
||||
expect(rec.ownerSecret, equals(secret));
|
||||
expect(rec.schema, isA<DHTSchemaDFLT>());
|
||||
expect(rec.schema.oCnt, equals(2));
|
||||
|
||||
// Verify subkey 1 can be set before it is get but newer is available online
|
||||
vdtemp = await rc.setDHTValue(key, 1, vc);
|
||||
expect(vdtemp, isNotNull);
|
||||
expect(vdtemp!.data, equals(vb));
|
||||
expect(vdtemp.seq, equals(1));
|
||||
expect(vdtemp.writer, equals(owner));
|
||||
|
||||
// Verify subkey 1 can be set a second time and it updates because seq is newer
|
||||
expect(await rc.setDHTValue(key, 1, vc), isNull);
|
||||
|
||||
// Verify the network got the subkey update with a refresh check
|
||||
vdtemp = await rc.getDHTValue(key, 1, forceRefresh: true);
|
||||
expect(vdtemp, isNotNull);
|
||||
expect(vdtemp!.data, equals(vc));
|
||||
expect(vdtemp.seq, equals(2));
|
||||
expect(vdtemp.writer, equals(owner));
|
||||
|
||||
// Delete things locally and reopen and see if we can write
|
||||
// with a different writer key (should fail)
|
||||
await rc.closeDHTRecord(key);
|
||||
await rc.deleteDHTRecord(key);
|
||||
|
||||
rec = await rc.openDHTRecord(key, writer: otherKeyPair);
|
||||
expect(rec, isNotNull);
|
||||
expect(rec.key, equals(key));
|
||||
expect(rec.owner, equals(owner));
|
||||
expect(rec.ownerSecret, isNull);
|
||||
expect(rec.schema, isA<DHTSchemaDFLT>());
|
||||
expect(rec.schema.oCnt, equals(2));
|
||||
|
||||
// Verify subkey 1 can NOT be set because we have the wrong writer
|
||||
await expectLater(() async => await rc.setDHTValue(key, 1, va),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
|
||||
// Verify subkey 0 can NOT be set because we have the wrong writer
|
||||
await expectLater(() async => await rc.setDHTValue(key, 0, va),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
|
||||
// Verify subkey 0 can be set because override with the right writer
|
||||
expect(
|
||||
await rc.setDHTValue(key, 0, va,
|
||||
writer: KeyPair(key: owner, secret: secret)),
|
||||
isNull);
|
||||
|
||||
// Clean up
|
||||
await rc.closeDHTRecord(key);
|
||||
await rc.deleteDHTRecord(key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testInspectDHTRecord() async {
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
var rec = await rc.createDHTRecord(const DHTSchema.dflt(oCnt: 2));
|
||||
|
||||
expect(await rc.setDHTValue(rec.key, 0, utf8.encode("BLAH BLAH BLAH")),
|
||||
isNull);
|
||||
|
||||
final rr = await rc.inspectDHTRecord(rec.key);
|
||||
expect(rr.subkeys, equals([ValueSubkeyRange.make(0, 1)]));
|
||||
expect(rr.localSeqs, equals([0, 0xFFFFFFFF]));
|
||||
expect(rr.networkSeqs, equals([]));
|
||||
|
||||
final rr2 =
|
||||
await rc.inspectDHTRecord(rec.key, scope: DHTReportScope.syncGet);
|
||||
expect(rr2.subkeys, equals([ValueSubkeyRange.make(0, 1)]));
|
||||
expect(rr2.localSeqs, equals([0, 0xFFFFFFFF]));
|
||||
expect(rr2.networkSeqs, equals([0, 0xFFFFFFFF]));
|
||||
|
||||
await rc.closeDHTRecord(rec.key);
|
||||
await rc.deleteDHTRecord(rec.key);
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
Future<void> testRoutingContexts() async {
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
rc.close();
|
||||
}
|
||||
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withDefaultSafety();
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withSequencing(Sequencing.ensureOrdered);
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withSafety(const SafetySelectionSafe(
|
||||
safetySpec: SafetySpec(
|
||||
hopCount: 2,
|
||||
stability: Stability.lowLatency,
|
||||
sequencing: Sequencing.noPreference)));
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
{
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
final rcp = rc.withSafety(
|
||||
const SafetySelectionUnsafe(sequencing: Sequencing.preferOrdered));
|
||||
rcp.close();
|
||||
rc.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppMessageLoopback(Stream<VeilidUpdate> updateStream) async {
|
||||
final appMessageQueue = StreamController<VeilidAppMessage>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppMessage) {
|
||||
appMessageQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance.newPrivateRoute();
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
// send an app message to our own private route
|
||||
final message = utf8.encode("abcd1234");
|
||||
await rc.appMessage(prr, message);
|
||||
|
||||
// we should get the same message back
|
||||
final update = await appMessageQueue.stream.first;
|
||||
expect(update.message, equals(message));
|
||||
expect(update.routeId, isNotNull);
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppCallLoopback(Stream<VeilidUpdate> updateStream) async {
|
||||
final appCallQueue = StreamController<VeilidAppCall>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppCall) {
|
||||
appCallQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance.newPrivateRoute();
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
// send an app call to our own private route
|
||||
final message = utf8.encode("abcd1234");
|
||||
final appCallFuture = rc.appCall(prr, message);
|
||||
|
||||
// we should get the same call back
|
||||
final update = await appCallQueue.stream.first;
|
||||
final appcallid = update.callId;
|
||||
|
||||
expect(update.message, equals(message));
|
||||
expect(update.routeId, isNotNull);
|
||||
|
||||
// now we reply to the request
|
||||
final reply = utf8.encode("qwer5678");
|
||||
await Veilid.instance.appCallReply(appcallid, reply);
|
||||
|
||||
// now we should get the reply from the call
|
||||
final result = await appCallFuture;
|
||||
expect(result, equals(reply));
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppMessageLoopbackBigPackets(
|
||||
Stream<VeilidUpdate> updateStream) async {
|
||||
final appMessageQueue = StreamController<VeilidAppMessage>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppMessage) {
|
||||
appMessageQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
|
||||
final sentMessages = <String>{};
|
||||
final random = Random.secure();
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = await Veilid.instance.routingContext();
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance.newPrivateRoute();
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
for (var i = 0; i < 5; i++) {
|
||||
// send an app message to our own private route
|
||||
final message = await cs.randomBytes(random.nextInt(32768));
|
||||
await rc.appMessage(prr, message);
|
||||
sentMessages.add(base64Url.encode(message));
|
||||
}
|
||||
|
||||
final appMessageQueueIterator =
|
||||
StreamIterator(appMessageQueue.stream);
|
||||
|
||||
// we should get the same messages back
|
||||
for (var i = 0; i < sentMessages.length; i++) {
|
||||
if (await appMessageQueueIterator.moveNext()) {
|
||||
final update = appMessageQueueIterator.current;
|
||||
expect(sentMessages.contains(base64Url.encode(update.message)),
|
||||
isTrue);
|
||||
} else {
|
||||
fail("not enough messages in the queue");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> testAppCallLoopbackBigPackets(
|
||||
Stream<VeilidUpdate> updateStream) async {
|
||||
final appCallQueue = StreamController<VeilidAppCall>();
|
||||
final appMessageSubscription = updateStream.listen((update) {
|
||||
if (update is VeilidAppCall) {
|
||||
appCallQueue.sink.add(update);
|
||||
}
|
||||
});
|
||||
final appCallQueueHandler = () async {
|
||||
await for (final update in appCallQueue.stream) {
|
||||
await Veilid.instance.appCallReply(update.callId, update.message);
|
||||
}
|
||||
}();
|
||||
|
||||
final sentMessages = <String>{};
|
||||
final random = Random.secure();
|
||||
final cs = await Veilid.instance.bestCryptoSystem();
|
||||
|
||||
try {
|
||||
await Veilid.instance.debug("purge routes");
|
||||
|
||||
// make a routing context that uses a safety route
|
||||
final rc = (await Veilid.instance.routingContext())
|
||||
.withSequencing(Sequencing.ensureOrdered, closeSelf: true);
|
||||
try {
|
||||
// make a new local private route
|
||||
final prl = await Veilid.instance
|
||||
.newCustomPrivateRoute(Stability.reliable, Sequencing.ensureOrdered);
|
||||
try {
|
||||
// import it as a remote route as well so we can send to it
|
||||
final prr = await Veilid.instance.importRemotePrivateRoute(prl.blob);
|
||||
try {
|
||||
for (var i = 0; i < 5; i++) {
|
||||
// send an app message to our own private route
|
||||
final message = await cs.randomBytes(random.nextInt(32768));
|
||||
final outmessage = await rc.appCall(prr, message);
|
||||
expect(message, equals(outmessage));
|
||||
}
|
||||
|
||||
final appMessageQueueIterator = StreamIterator(appCallQueue.stream);
|
||||
|
||||
// we should get the same messages back
|
||||
for (var i = 0; i < sentMessages.length; i++) {
|
||||
if (await appMessageQueueIterator.moveNext()) {
|
||||
final update = appMessageQueueIterator.current;
|
||||
expect(sentMessages.contains(base64Url.encode(update.message)),
|
||||
isTrue);
|
||||
} else {
|
||||
fail("not enough messages in the queue");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prr);
|
||||
}
|
||||
} finally {
|
||||
await Veilid.instance.releasePrivateRoute(prl.routeId);
|
||||
}
|
||||
} finally {
|
||||
rc.close();
|
||||
}
|
||||
} finally {
|
||||
await appMessageSubscription.cancel();
|
||||
}
|
||||
await appCallQueue.close();
|
||||
await appCallQueueHandler;
|
||||
}
|
133
veilid-flutter/example/integration_test/test_table_db.dart
Normal file
133
veilid-flutter/example/integration_test/test_table_db.dart
Normal file
|
@ -0,0 +1,133 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:veilid/veilid.dart';
|
||||
|
||||
const testDb = "__dart_test_db";
|
||||
const testNonexistentDb = "__dart_test_nonexistent_db";
|
||||
|
||||
Future<void> testDeleteTableDbNonExistent() async {
|
||||
expect(await Veilid.instance.deleteTableDB(testNonexistentDb), isFalse);
|
||||
}
|
||||
|
||||
Future<void> testOpenDeleteTableDb() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
expect(() async => await Veilid.instance.deleteTableDB(testDb),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testOpenTwiceTableDb() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
|
||||
// delete should fail because open
|
||||
await expectLater(() async => await Veilid.instance.deleteTableDB(testDb),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
tdb.close();
|
||||
// delete should fail because open
|
||||
await expectLater(() async => await Veilid.instance.deleteTableDB(testDb),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
tdb2.close();
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testOpenTwiceTableDbStoreLoad() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// store into first db copy
|
||||
await tdb.store(0, utf8.encode("asdf"), utf8.encode("1234"));
|
||||
// load from second db copy
|
||||
expect(
|
||||
await tdb2.load(0, utf8.encode("asdf")), equals(utf8.encode("1234")));
|
||||
} finally {
|
||||
tdb2.close();
|
||||
}
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testOpenTwiceTableDbStoreDeleteLoad() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// store into first db copy
|
||||
await tdb.store(0, utf8.encode("asdf"), utf8.encode("1234"));
|
||||
// delete from second db copy and clean up
|
||||
await tdb2.delete(0, utf8.encode("asdf"));
|
||||
} finally {
|
||||
tdb2.close();
|
||||
}
|
||||
// load from first db copy
|
||||
expect(await tdb.load(0, utf8.encode("asdf")), isNull);
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
||||
|
||||
Future<void> testResizeTableDb() async {
|
||||
// delete test db if it exists
|
||||
await Veilid.instance.deleteTableDB(testDb);
|
||||
|
||||
final tdb = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// reopen the db with more columns should fail if it is already open
|
||||
await expectLater(() async => await Veilid.instance.openTableDB(testDb, 2),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
} finally {
|
||||
tdb.close();
|
||||
}
|
||||
|
||||
final tdb2 = await Veilid.instance.openTableDB(testDb, 2);
|
||||
try {
|
||||
// write something to second column
|
||||
await tdb2.store(1, utf8.encode("qwer"), utf8.encode("5678"));
|
||||
|
||||
// reopen the db with fewer columns
|
||||
final tdb3 = await Veilid.instance.openTableDB(testDb, 1);
|
||||
try {
|
||||
// Should fail access to second column
|
||||
await expectLater(() async => await tdb3.load(1, utf8.encode("qwer")),
|
||||
throwsA(isA<VeilidAPIException>()));
|
||||
|
||||
// Should succeed with access to second column
|
||||
expect(
|
||||
await tdb2.load(1, utf8.encode("qwer")), equals(utf8.encode("5678")));
|
||||
} finally {
|
||||
tdb3.close();
|
||||
}
|
||||
} finally {
|
||||
tdb2.close();
|
||||
}
|
||||
|
||||
// delete should now succeed
|
||||
expect(await Veilid.instance.deleteTableDB(testDb), isTrue);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue