refactor(shorebird_code_push_client): rename package and support getApps (#64)
This commit is contained in:
@@ -34,10 +34,10 @@ jobs:
|
||||
shorebird_cli:
|
||||
- ./.github/actions/dart_package
|
||||
- packages/shorebird_cli/**
|
||||
- packages/shorebird_code_push_api_client/**
|
||||
shorebird_code_push_api_client:
|
||||
- packages/shorebird_code_push_client/**
|
||||
shorebird_code_push_client:
|
||||
- ./.github/actions/dart_package
|
||||
- packages/shorebird_code_push_api_client/**
|
||||
- packages/shorebird_code_push_client/**
|
||||
shorebird_code_push_protocol:
|
||||
- ./.github/actions/dart_package
|
||||
- packages/shorebird_code_push_protocol/**
|
||||
|
||||
@@ -22,12 +22,12 @@ Refer to [shorebird/install](https://github.com/shorebirdtech/install) for insta
|
||||
|
||||
This repository is a monorepo containing the following packages:
|
||||
|
||||
| Package | Description |
|
||||
| ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| [shorebird_cli](packages/shorebird_cli/README.md) | Command-line which allows developers to interact with various Shorebird services |
|
||||
| [shorebird_code_push_api_client](packages/shorebird_code_push_api_client/README.md) | Dart library which allows Dart applications to interact with the ShoreBird CodePush API |
|
||||
| [shorebird_code_push_protocol](packages/shorebird_code_push_protocol/README.md) | Dart library which contains common interfaces used by Shorebird CodePush |
|
||||
| [shorebird_code_push_updater](updater/README.md) | Rust library which handles the CodePush logic and does the real update work |
|
||||
| Package | Description |
|
||||
| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
||||
| [shorebird_cli](packages/shorebird_cli/README.md) | Command-line which allows developers to interact with various Shorebird services |
|
||||
| [shorebird_code_push_client](packages/shorebird_code_push_client/README.md) | Dart library which allows Dart applications to interact with the ShoreBird CodePush API |
|
||||
| [shorebird_code_push_protocol](packages/shorebird_code_push_protocol/README.md) | Dart library which contains common interfaces used by Shorebird CodePush |
|
||||
| [shorebird_code_push_updater](updater/README.md) | Rust library which handles the CodePush logic and does the real update work |
|
||||
|
||||
For more information, please refer to the documentation for each package.
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@ import 'package:args/command_runner.dart';
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
typedef CodePushClientBuilder = ShorebirdCodePushApiClient Function({
|
||||
typedef CodePushClientBuilder = CodePushClient Function({
|
||||
required String apiKey,
|
||||
});
|
||||
|
||||
@@ -35,8 +35,7 @@ abstract class ShorebirdCommand extends Command<int> {
|
||||
StartProcess? startProcess,
|
||||
UuidBuilder? buildUuid,
|
||||
}) : auth = auth ?? Auth(),
|
||||
buildCodePushClient =
|
||||
buildCodePushClient ?? ShorebirdCodePushApiClient.new,
|
||||
buildCodePushClient = buildCodePushClient ?? CodePushClient.new,
|
||||
runProcess = runProcess ?? Process.run,
|
||||
startProcess = startProcess ?? Process.start,
|
||||
buildUuid = buildUuid ?? const Uuid().v4;
|
||||
|
||||
@@ -5,7 +5,7 @@ import 'dart:isolate';
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/command.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
mixin ShorebirdEngineMixin on ShorebirdCommand {
|
||||
String get shorebirdEnginePath {
|
||||
@@ -60,7 +60,7 @@ mixin ShorebirdEngineMixin on ShorebirdCommand {
|
||||
}
|
||||
|
||||
Future<void> _downloadShorebirdEngine(
|
||||
ShorebirdCodePushApiClient codePushClient,
|
||||
CodePushClient codePushClient,
|
||||
String path,
|
||||
) async {
|
||||
final engine = await codePushClient.downloadEngine('latest');
|
||||
|
||||
@@ -20,8 +20,8 @@ dependencies:
|
||||
path: ^1.8.3
|
||||
pub_updater: ^0.2.4
|
||||
pubspec_parse: ^1.2.2
|
||||
shorebird_code_push_api_client:
|
||||
path: ../shorebird_code_push_api_client
|
||||
shorebird_code_push_client:
|
||||
path: ../shorebird_code_push_client
|
||||
uuid: ^3.0.7
|
||||
yaml: ^3.1.1
|
||||
yaml_edit: ^2.1.0
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:mocktail/mocktail.dart';
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_cli/src/auth/session.dart';
|
||||
import 'package:shorebird_cli/src/commands/build_command.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockAuth extends Mock implements Auth {}
|
||||
@@ -17,8 +17,7 @@ class _MockProgress extends Mock implements Progress {}
|
||||
|
||||
class _MockProcessResult extends Mock implements ProcessResult {}
|
||||
|
||||
class _MockShorebirdCodePushApiClient extends Mock
|
||||
implements ShorebirdCodePushApiClient {}
|
||||
class _MockCodePushClient extends Mock implements CodePushClient {}
|
||||
|
||||
void main() {
|
||||
group('build', () {
|
||||
@@ -28,14 +27,14 @@ void main() {
|
||||
);
|
||||
|
||||
late Auth auth;
|
||||
late ShorebirdCodePushApiClient codePushClient;
|
||||
late CodePushClient codePushClient;
|
||||
late Logger logger;
|
||||
late ProcessResult processResult;
|
||||
late BuildCommand buildCommand;
|
||||
|
||||
setUp(() {
|
||||
auth = _MockAuth();
|
||||
codePushClient = _MockShorebirdCodePushApiClient();
|
||||
codePushClient = _MockCodePushClient();
|
||||
logger = _MockLogger();
|
||||
processResult = _MockProcessResult();
|
||||
buildCommand = BuildCommand(
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_cli/src/auth/session.dart';
|
||||
import 'package:shorebird_cli/src/commands/publish_command.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockArgResults extends Mock implements ArgResults {}
|
||||
@@ -19,8 +19,7 @@ class _MockLogger extends Mock implements Logger {}
|
||||
|
||||
class _MockProgress extends Mock implements Progress {}
|
||||
|
||||
class _MockShorebirdCodePushApiClient extends Mock
|
||||
implements ShorebirdCodePushApiClient {}
|
||||
class _MockCodePushClient extends Mock implements CodePushClient {}
|
||||
|
||||
class _FakeCommandRunner extends Fake implements CommandRunner<int> {
|
||||
@override
|
||||
@@ -44,14 +43,14 @@ environment:
|
||||
late ArgResults argResults;
|
||||
late Auth auth;
|
||||
late Logger logger;
|
||||
late _MockShorebirdCodePushApiClient codePushClient;
|
||||
late CodePushClient codePushClient;
|
||||
late PublishCommand command;
|
||||
|
||||
setUp(() {
|
||||
argResults = _MockArgResults();
|
||||
auth = _MockAuth();
|
||||
logger = _MockLogger();
|
||||
codePushClient = _MockShorebirdCodePushApiClient();
|
||||
codePushClient = _MockCodePushClient();
|
||||
command = PublishCommand(
|
||||
auth: auth,
|
||||
buildCodePushClient: ({required String apiKey}) => codePushClient,
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:path/path.dart' as p;
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_cli/src/auth/session.dart';
|
||||
import 'package:shorebird_cli/src/commands/run_command.dart';
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockArgResults extends Mock implements ArgResults {}
|
||||
@@ -24,8 +24,7 @@ class _MockProgress extends Mock implements Progress {}
|
||||
|
||||
class _MockProcess extends Mock implements Process {}
|
||||
|
||||
class _MockShorebirdCodePushApiClient extends Mock
|
||||
implements ShorebirdCodePushApiClient {}
|
||||
class _MockCodePushClient extends Mock implements CodePushClient {}
|
||||
|
||||
void main() {
|
||||
group('run', () {
|
||||
@@ -38,7 +37,7 @@ void main() {
|
||||
late Auth auth;
|
||||
late Logger logger;
|
||||
late Process process;
|
||||
late ShorebirdCodePushApiClient codePushClient;
|
||||
late CodePushClient codePushClient;
|
||||
late RunCommand runCommand;
|
||||
|
||||
setUp(() {
|
||||
@@ -46,7 +45,7 @@ void main() {
|
||||
auth = _MockAuth();
|
||||
logger = _MockLogger();
|
||||
process = _MockProcess();
|
||||
codePushClient = _MockShorebirdCodePushApiClient();
|
||||
codePushClient = _MockCodePushClient();
|
||||
runCommand = RunCommand(
|
||||
auth: auth,
|
||||
logger: logger,
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
## Shorebird CodePush API Client
|
||||
|
||||
The Shorebird CodePush API Client is a Dart library which allows Dart applications to interact with the ShoreBird CodePush API.
|
||||
|
||||
### Installing
|
||||
|
||||
To get started, add the library to your `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
shorebird_code_push_api_client:
|
||||
git:
|
||||
url: https://github.com/shorebirdtech/shorebird
|
||||
path: packages/shorebird_code_push_api_client
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```dart
|
||||
void main() async {
|
||||
// Create an instance of the client.
|
||||
final client = ShorebirdCodePushApiClient();
|
||||
|
||||
// Publish a new release.
|
||||
await client.createRelease('path/to/release');
|
||||
}
|
||||
```
|
||||
@@ -1,4 +0,0 @@
|
||||
/// The Shorebird CodePush API Client
|
||||
library shorebird_code_push_api_client;
|
||||
|
||||
export 'src/shorebird_code_push_api_client.dart';
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockHttpClient extends Mock implements http.Client {}
|
||||
|
||||
class _FakeBaseRequest extends Fake implements http.BaseRequest {}
|
||||
|
||||
void main() {
|
||||
group('ShorebirdCodePushApiClient', () {
|
||||
const apiKey = 'api-key';
|
||||
const productId = 'shorebird-example';
|
||||
|
||||
late http.Client httpClient;
|
||||
late ShorebirdCodePushApiClient shorebirdCodePushApiClient;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeBaseRequest());
|
||||
registerFallbackValue(Uri());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
httpClient = _MockHttpClient();
|
||||
shorebirdCodePushApiClient = ShorebirdCodePushApiClient(
|
||||
apiKey: apiKey,
|
||||
httpClient: httpClient,
|
||||
);
|
||||
});
|
||||
|
||||
test('can be instantiated', () {
|
||||
expect(ShorebirdCodePushApiClient(apiKey: apiKey), isNotNull);
|
||||
});
|
||||
|
||||
group('createApp', () {
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(
|
||||
() => httpClient.post(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
body: any(named: 'body'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.badRequest));
|
||||
|
||||
expect(
|
||||
shorebirdCodePushApiClient.createApp(productId: productId),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('completes when request succeeds', () async {
|
||||
when(
|
||||
() => httpClient.post(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
body: any(named: 'body'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.created));
|
||||
|
||||
await shorebirdCodePushApiClient.createApp(productId: productId);
|
||||
|
||||
final uri = verify(
|
||||
() => httpClient.post(
|
||||
captureAny(),
|
||||
headers: any(named: 'headers'),
|
||||
body: any(named: 'body'),
|
||||
),
|
||||
).captured.single as Uri;
|
||||
|
||||
expect(
|
||||
uri,
|
||||
shorebirdCodePushApiClient.hostedUri.replace(path: '/api/v1/apps'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('createPatch', () {
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
400,
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
shorebirdCodePushApiClient.createPatch(
|
||||
artifactPath: path.join('test', 'fixtures', 'release.txt'),
|
||||
baseVersion: '1.0.0',
|
||||
productId: 'shorebird-example',
|
||||
channel: 'stable',
|
||||
),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('sends a multipart request to the correct url', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
HttpStatus.created,
|
||||
);
|
||||
});
|
||||
|
||||
await shorebirdCodePushApiClient.createPatch(
|
||||
artifactPath: path.join('test', 'fixtures', 'release.txt'),
|
||||
baseVersion: '1.0.0',
|
||||
productId: 'shorebird-example',
|
||||
channel: 'stable',
|
||||
);
|
||||
|
||||
final request = verify(() => httpClient.send(captureAny()))
|
||||
.captured
|
||||
.single as http.MultipartRequest;
|
||||
expect(
|
||||
request.url,
|
||||
shorebirdCodePushApiClient.hostedUri.replace(path: '/api/v1/patches'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('deleteApp', () {
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(
|
||||
() => httpClient.delete(any(), headers: any(named: 'headers')),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.badRequest));
|
||||
|
||||
expect(
|
||||
shorebirdCodePushApiClient.deleteApp(productId: productId),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('completes when request succeeds', () async {
|
||||
when(
|
||||
() => httpClient.delete(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.noContent));
|
||||
|
||||
await shorebirdCodePushApiClient.deleteApp(productId: productId);
|
||||
|
||||
final uri = verify(
|
||||
() => httpClient.delete(
|
||||
captureAny(),
|
||||
headers: any(named: 'headers'),
|
||||
),
|
||||
).captured.single as Uri;
|
||||
|
||||
expect(
|
||||
uri,
|
||||
shorebirdCodePushApiClient.hostedUri.replace(
|
||||
path: '/api/v1/apps/$productId',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('downloadEngine', () {
|
||||
const engineRevision = 'engine-revision';
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
400,
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
shorebirdCodePushApiClient.downloadEngine(engineRevision),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('sends a request to the correct url', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
HttpStatus.ok,
|
||||
);
|
||||
});
|
||||
|
||||
await shorebirdCodePushApiClient.downloadEngine(engineRevision);
|
||||
|
||||
final request = verify(() => httpClient.send(captureAny()))
|
||||
.captured
|
||||
.single as http.Request;
|
||||
|
||||
expect(
|
||||
request.url,
|
||||
Uri.parse(
|
||||
'https://storage.googleapis.com/code-push-dev.appspot.com/engines/dev/engine.zip',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('close', () {
|
||||
test('closes the underlying client', () {
|
||||
shorebirdCodePushApiClient.close();
|
||||
verify(() => httpClient.close()).called(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
## Shorebird CodePush Client
|
||||
|
||||
The Shorebird CodePush Client is a Dart library which allows Dart applications to interact with the ShoreBird CodePush API.
|
||||
|
||||
### Installing
|
||||
|
||||
To get started, add the library to your `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
shorebird_code_push_client:
|
||||
git:
|
||||
url: https://github.com/shorebirdtech/shorebird
|
||||
path: packages/shorebird_code_push_client
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```dart
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
final client = CodePushClient(apiKey: '<API KEY>');
|
||||
|
||||
// Download the latest engine revision.
|
||||
final engine = await client.downloadEngine('latest');
|
||||
|
||||
// Create a new Shorebird application.
|
||||
await client.createApp(productId: '<PRODUCT ID>');
|
||||
|
||||
// List all apps.
|
||||
final apps = await client.getApps();
|
||||
|
||||
// Create a new patch.
|
||||
await client.createPatch(
|
||||
artifactPath: '<PATH TO ARTIFACT>', // e.g. 'libapp.so'
|
||||
baseVersion: '<BASE VERSION>', // e.g. '1.0.0'
|
||||
productId: '<PRODUCT ID>', // e.g. 'shorebird-example'
|
||||
channel: '<CHANNEL>', // e.g. 'stable'
|
||||
);
|
||||
|
||||
// Close the client.
|
||||
client.close();
|
||||
}
|
||||
```
|
||||
+5
-2
@@ -1,9 +1,9 @@
|
||||
// ignore_for_file: unused_local_variable
|
||||
|
||||
import 'package:shorebird_code_push_api_client/shorebird_code_push_api_client.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
final client = ShorebirdCodePushApiClient(apiKey: '<API KEY>');
|
||||
final client = CodePushClient(apiKey: '<API KEY>');
|
||||
|
||||
// Download the latest engine revision.
|
||||
final engine = await client.downloadEngine('latest');
|
||||
@@ -11,6 +11,9 @@ Future<void> main() async {
|
||||
// Create a new Shorebird application.
|
||||
await client.createApp(productId: '<PRODUCT ID>');
|
||||
|
||||
// List all apps.
|
||||
final apps = await client.getApps();
|
||||
|
||||
// Create a new patch.
|
||||
await client.createPatch(
|
||||
artifactPath: '<PATH TO ARTIFACT>', // e.g. 'libapp.so'
|
||||
@@ -0,0 +1,4 @@
|
||||
/// The Shorebird CodePush API Client
|
||||
library shorebird_code_push_client;
|
||||
|
||||
export 'src/code_push_client.dart';
|
||||
+23
-5
@@ -3,13 +3,14 @@ import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
|
||||
/// {@template shorebird_code_push_api_client}
|
||||
/// The Shorebird CodePush API Client
|
||||
/// {@template code_push_client}
|
||||
/// Dart client for the Shorebird CodePush API.
|
||||
/// {@endtemplate}
|
||||
class ShorebirdCodePushApiClient {
|
||||
/// {@macro shorebird_code_push_api_client}
|
||||
ShorebirdCodePushApiClient({
|
||||
class CodePushClient {
|
||||
/// {@macro code_push_client}
|
||||
CodePushClient({
|
||||
required String apiKey,
|
||||
http.Client? httpClient,
|
||||
Uri? hostedUri,
|
||||
@@ -97,6 +98,23 @@ class ShorebirdCodePushApiClient {
|
||||
return response.stream.toBytes();
|
||||
}
|
||||
|
||||
/// List all apps for the current account.
|
||||
Future<List<App>> getApps() async {
|
||||
final response = await _httpClient.get(
|
||||
Uri.parse('$hostedUri/api/v1/apps'),
|
||||
headers: _apiKeyHeader,
|
||||
);
|
||||
|
||||
if (response.statusCode != HttpStatus.ok) {
|
||||
throw Exception('${response.statusCode} ${response.reasonPhrase}');
|
||||
}
|
||||
|
||||
final apps = json.decode(response.body) as List;
|
||||
return apps
|
||||
.map((app) => App.fromJson(app as Map<String, dynamic>))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Closes the client.
|
||||
void close() => _httpClient.close();
|
||||
}
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
name: shorebird_code_push_api_client
|
||||
name: shorebird_code_push_client
|
||||
description: Library which allows Dart applications to interact with the ShoreBird CodePush API
|
||||
version: 0.1.0+1
|
||||
repository: https://github.com/shorebirdtech/shorebird
|
||||
@@ -10,6 +10,8 @@ environment:
|
||||
|
||||
dependencies:
|
||||
http: ^0.13.5
|
||||
shorebird_code_push_protocol:
|
||||
path: ../shorebird_code_push_protocol
|
||||
|
||||
dev_dependencies:
|
||||
mocktail: ^0.3.0
|
||||
@@ -0,0 +1,353 @@
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:shorebird_code_push_protocol/shorebird_code_push_protocol.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class _MockHttpClient extends Mock implements http.Client {}
|
||||
|
||||
class _FakeBaseRequest extends Fake implements http.BaseRequest {}
|
||||
|
||||
void main() {
|
||||
group('CodePushClient', () {
|
||||
const apiKey = 'api-key';
|
||||
const productId = 'shorebird-example';
|
||||
|
||||
late http.Client httpClient;
|
||||
late CodePushClient codePushClient;
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(_FakeBaseRequest());
|
||||
registerFallbackValue(Uri());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
httpClient = _MockHttpClient();
|
||||
codePushClient = CodePushClient(
|
||||
apiKey: apiKey,
|
||||
httpClient: httpClient,
|
||||
);
|
||||
});
|
||||
|
||||
test('can be instantiated', () {
|
||||
expect(CodePushClient(apiKey: apiKey), isNotNull);
|
||||
});
|
||||
|
||||
group('createApp', () {
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(
|
||||
() => httpClient.post(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
body: any(named: 'body'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.badRequest));
|
||||
|
||||
expect(
|
||||
codePushClient.createApp(productId: productId),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('completes when request succeeds', () async {
|
||||
when(
|
||||
() => httpClient.post(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
body: any(named: 'body'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.created));
|
||||
|
||||
await codePushClient.createApp(productId: productId);
|
||||
|
||||
final uri = verify(
|
||||
() => httpClient.post(
|
||||
captureAny(),
|
||||
headers: any(named: 'headers'),
|
||||
body: any(named: 'body'),
|
||||
),
|
||||
).captured.single as Uri;
|
||||
|
||||
expect(
|
||||
uri,
|
||||
codePushClient.hostedUri.replace(path: '/api/v1/apps'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('createPatch', () {
|
||||
test('throws an exception if the http request fails', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final fixture = File(path.join(tempDir.path, 'release.txt'))
|
||||
..createSync();
|
||||
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
400,
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
codePushClient.createPatch(
|
||||
artifactPath: fixture.path,
|
||||
baseVersion: '1.0.0',
|
||||
productId: 'shorebird-example',
|
||||
channel: 'stable',
|
||||
),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('sends a multipart request to the correct url', () async {
|
||||
final tempDir = Directory.systemTemp.createTempSync();
|
||||
final fixture = File(path.join(tempDir.path, 'release.txt'))
|
||||
..createSync();
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
HttpStatus.created,
|
||||
);
|
||||
});
|
||||
|
||||
await codePushClient.createPatch(
|
||||
artifactPath: fixture.path,
|
||||
baseVersion: '1.0.0',
|
||||
productId: 'shorebird-example',
|
||||
channel: 'stable',
|
||||
);
|
||||
|
||||
final request = verify(() => httpClient.send(captureAny()))
|
||||
.captured
|
||||
.single as http.MultipartRequest;
|
||||
|
||||
expect(
|
||||
request.url,
|
||||
codePushClient.hostedUri.replace(path: '/api/v1/patches'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('deleteApp', () {
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(
|
||||
() => httpClient.delete(any(), headers: any(named: 'headers')),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.badRequest));
|
||||
|
||||
expect(
|
||||
codePushClient.deleteApp(productId: productId),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('completes when request succeeds', () async {
|
||||
when(
|
||||
() => httpClient.delete(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.noContent));
|
||||
|
||||
await codePushClient.deleteApp(productId: productId);
|
||||
|
||||
final uri = verify(
|
||||
() => httpClient.delete(
|
||||
captureAny(),
|
||||
headers: any(named: 'headers'),
|
||||
),
|
||||
).captured.single as Uri;
|
||||
|
||||
expect(
|
||||
uri,
|
||||
codePushClient.hostedUri.replace(
|
||||
path: '/api/v1/apps/$productId',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('downloadEngine', () {
|
||||
const engineRevision = 'engine-revision';
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
400,
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
codePushClient.downloadEngine(engineRevision),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('sends a request to the correct url', () async {
|
||||
when(() => httpClient.send(any())).thenAnswer((_) async {
|
||||
return http.StreamedResponse(
|
||||
Stream.empty(),
|
||||
HttpStatus.ok,
|
||||
);
|
||||
});
|
||||
|
||||
await codePushClient.downloadEngine(engineRevision);
|
||||
|
||||
final request = verify(() => httpClient.send(captureAny()))
|
||||
.captured
|
||||
.single as http.Request;
|
||||
|
||||
expect(
|
||||
request.url,
|
||||
Uri.parse(
|
||||
'https://storage.googleapis.com/code-push-dev.appspot.com/engines/dev/engine.zip',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('getApps', () {
|
||||
test('throws an exception if the http request fails', () async {
|
||||
when(
|
||||
() => httpClient.get(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
),
|
||||
).thenAnswer((_) async => http.Response('', HttpStatus.badRequest));
|
||||
|
||||
expect(
|
||||
codePushClient.getApps(),
|
||||
throwsA(isA<Exception>()),
|
||||
);
|
||||
});
|
||||
|
||||
test('completes when request succeeds (empty)', () async {
|
||||
when(
|
||||
() => httpClient.get(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => http.Response(json.encode([]), HttpStatus.ok),
|
||||
);
|
||||
|
||||
final apps = await codePushClient.getApps();
|
||||
expect(apps, isEmpty);
|
||||
});
|
||||
|
||||
test('completes when request succeeds (populated)', () async {
|
||||
final expected = [
|
||||
App(
|
||||
productId: 'shorebird-example',
|
||||
releases: [
|
||||
Release(
|
||||
version: '1.0.0',
|
||||
patches: [
|
||||
Patch(
|
||||
number: 1,
|
||||
channels: ['stable'],
|
||||
artifacts: [
|
||||
Artifact(
|
||||
arch: 'aarm64',
|
||||
platform: 'android',
|
||||
url: 'http://localhost:8080',
|
||||
hash: '#',
|
||||
)
|
||||
],
|
||||
),
|
||||
Patch(
|
||||
number: 2,
|
||||
channels: ['stable', 'dev'],
|
||||
artifacts: [
|
||||
Artifact(
|
||||
arch: 'aarm64',
|
||||
platform: 'android',
|
||||
url: 'http://localhost:8080',
|
||||
hash: '#',
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Release(version: '2.0.0'),
|
||||
],
|
||||
),
|
||||
App(
|
||||
productId: 'shorebird-counter',
|
||||
releases: [
|
||||
Release(
|
||||
version: '1.0.0',
|
||||
patches: [
|
||||
Patch(
|
||||
number: 1,
|
||||
channels: ['stable'],
|
||||
artifacts: [
|
||||
Artifact(
|
||||
arch: 'aarm64',
|
||||
platform: 'android',
|
||||
url: 'http://localhost:8080',
|
||||
hash: '#',
|
||||
)
|
||||
],
|
||||
),
|
||||
Patch(
|
||||
number: 2,
|
||||
channels: ['stable', 'dev'],
|
||||
artifacts: [
|
||||
Artifact(
|
||||
arch: 'aarm64',
|
||||
platform: 'android',
|
||||
url: 'http://localhost:8080',
|
||||
hash: '#',
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Release(
|
||||
version: '1.0.1',
|
||||
patches: [
|
||||
Patch(
|
||||
number: 1,
|
||||
channels: ['stable'],
|
||||
artifacts: [
|
||||
Artifact(
|
||||
arch: 'aarm64',
|
||||
platform: 'android',
|
||||
url: 'http://localhost:8080',
|
||||
hash: '#',
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
when(
|
||||
() => httpClient.get(
|
||||
any(),
|
||||
headers: any(named: 'headers'),
|
||||
),
|
||||
).thenAnswer(
|
||||
(_) async => http.Response(json.encode(expected), HttpStatus.ok),
|
||||
);
|
||||
|
||||
final actual = await codePushClient.getApps();
|
||||
expect(json.encode(actual), equals(json.encode(expected)));
|
||||
});
|
||||
});
|
||||
|
||||
group('close', () {
|
||||
test('closes the underlying client', () {
|
||||
codePushClient.close();
|
||||
verify(() => httpClient.close()).called(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user