Merge branch 'train-days' into integration
# Conflicts: # apps/train/halkyon_train_flutter/lib/src/app/train_paths.dart # docs/adr/0030-halkyon-train.md
This commit is contained in:
commit
81adeb91cc
2 changed files with 25705 additions and 410 deletions
|
|
@ -0,0 +1,116 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:halkyon_train_server/src/generated/protocol.dart';
|
||||
import 'package:serverpod/serverpod.dart';
|
||||
import 'package:serverpod_offline_sync_server/serverpod_offline_sync_server.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../test_tools/serverpod_test_tools.dart';
|
||||
|
||||
void main() {
|
||||
withServerpod(
|
||||
"A person's weights (ADR 0030, D64)",
|
||||
(sessionBuilder, endpoints) {
|
||||
late Session session;
|
||||
late UuidValue alice;
|
||||
late UuidValue bob;
|
||||
final sealed = ByteData.sublistView(Uint8List.fromList([1, 2, 3]));
|
||||
|
||||
setUp(() {
|
||||
// As person_deletion_test: the test pod is built without the
|
||||
// generated Serverpod class, which sets up sync for the server.
|
||||
Serverpod.instance.initializeOfflineSync(syncTables: syncTables);
|
||||
session = sessionBuilder.build();
|
||||
alice = const Uuid().v7obj();
|
||||
bob = const Uuid().v7obj();
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
for (final space in [alice, bob]) {
|
||||
await session.db.unsafeExecute(
|
||||
'DELETE FROM "offline_sync_spaces" WHERE "uuidSpaceId" = @space',
|
||||
parameters: QueryParameters.named({'space': space.uuid}),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
OfflineSyncDatabase sync() => _syncDatabases[session]!;
|
||||
|
||||
/// Writes a weight in [person]'s own space, as their device's sync
|
||||
/// does.
|
||||
Future<BodyWeight> weightOf(UuidValue person) =>
|
||||
sync().transactionForUser(
|
||||
person,
|
||||
(transaction) => BodyWeight.db.insertRow(
|
||||
session,
|
||||
BodyWeight(keyId: 'p1.0.key', payload: sealed, nonce: sealed),
|
||||
transaction: transaction,
|
||||
),
|
||||
);
|
||||
|
||||
test(
|
||||
"is written in the person's own space and read back there",
|
||||
() async {
|
||||
final written = await weightOf(alice);
|
||||
|
||||
final read = await sync().transactionForUser(
|
||||
alice,
|
||||
(transaction) =>
|
||||
BodyWeight.db.find(session, transaction: transaction),
|
||||
);
|
||||
expect([for (final weight in read) weight.id], [written.id]);
|
||||
},
|
||||
);
|
||||
|
||||
test("is denied to anyone else: someone else's reading finds none of "
|
||||
'it', () async {
|
||||
await weightOf(alice);
|
||||
|
||||
final read = await sync().transactionForUser(
|
||||
bob,
|
||||
(transaction) =>
|
||||
BodyWeight.db.find(session, transaction: transaction),
|
||||
);
|
||||
expect(read, isEmpty);
|
||||
});
|
||||
|
||||
test("is denied to anyone else: writing in another person's space is "
|
||||
'refused before anything is written', () async {
|
||||
await expectLater(
|
||||
sync().transactionForUser(
|
||||
bob,
|
||||
spaceId: alice,
|
||||
(transaction) => BodyWeight.db.insertRow(
|
||||
session,
|
||||
BodyWeight(keyId: 'p1.0.key', payload: sealed, nonce: sealed),
|
||||
transaction: transaction,
|
||||
),
|
||||
),
|
||||
throwsA(isA<OfflineSyncSpaceMembershipException>()),
|
||||
);
|
||||
// Nothing landed in Alice's space.
|
||||
final rows = await session.db.unsafeQuery(
|
||||
'SELECT count(*) FROM "body_weight" t '
|
||||
'JOIN "offline_sync_spaces" s ON s."id" = t."spaceId" '
|
||||
'WHERE s."uuidSpaceId" = @space',
|
||||
parameters: QueryParameters.named({'space': alice.uuid}),
|
||||
);
|
||||
expect(rows.first.first, 0);
|
||||
});
|
||||
},
|
||||
// As person_deletion_test: the sync engine's database, writing in
|
||||
// transactions of its own, so the tests delete what they made.
|
||||
databaseInterceptor: (session, inner) {
|
||||
final database = offlineSyncDatabaseInterceptor(session, inner);
|
||||
if (database is OfflineSyncDatabase) {
|
||||
_syncDatabases[session] = database;
|
||||
}
|
||||
return database;
|
||||
},
|
||||
rollbackDatabase: RollbackDatabase.disabled,
|
||||
);
|
||||
}
|
||||
|
||||
/// The sync engine's database of each session, as the test pod's own
|
||||
/// database wraps it.
|
||||
final _syncDatabases = Expando<OfflineSyncDatabase>();
|
||||
|
|
@ -53,8 +53,8 @@ void main() {
|
|||
|
||||
test(
|
||||
'deleting a person deletes their space with every key, workout, '
|
||||
"series, profile, day, day's series, night, plan, route and training "
|
||||
"place in it, and nobody else's",
|
||||
"series, profile, day, day's series, night, plan, route, training "
|
||||
"place, strength plan and weight in it, and nobody else's",
|
||||
() async {
|
||||
for (final person in [alice, bob]) {
|
||||
await _workoutOf(session, person);
|
||||
|
|
@ -71,6 +71,7 @@ void main() {
|
|||
'route',
|
||||
'training_place',
|
||||
'strength_plan',
|
||||
'body_weight',
|
||||
]) {
|
||||
expect(await rowsOf(alice, table), 1, reason: table);
|
||||
}
|
||||
|
|
@ -99,6 +100,7 @@ void main() {
|
|||
'route',
|
||||
'training_place',
|
||||
'strength_plan',
|
||||
'body_weight',
|
||||
]) {
|
||||
expect(await rowsOf(alice, table), 0, reason: table);
|
||||
expect(await rowsOf(bob, table), 1, reason: table);
|
||||
|
|
@ -196,4 +198,9 @@ Future<void> _workoutOf(Session session, UuidValue person) =>
|
|||
StrengthPlan(keyId: 'p1.0.key', payload: sealed, nonce: sealed),
|
||||
transaction: transaction,
|
||||
);
|
||||
await BodyWeight.db.insertRow(
|
||||
session,
|
||||
BodyWeight(keyId: 'p1.0.key', payload: sealed, nonce: sealed),
|
||||
transaction: transaction,
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue