Files
buzz/mobile/test/shared/push/push_subscription_test.dart
Tom Brow d69b89c43c feat: add internal iOS push MVP
Signed-off-by: Tom Brow <tomb@squareup.com>
2026-08-18 16:23:53 -07:00

150 lines
4.6 KiB
Dart

import 'dart:convert';
import 'package:buzz/shared/push/push_subscription.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
final me = 'a' * 64;
const channelA = '123e4567-e89b-42d3-a456-426614174000';
const channelB = '123e4567-e89b-42d3-a456-426614174001';
test(
'desired subscription state round-trips with an accepted-state seam',
() {
final subscriptions = buildDesiredBuzzPushSubscriptions(
myPubkey: me,
channelIds: [channelB, channelA],
);
final state = BuzzPushLeaseSubscriptionState.desired(
desired: subscriptions,
);
final decoded = BuzzPushLeaseSubscriptionState.fromJson(
jsonDecode(jsonEncode(state.toJson())) as Map<String, dynamic>,
);
expect(decoded.authority, BuzzPushLeaseSubscriptionAuthority.desired);
expect(decoded.accepted, isNull);
expect(decoded.toJson(), state.toJson());
expect(decoded.authoritative, hasLength(2));
expect(decoded.authoritative.last.filter.hTags, [channelA, channelB]);
},
);
test('accepted authority requires observed accepted subscriptions', () {
final subscription = buildDesiredBuzzPushSubscriptions(myPubkey: me).single;
expect(
() => BuzzPushLeaseSubscriptionState.fromJson({
'authority': 'accepted',
'desired': [subscription.toJson()],
}),
throwsFormatException,
);
});
test('tracks relay lease and gateway grant generations independently', () {
final subscription = buildDesiredBuzzPushSubscriptions(myPubkey: me).single;
final state =
BuzzPushLeaseSubscriptionState.desired(
desired: [subscription],
).withAccepted(
subscriptions: [subscription],
generation: 9,
grantGeneration: 3,
installationId: 'c' * 32,
);
final decoded = BuzzPushLeaseSubscriptionState.fromJson(state.toJson());
expect(decoded.acceptedGeneration, 9);
expect(decoded.acceptedGrantGeneration, 3);
final migrated = BuzzPushLeaseSubscriptionState.fromJson({
...state.toJson()..remove('acceptedGrantGeneration'),
});
expect(migrated.acceptedGeneration, 9);
expect(migrated.acceptedGrantGeneration, 9);
});
test('builds aligned self and unmuted channel subscriptions', () {
final subscriptions = buildDesiredBuzzPushSubscriptions(
myPubkey: me.toUpperCase(),
channelIds: [channelB, channelA],
mutedChannelIds: [channelB, '123e4567-e89b-42d3-a456-426614174099'],
);
expect(subscriptions, hasLength(2));
expect(subscriptions.first.filter.kinds, buzzPushSelfDirectedKinds);
expect(subscriptions.first.filter.kinds, isNot(contains(7)));
expect(subscriptions.first.filter.pTags, [me]);
expect(subscriptions.last.filter.kinds, buzzPushChannelKinds);
expect(subscriptions.last.filter.hTags, [channelA]);
expect(subscriptions.first.ignore, hasLength(2));
expect(subscriptions.first.ignore.first.kinds, buzzPushRenderableKinds);
expect(subscriptions.first.ignore.last.hTags, [channelB]);
expect(
subscriptions.first.suppress?.pTagsMax,
buzzPushHellthreadParticipantLimit,
);
});
test('chunks channel subscriptions to relay limits deterministically', () {
final channels = [
for (var i = 0; i < 51; i++)
'00000000-0000-4000-8000-${i.toString().padLeft(12, '0')}',
]..shuffle();
final subscriptions = buildDesiredBuzzPushSubscriptions(
myPubkey: me,
channelIds: channels,
);
expect(subscriptions, hasLength(3));
expect(subscriptions[1].filter.hTags, hasLength(50));
expect(subscriptions[2].filter.hTags, hasLength(1));
expect(
subscriptions[1].filter.hTags,
orderedEquals([...subscriptions[1].filter.hTags!]..sort()),
);
});
test('rejects malformed and unsupported subscription fields', () {
final valid = buildDesiredBuzzPushSubscriptions(
myPubkey: me,
).single.toJson();
expect(
() => buildDesiredBuzzPushSubscriptions(
myPubkey: me,
channelIds: const ['not-a-channel'],
),
throwsFormatException,
);
expect(
() => BuzzPushSubscription.fromJson({...valid, 'unexpected': true}),
throwsFormatException,
);
expect(
() => BuzzPushSubscription.fromJson({
'filter': {
'kinds': [7],
'#p': [me],
},
'class': 'default',
}),
throwsFormatException,
);
expect(
() => BuzzPushSubscription.fromJson({
'filter': {
'kinds': [9],
'#p': ['not-a-pubkey'],
},
'class': 'default',
}),
throwsFormatException,
);
});
}