tane/apps/app_seeds/test/state/trust_cubit_test.dart
vjrj dc7b2eee27 refactor(trust): ego-centric trust replaces the global Duniter membership
The global membership rule (curated bootstrap referents + sigQty/stepMax
parameters) solved sybil-proof identity for a UBI — a problem this app
doesn't have — and its screen leaked graph jargon (npub roots, parameter
steppers). Trust is now computed from the user's own position only:
you vouch / vouched by people you know (distance <=2) / vouched by N.

- TrustCubit: drop networkMember tier, referents and params; keep the
  circle rule and the 365-day vouch expiry (renewable, self-pruning).
- Delete TrustReferents, WotSettings, TrustNetworkScreen and the bundled
  referents asset; unwire injector/bootstrap/app/chat.
- New 'Your people' screen (/your-people, from the profile): who you
  vouch for (revocable) and who vouches for you, names via ProfileCache.
- i18n: wot.* removed, yourPeople.* added (en/es/pt/ast); trust.member
  removed. Kind 30777 events on relays stay fully compatible — only the
  interpretation changes.
2026-07-11 13:03:20 +02:00

128 lines
4.1 KiB
Dart

import 'package:commons_core/commons_core.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tane/state/trust_cubit.dart';
/// In-memory [TrustTransport]: certify/revoke act as this device's identity.
class FakeTrustTransport implements TrustTransport {
FakeTrustTransport(this.selfId);
final String selfId;
final Map<String, Set<String>> _certs = {}; // subject -> issuers
/// Test helper: record an arbitrary issuer→subject vouch (to build a graph).
void addCert(String issuer, String subject) =>
(_certs[subject] ??= {}).add(issuer);
@override
Future<Set<String>> certifiersOf(String subjectPubkey) async =>
_certs[subjectPubkey] ?? <String>{};
@override
Future<void> certify({
required String subjectPubkey,
Duration validity = const Duration(days: 365),
String note = '',
}) async =>
addCert(selfId, subjectPubkey);
@override
Future<void> revoke({required String subjectPubkey}) async =>
_certs[subjectPubkey]?.remove(selfId);
@override
Future<List<Certification>> allCertifications() async => [
for (final entry in _certs.entries)
for (final issuer in entry.value)
Certification(
issuer: issuer,
subject: entry.key,
issuedAt: DateTime(2026),
),
];
@override
Future<void> close() async {}
}
void main() {
const me = 'me';
const peer = 'peer';
test('loads certifier count and whether I vouch', () async {
final transport = FakeTrustTransport(me);
await transport.certify(subjectPubkey: peer); // I already vouch
final cubit = TrustCubit(transport, peerPubkey: peer, selfPubkey: me);
await cubit.load();
expect(cubit.state.certifierCount, 1);
expect(cubit.state.iVouch, isTrue);
expect(cubit.state.loading, isFalse);
await cubit.close();
});
test('toggleVouch certifies then revokes', () async {
final cubit =
TrustCubit(FakeTrustTransport(me), peerPubkey: peer, selfPubkey: me);
await cubit.load();
expect(cubit.state.iVouch, isFalse);
await cubit.toggleVouch();
expect(cubit.state.iVouch, isTrue);
expect(cubit.state.certifierCount, 1);
await cubit.toggleVouch();
expect(cubit.state.iVouch, isFalse);
expect(cubit.state.certifierCount, 0);
await cubit.close();
});
test('a friend-of-a-friend is in your circle; a stranger is not', () async {
final transport = FakeTrustTransport(me)
..addCert(me, 'friend') // you vouch for a friend
..addCert('friend', peer) // your friend vouches for the peer
..addCert('stranger', 'other'); // unrelated to you
final inCircle = TrustCubit(transport, peerPubkey: peer, selfPubkey: me);
await inCircle.load();
expect(inCircle.state.knownToYou, isTrue);
expect(inCircle.state.tier, TrustTier.inYourCircle);
await inCircle.close();
final outside = TrustCubit(transport, peerPubkey: 'other', selfPubkey: me);
await outside.load();
expect(outside.state.knownToYou, isFalse);
await outside.close();
});
test('tier falls back to vouched, then unknown', () async {
final vouched = TrustCubit(
FakeTrustTransport(me)..addCert('someone', peer),
peerPubkey: peer,
selfPubkey: me);
await vouched.load();
expect(vouched.state.tier, TrustTier.vouched);
await vouched.close();
final unknown = TrustCubit(FakeTrustTransport(me),
peerPubkey: 'nobody', selfPubkey: me);
await unknown.load();
expect(unknown.state.tier, TrustTier.unknown);
await unknown.close();
});
test('never vouches for self', () async {
final cubit =
TrustCubit(FakeTrustTransport(me), peerPubkey: me, selfPubkey: me);
await cubit.load();
await cubit.toggleVouch();
expect(cubit.state.iVouch, isFalse);
await cubit.close();
});
test('offline (no transport) never throws', () async {
final cubit = TrustCubit(null, peerPubkey: peer, selfPubkey: me);
expect(cubit.isOnline, isFalse);
await cubit.load();
await cubit.toggleVouch();
expect(cubit.state.loading, isFalse);
await cubit.close();
});
}