swarm/test/nostr_publish_actor_test.dart

151 lines
4.8 KiB
Dart
Raw Permalink Normal View History

2026-06-07 16:45:43 +03:00
import 'dart:convert';
import 'package:swarm/swarm.dart';
import 'package:test/test.dart';
import 'fake_relay_connection.dart';
const _event = '{'
'"id":"abc123",'
'"pubkey":"pub",'
'"created_at":1,'
'"kind":1,'
'"tags":[],'
'"content":"hi",'
'"sig":"sig"'
'}';
void main() {
group('NostrPublishActor', () {
tearDown(() async {});
test('sends correct EVENT format', () async {
final fake = FakeRelayConnection();
addTearDown(fake.dispose);
final actor = NostrPublishActor(
relayUrls: ['ws://relay1'],
retryConfig: const RetryConfig(maxAttempts: 1),
connectionFactory: (_) => fake,
);
await actor.handle(const PublishBatch([_event]));
final eventMsgs = fake.sentMessages
.where((m) => (jsonDecode(m) as List)[0] == 'EVENT')
.toList();
expect(eventMsgs, hasLength(1));
final decoded = jsonDecode(eventMsgs[0]) as List;
expect(decoded[0], equals('EVENT'));
expect(decoded[1], isA<Map>());
expect((decoded[1] as Map)['id'], equals('abc123'));
});
test('returns RelayConfirmed on success', () async {
final fake = FakeRelayConnection();
addTearDown(fake.dispose);
final actor = NostrPublishActor(
relayUrls: ['ws://relay1'],
retryConfig: const RetryConfig(maxAttempts: 1),
connectionFactory: (_) => fake,
);
final result = await actor.handle(const PublishBatch([_event]));
expect(result, isA<BatchPublished>());
final outcomes = (result as BatchPublished).outcomes;
expect(outcomes['ws://relay1'], isA<RelayConfirmed>());
});
test('returns RelayRejected with no retry', () async {
final fake = FakeRelayConnection()..autoRespond = false;
addTearDown(fake.dispose);
fake.queuedResponses.add(jsonEncode(['OK', 'abc123', false, 'spam']));
final actor = NostrPublishActor(
relayUrls: ['ws://relay1'],
retryConfig: const RetryConfig(maxAttempts: 1),
connectionFactory: (_) => fake,
);
final result = await actor.handle(const PublishBatch([_event]));
expect(result, isA<BatchPublished>());
final outcomes = (result as BatchPublished).outcomes;
expect(outcomes['ws://relay1'], isA<RelayRejected>());
expect((outcomes['ws://relay1'] as RelayRejected).reason, equals('spam'));
final eventMsgs = fake.sentMessages
.where((m) => (jsonDecode(m) as List)[0] == 'EVENT')
.toList();
expect(eventMsgs, hasLength(1));
});
test('returns RelayFailed on timeout and retries', () async {
final fake = FakeRelayConnection()..autoRespond = false;
addTearDown(fake.dispose);
final actor = NostrPublishActor(
relayUrls: ['ws://relay1'],
retryConfig: const RetryConfig(
maxAttempts: 2,
baseDelay: Duration(milliseconds: 1),
jitter: false,
),
batchTimeout: const Duration(milliseconds: 50),
connectionFactory: (_) => fake,
);
final result = await actor.handle(const PublishBatch([_event]));
expect(result, isA<BatchPublished>());
final outcomes = (result as BatchPublished).outcomes;
expect(outcomes['ws://relay1'], isA<RelayFailed>());
final eventMsgs = fake.sentMessages
.where((m) => (jsonDecode(m) as List)[0] == 'EVENT')
.toList();
expect(eventMsgs, hasLength(2));
});
test('returns RelayFailed on connection error', () async {
final fake = FakeRelayConnection()..throwOnConnect = true;
addTearDown(fake.dispose);
final actor = NostrPublishActor(
relayUrls: ['ws://relay1'],
retryConfig: const RetryConfig(maxAttempts: 1),
connectionFactory: (_) => fake,
);
final result = await actor.handle(const PublishBatch([_event]));
expect(result, isA<BatchPublished>());
final outcomes = (result as BatchPublished).outcomes;
expect(outcomes['ws://relay1'], isA<RelayFailed>());
});
test('fans out to multiple relays', () async {
final fake1 = FakeRelayConnection();
final fake2 = FakeRelayConnection();
addTearDown(fake1.dispose);
addTearDown(fake2.dispose);
final fakes = {'ws://relay1': fake1, 'ws://relay2': fake2};
final actor = NostrPublishActor(
relayUrls: ['ws://relay1', 'ws://relay2'],
retryConfig: const RetryConfig(maxAttempts: 1),
connectionFactory: (url) => fakes[url]!,
);
final result = await actor.handle(const PublishBatch([_event]));
expect(result, isA<BatchPublished>());
final outcomes = (result as BatchPublished).outcomes;
expect(outcomes.keys, containsAll(['ws://relay1', 'ws://relay2']));
expect(outcomes['ws://relay1'], isA<RelayConfirmed>());
expect(outcomes['ws://relay2'], isA<RelayConfirmed>());
});
});
}