Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
flutter-version: ${{ env.flutter_version }}
- name: "Install Tools"
run: |
dart pub global activate pana
flutter pub global activate pana
flutter pub global activate melos
- name: "Bootstrap Workspace"
run: melos bootstrap --verbose
Expand Down
58 changes: 44 additions & 14 deletions insights/lib/src/algolia_event_service.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import 'package:algolia_client_insights/algolia_client_insights.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';

import 'event.dart';
import 'event_service.dart';
import 'lib_version.dart';

/// Abstraction over the Insights client's push API to enable testing.
/// @nodoc - Internal use only, exposed for testing.
@internal
abstract class PushEvents {
Future<void> pushEvents({required InsightsEvents insightsEvents});
}

class _InsightsClient implements PushEvents {
final InsightsClient _client;
_InsightsClient(this._client);

@override
Future<void> pushEvents({required InsightsEvents insightsEvents}) {
return _client.pushEvents(insightsEvents: insightsEvents);
}
}

/// EventService implementation using community client instance
class AlgoliaEventService implements EventService {
/// Client instance
InsightsClient _client;
/// client instance
final PushEvents _client;

/// Logger instance
final Logger _log;
Expand All @@ -33,21 +51,33 @@ class AlgoliaEventService implements EventService {
),
);

/// Creates [AlgoliaEventService] instance.
AlgoliaEventService.create(this._client)
/// Creates [AlgoliaEventService] instance from an InsightsClient.
AlgoliaEventService.create(InsightsClient client)
: this.withClient(_InsightsClient(client));

/// Creates [AlgoliaEventService] instance with a custom client.
/// @nodoc - Internal use only, exposed for testing.
@internal
AlgoliaEventService.withClient(this._client)
: _log = Logger('Algolia/EventsService');

@override
void send(List<Event> events) => _client
.pushEvents(
insightsEvents: InsightsEvents(
events:
events.map((e) => e.toAlgoliaEvent()).where((e) => e != null)),
)
.then(
(_) => _log.fine('Events upload: $events'),
onError: (exception) => _log.severe('Events upload error: $exception'),
);
void send(List<Event> events) {
final validEvents =
events.map((e) => e.toAlgoliaEvent()).where((e) => e != null).toList();
if (validEvents.isEmpty) {
return;
}
_client
.pushEvents(
insightsEvents: InsightsEvents(events: validEvents),
)
.then(
(_) => _log.fine('Events upload: $events'),
onError: (exception) =>
_log.severe('Events upload error: $exception'),
);
}
}

extension AlgoliaEventConversion on Event {
Expand Down
107 changes: 107 additions & 0 deletions insights/test/algolia_event_service_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'package:test/test.dart';
import 'package:algolia_client_insights/algolia_client_insights.dart';
import 'package:algolia_insights/src/algolia_event_service.dart';
import 'package:algolia_insights/src/event.dart';

class TestClient implements PushEvents {
int pushEventsCallCount = 0;
@override
Future<void> pushEvents({required InsightsEvents insightsEvents}) async {
pushEventsCallCount++;
}
}

void main() {
group('AlgoliaEventService', () {
test('does not call pushEvents when validEvents is empty', () async {
final testClient = TestClient();
final service = AlgoliaEventService.withClient(testClient);
final events = <Event>[];

service.send(events);

// No events, so pushEvents should not be called
expect(testClient.pushEventsCallCount, equals(0));
});

test('calls pushEvents when all events are valid', () async {
final testClient = TestClient();
final service = AlgoliaEventService.withClient(testClient);
final events = [
Event.clickHits(
'test_event',
'test_index',
'test_user_token',
['object1', 'object2'],
),
Event.viewHits(
'test_view_event',
'test_index',
'test_user_token',
['object3', 'object4'],
),
];

service.send(events);

// All events are valid, so pushEvents should be called once
expect(testClient.pushEventsCallCount, equals(1));
});

test('calls pushEvents when some events are valid', () async {
final testClient = TestClient();
final service = AlgoliaEventService.withClient(testClient);
final events = [
Event.clickHits(
'test_event',
'test_index',
'test_user_token',
['object1', 'object2'],
),
// This event has no objectIDs or filterValues, so it will be invalid
Event.clickHits(
'invalid_event',
'test_index',
'test_user_token',
[],
),
];

service.send(events);

// At least one event is valid, so pushEvents should be called once
expect(testClient.pushEventsCallCount, equals(1));
});

test('does not call pushEvents when all events are invalid', () async {
final testClient = TestClient();
final service = AlgoliaEventService.withClient(testClient);
final events = [
// All these events have no objectIDs or filterValues, so they're invalid
Event.clickHits(
'invalid_event_1',
'test_index',
'test_user_token',
[],
),
Event.viewHits(
'invalid_event_2',
'test_index',
'test_user_token',
[],
),
Event.convertHits(
'invalid_event_3',
'test_index',
'test_user_token',
[],
),
];

service.send(events);

// All events are invalid, so pushEvents should not be called
expect(testClient.pushEventsCallCount, equals(0));
});
});
}