Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: ratings service and model #1439

Merged
merged 3 commits into from
Oct 5, 2023
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
9 changes: 7 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:io';

import 'package:app_center_ratings_client/ratings_client.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:github/github.dart';
Expand Down Expand Up @@ -44,8 +45,12 @@ Future<void> main(List<String> args) async {
final config = ConfigService();
config.load();

final ratings =
RatingsService(config.ratingServiceUrl, config.ratingsServicePort);
final ratings = RatingsService(
RatingsClient(
config.ratingServiceUrl,
config.ratingsServicePort,
),
);
registerServiceInstance(config);
registerServiceInstance(ratings);

Expand Down
4 changes: 2 additions & 2 deletions lib/src/ratings/ratings_model.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'package:clock/clock.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:snapd/snapd.dart';
import 'package:ubuntu_service/ubuntu_service.dart';

import 'exports.dart';

import 'ratings_service.dart';

final ratingsModelProvider =
Expand Down Expand Up @@ -72,7 +72,7 @@ class RatingsModel extends ChangeNotifier {
snapId: snapId,
snapRevision: int.parse(snapRevision),
voteUp: voteUp, // using voteUp directly here
dateTime: DateTime.now(),
dateTime: clock.now(),
);
await ratings.vote(vote);
_vote = castVote;
Expand Down
21 changes: 10 additions & 11 deletions lib/src/ratings/ratings_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:glib/glib.dart';
import 'package:jwt_decode/jwt_decode.dart';

import 'exports.dart';

class RatingsService {
RatingsService(String url, int port,
[@visibleForTesting RatingsClient? client])
: _client = client ?? RatingsClient(url, port),
_id = _generateId();
RatingsService(this.client, {@visibleForTesting String? id})
: _id = id ?? _generateId();

final RatingsClient _client;
final RatingsClient client;
String? _jwt;
final String _id;

Expand All @@ -26,32 +25,32 @@ class RatingsService {

Future<void> _ensureValidToken() async {
if (_jwt == null || Jwt.isExpired(_jwt!)) {
_jwt = await _client.authenticate(_id);
_jwt = await client.authenticate(_id);
}
}

Future<Rating?> getRating(String snapId) async {
await _ensureValidToken();
return _client.getRating(snapId, _jwt!);
return client.getRating(snapId, _jwt!);
}

Future<void> vote(Vote vote) async {
await _ensureValidToken();
await _client.vote(vote.snapId, vote.snapRevision, vote.voteUp, _jwt!);
await client.vote(vote.snapId, vote.snapRevision, vote.voteUp, _jwt!);
}

Future<void> delete() async {
await _ensureValidToken();
await _client.delete(_jwt!);
await client.delete(_jwt!);
}

Future<List<Vote>> listMyVotes(String snapFilter) async {
await _ensureValidToken();
return await _client.listMyVotes(snapFilter, _jwt!);
return await client.listMyVotes(snapFilter, _jwt!);
}

Future<List<Vote>> getSnapVotes(String snapId) async {
await _ensureValidToken();
return await _client.getSnapVotes(snapId, _jwt!);
return await client.getSnapVotes(snapId, _jwt!);
}
}
3 changes: 2 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ dependencies:
collection: ^1.17.0
dbus: ^0.7.8
app_center_ratings_client:
git:
git:
url: https://github.com/matthew-hagemann/app_center_ratings_client.dart.git
ref: main
crypto: ^3.0.3
Expand Down Expand Up @@ -52,6 +52,7 @@ dependencies:
yaru_icons: ^1.0.4
yaru_widgets: ^2.6.0
yaru_test: ^0.1.4
clock: ^1.1.1

dev_dependencies:
build_runner: ^2.4.5
Expand Down
70 changes: 70 additions & 0 deletions test/ratings_model_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'package:app_center/ratings.dart';
import 'package:clock/clock.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

import 'test_utils.dart';

void main() {
test('init', () async {
final mockService = createMockRatingsService(
rating: const Rating(
snapId: 'firefox',
totalVotes: 1337,
ratingsBand: RatingsBand.veryGood,
),
snapVotes: [
Vote(
snapId: 'firefox',
snapRevision: 42,
voteUp: true,
dateTime: DateTime(1970),
),
],
);
final model = RatingsModel(
ratings: mockService,
snapId: 'firefox',
snapRevision: '42',
);

await model.init();
expect(model.state.hasValue, isTrue);
expect(
model.snapRating,
equals(
const Rating(
snapId: 'firefox',
totalVotes: 1337,
ratingsBand: RatingsBand.veryGood,
),
),
);
expect(model.vote, equals(VoteStatus.up));
});

test('cast vote', () async {
final mockService = createMockRatingsService();
final model = RatingsModel(
ratings: mockService,
snapId: 'firefox',
snapRevision: '42',
);

await model.init();
await withClock(
Clock.fixed(DateTime(1984)),
() => model.castVote(VoteStatus.up),
);
verify(
mockService.vote(
Vote(
dateTime: DateTime(1984),
snapId: 'firefox',
snapRevision: 42,
voteUp: true,
),
),
).called(1);
});
}
120 changes: 120 additions & 0 deletions test/ratings_service_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import 'package:app_center/src/ratings/exports.dart';
import 'package:app_center/src/ratings/ratings_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

import 'test_utils.dart';

void main() {
test('get rating', () async {
final mockClient = createMockRatingsClient(
token: 'jwt',
rating: const Rating(
snapId: 'firefox',
totalVotes: 1337,
ratingsBand: RatingsBand.veryGood,
),
);
final service = RatingsService(mockClient, id: 'myId');

final rating = await service.getRating('firefox');
verify(mockClient.authenticate('myId')).called(1);
expect(
rating,
equals(
const Rating(
snapId: 'firefox',
totalVotes: 1337,
ratingsBand: RatingsBand.veryGood,
),
),
);
});

test('vote', () async {
final mockClient = createMockRatingsClient(token: 'jwt');
final service = RatingsService(mockClient, id: 'myId');

await service.vote(
Vote(
snapId: 'thunderbird',
snapRevision: 42,
voteUp: true,
dateTime: DateTime(1970),
),
);
verify(mockClient.authenticate('myId')).called(1);
verify(mockClient.vote('thunderbird', 42, true, 'jwt')).called(1);
});

test('delete', () async {
final mockClient = createMockRatingsClient(token: 'jwt');
final service = RatingsService(mockClient, id: 'myId');

await service.delete();
verify(mockClient.authenticate('myId')).called(1);
verify(mockClient.delete('jwt')).called(1);
});

test('list my votes', () async {
final mockClient = createMockRatingsClient(
token: 'jwt',
myVotes: [
Vote(
snapId: 'testsnap',
snapRevision: 1,
voteUp: false,
dateTime: DateTime(1984),
),
],
);
final service = RatingsService(mockClient, id: 'myId');

final votes = await service.listMyVotes('testsnap');
verify(mockClient.authenticate('myId')).called(1);
expect(
votes,
equals(
[
Vote(
snapId: 'testsnap',
snapRevision: 1,
voteUp: false,
dateTime: DateTime(1984),
),
],
),
);
});

test('snap votes', () async {
final mockClient = createMockRatingsClient(
token: 'jwt',
snapVotes: [
Vote(
snapId: 'testsnap2',
snapRevision: 2,
voteUp: true,
dateTime: DateTime(1999),
),
],
);
final service = RatingsService(mockClient, id: 'myId');

final votes = await service.getSnapVotes('testsnap2');
verify(mockClient.authenticate('myId')).called(1);
expect(
votes,
equals(
[
Vote(
snapId: 'testsnap2',
snapRevision: 2,
voteUp: true,
dateTime: DateTime(1999),
),
],
),
);
});
}
8 changes: 6 additions & 2 deletions test/snap_page_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,14 @@ void main() {
);
final snapLauncher = createMockSnapLauncher(isLaunchable: true);
final updatesModel = createMockUpdatesModel();
final ratingsModel = createMockRatingsModel();

await tester.pumpApp((_) => ProviderScope(
overrides: [
snapModelProvider.overrideWith((ref, arg) => snapModel),
launchProvider.overrideWith((ref, arg) => snapLauncher),
updatesModelProvider.overrideWith((ref) => updatesModel)
updatesModelProvider.overrideWith((ref) => updatesModel),
ratingsModelProvider.overrideWith((ref, arg) => ratingsModel),
],
child: SnapPage(snapName: storeSnap.name),
));
Expand Down Expand Up @@ -313,12 +315,14 @@ void main() {
);
final snapLauncher = createMockSnapLauncher(isLaunchable: true);
final updatesModel = createMockUpdatesModel();
final ratingsModel = createMockRatingsModel();

await tester.pumpApp((_) => ProviderScope(
overrides: [
snapModelProvider.overrideWith((ref, arg) => snapModel),
launchProvider.overrideWith((ref, arg) => snapLauncher),
updatesModelProvider.overrideWith((ref) => updatesModel)
updatesModelProvider.overrideWith((ref) => updatesModel),
ratingsModelProvider.overrideWith((ref, arg) => ratingsModel),
],
child: SnapPage(snapName: storeSnap.name),
));
Expand Down
37 changes: 35 additions & 2 deletions test/test_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:app_center/ratings.dart';
import 'package:app_center/snapd.dart';
import 'package:app_center/src/deb/deb_model.dart';
import 'package:app_center/src/manage/manage_model.dart';
import 'package:app_center_ratings_client/ratings_client.dart';
import 'package:appstream/appstream.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -282,12 +283,44 @@ MockPackageKitTransaction createMockPackageKitTransaction({
}

@GenerateMocks([RatingsService])
MockRatingsService createMockRatingsService() {
MockRatingsService createMockRatingsService({
Rating? rating,
List<Vote>? snapVotes,
}) {
final service = MockRatingsService();
when(service.vote(any)).thenAnswer((_) async => {});
when(service.getRating(any)).thenAnswer((_) async =>
rating ??
const Rating(
snapId: '',
totalVotes: 0,
ratingsBand: RatingsBand.insufficientVotes,
));
when(service.getSnapVotes(any)).thenAnswer((_) async => snapVotes ?? []);

return service;
}

@GenerateMocks([RatingsClient])
MockRatingsClient createMockRatingsClient({
String? token,
Rating? rating,
List<Vote>? myVotes,
List<Vote>? snapVotes,
}) {
final client = MockRatingsClient();
when(client.authenticate(any)).thenAnswer((_) async => token ?? '');
when(client.getRating(any, any)).thenAnswer((_) async =>
rating ??
const Rating(
snapId: '',
totalVotes: 0,
ratingsBand: RatingsBand.insufficientVotes,
));
when(client.listMyVotes(any, any)).thenAnswer((_) async => myVotes ?? []);
when(client.getSnapVotes(any, any)).thenAnswer((_) async => snapVotes ?? []);
return client;
}

@GenerateMocks([AppstreamService])
MockAppstreamService createMockAppstreamService({
AppstreamComponent? component,
Expand Down
Loading