Skip to content

Commit

Permalink
Bumps dart SDK to 2.12 and FFI to stable version
Browse files Browse the repository at this point in the history
Bumps dart SDK to 2.12
Bumps FFI to >1.0
Bumps pact native library to 0.1.1
Bumps dependencies to the latest version supporting the new SDK
Adapts code to take advantage of null safety
  • Loading branch information
pak3nuh authored Mar 21, 2023
1 parent 444b4ae commit 3014dc1
Show file tree
Hide file tree
Showing 23 changed files with 292 additions and 383 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@

- Introduces pact matching in unit tests
- Better documentation

## 0.2.0

- Bumps dart SDK to 2.12
- Updates dependencies to match dart SDK 2.12
- Enables null safety
- Bumps pact native library to 0.1.1
15 changes: 12 additions & 3 deletions DEV_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@ The bindings work with a specific version of the rust library. There are no comp
If all planets align is even possible to write code that runs, but gives incorrect results due to incompatible bindings.

## Pact DTO generation
`dart pub run build_runner build --delete-conflicting-outputs`
`dart run build_runner build --delete-conflicting-outputs`

## Unit test
Download the ffi library from github `dart run dart_pact_consumer:github_download`

Run `dart test`

## Integration tests

Docker setup in the [docker](./docker) folder.
Launch the pact broker container in the [compose](./docker/docker-compose.yaml) file.

Run the tests specifying the `PACT_HOST` flag in a `define` parameter. The test command doesn't respect the values
passed as environment ([issue](https://github.com/dart-lang/sdk/issues/44562)), so we need to run each test
individually with the `run` command.

Then launch the integration tests with `dart test integration_test`
Or use the script `ls integration_test | xargs -i dart -DPACT_HOST=[broker] run integration_test/{}`.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final serverFactory = await MockServerFactory.create();
The mock server requires an external library to launch, made by the team behind pact. This package uses defaults to
load such library but it needs to be downloaded previously.

To use the defaults run `dart pub run dart_pact_consumer:github_download`.
To use the defaults run `dart run dart_pact_consumer:github_download`.

### Define each interaction
```dart
Expand Down
2 changes: 1 addition & 1 deletion analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Defines a default set of lint rules enforced for
# projects at Google. For details and rationale,
# see https://github.com/dart-lang/pedantic#enabled-lints.
include: package:pedantic/analysis_options.yaml
include: package:pedantic_mono/analysis_options.yaml

# For lint rules and documentation, see http://dart-lang.github.io/linter/lints.
# Uncomment to specify additional rules.
Expand Down
39 changes: 21 additions & 18 deletions bin/github_download.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@

import 'dart:io';

const pactVersion = '0.1.1';

String _getDownloadLink() {
if (Platform.isLinux) {
return 'https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v0.0.17/libpact_mock_server_ffi-linux-x86_64.so.gz';
return 'https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v$pactVersion/libpact_mock_server_ffi-linux-x86_64.so.gz';
} else if (Platform.isMacOS) {
return 'https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v0.0.17/libpact_mock_server_ffi-osx-x86_64.dylib.gz';
return 'https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v$pactVersion/libpact_mock_server_ffi-osx-x86_64.dylib.gz';
} else if (Platform.isWindows) {
return 'https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v0.0.17/libpact_mock_server_ffi-windows-x86_64.dll.gz';
return 'https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v$pactVersion/libpact_mock_server_ffi-windows-x86_64.dll.gz';
}
throw Exception('Unsupported platform: ${Platform.operatingSystem}');
}

final _finalPath = Directory.systemTemp.path + '/libpact_mock_server_ffi-v0.0.17';
final _downloadPath = Directory.systemTemp.path + '/libpact_mock_server_ffi-v0.0.17.gz';
final _finalPath = '${Directory.systemTemp.path}/libpact_mock_server_ffi-v$pactVersion';
final _downloadPath = '${Directory.systemTemp.path}/libpact_mock_server_ffi-v$pactVersion.gz';

Future<String> downloadFromGithub() async {
if (await File(_finalPath).exists()) {
Expand All @@ -25,31 +27,32 @@ Future<String> downloadFromGithub() async {
return _finalPath;
}

void _unzip() async {
Future<void> _unzip() async {
print('Decompressing file $_downloadPath');
var bytes = await File(_downloadPath).readAsBytes();
var decoded = gzip.decode(bytes);
var writeStream = File(_finalPath).openWrite();
await writeStream.add(decoded);
final bytes = await File(_downloadPath).readAsBytes();
final decoded = gzip.decode(bytes);
print('Writing file $_finalPath');
final writeStream = File(_finalPath).openWrite();
writeStream.add(decoded);
await writeStream.close();
}

void _download() async {
var file = File(_downloadPath);
Future<void> _download() async {
final file = File(_downloadPath);
if (await file.exists()) {
print('GZ file exists');
return;
}

var link = _getDownloadLink();
final link = _getDownloadLink();
print('Downloading gz file: $link');
var request = await HttpClient().getUrl(Uri.parse(link));
var response = await request.close();
var streamConsumer = file.openWrite();
final request = await HttpClient().getUrl(Uri.parse(link));
final response = await request.close();
final streamConsumer = file.openWrite();
await response.pipe(streamConsumer);
await streamConsumer.close();
}

void main() {
downloadFromGithub();
Future<void> main() async {
await downloadFromGithub();
}
3 changes: 1 addition & 2 deletions build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ targets:
field_rename: none
generic_argument_factories: false
ignore_unannotated: false
include_if_null: false
nullable: true
include_if_null: false
8 changes: 0 additions & 8 deletions docker/Dockerfile

This file was deleted.

13 changes: 9 additions & 4 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
version: "3"
services:
local-pact-broker:
build:
context: .
dockerfile: Dockerfile
image: pactfoundation/pact-broker:2.79.0.1
environment:
#https://hub.docker.com/r/pactfoundation/pact-broker
- PACT_BROKER_PORT=9292
# change when final db is available
- PACT_BROKER_DATABASE_ADAPTER=sqlite
- PACT_BROKER_LOG_LEVEL=INFO
- PACT_BROKER_SQL_LOG_LEVEL=DEBUG
ports:
- 9292:9292
- "9292:9292"
11 changes: 10 additions & 1 deletion integration_test/pact_host_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ import 'package:dart_pact_consumer/src/pact_host_client.dart';
import 'package:test/test.dart';

void main() {
const pactHostEnv = 'PACT_HOST';
assert(
const bool.hasEnvironment(pactHostEnv),
'Please define the PACT_HOST env variable',
);
const hostAddress = String.fromEnvironment(pactHostEnv);

group('PactHost', () {
const host = 'http://localhost:9292';
const host = 'http://$hostAddress';
final client = PactHost(host);

test('should get contracts', () async {
Expand All @@ -15,6 +22,8 @@ void main() {
'pet-shop-api-provider', '0.0.1', 'my-special-tag');

await client.addLabel('pet-shop-api-provider', 'my-label');

client.close(force: true);
});
});
}
14 changes: 10 additions & 4 deletions integration_test/pact_publish_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@ import 'package:dart_pact_consumer/src/pact_host_client.dart';
import 'package:test/test.dart';

void main() async {
const brokerUrl = 'http://localhost:9292';
const pactHostEnv = 'PACT_HOST';
assert(
const bool.hasEnvironment(pactHostEnv),
'Please define the PACT_HOST env variable',
);
const hostAddress = String.fromEnvironment(pactHostEnv);

const brokerUrl = 'http://$hostAddress';
final serverFactory = await MockServerFactory.create();

group('ContractBuilder for pact broker', () {
final host = PactHost(brokerUrl);
final repo = PactRepository();

PactBuilder petShopApiBuilder() {
return PactBuilder()
..consumer = 'dart-consumer'
..provider = 'pet-shop-api-provider';
return PactBuilder('dart-consumer', 'pet-shop-api-provider');
}

test('should get pet list', () async {
Expand Down Expand Up @@ -76,6 +81,7 @@ void main() async {
final closed = serverFactory.close();
expect(closed, isTrue);
await repo.publish(host, '0.0.1');
host.close(force: true);
});
});

Expand Down
41 changes: 16 additions & 25 deletions lib/src/contract_builder_api.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:convert';

import 'package:dart_pact_consumer/dart_pact_consumer.dart';
import 'package:dart_pact_consumer/src/ffi/rust_mock_server.dart';
import 'package:dart_pact_consumer/src/functional.dart';
import 'package:dart_pact_consumer/src/json_serialize.dart';
Expand Down Expand Up @@ -37,7 +36,7 @@ class PactRepository {
}

/// Gets a pact file in JSON format
String getPactFile(String consumer, String provider) {
String? getPactFile(String consumer, String provider) {
return _pacts[_key(consumer, provider)].let((value) {
return jsonEncode(value);
});
Expand Down Expand Up @@ -99,8 +98,9 @@ class RequestTester {

RequestTester._(this._stateBuilder);

void test(MockServerFactory factory, RequestTestFunction testFunction) async {
final pactBuilder = PactBuilder()..stateBuilders.add(_stateBuilder);
Future<void> test(MockServerFactory factory, RequestTestFunction testFunction) async {
final pactBuilder = PactBuilder("mock-consumer", "mock-provider")
..stateBuilders.add(_stateBuilder);
final pact = PactRepository._createHeader(pactBuilder);
PactRepository._mergeInteractions(pactBuilder, pact);
final server = factory.createMockServer(pact.interactions[0]);
Expand Down Expand Up @@ -132,11 +132,11 @@ class RequestTester {
/// . Generators
/// . Encoders
class PactBuilder {
String consumer;
String provider;
final String consumer;
final String provider;
final List<StateBuilder> _states = [];

PactBuilder();
PactBuilder(this.consumer, this.provider);

List<StateBuilder> get stateBuilders => _states;

Expand All @@ -149,23 +149,19 @@ class PactBuilder {
}

void validate({bool requireTests = true}) {
assert(consumer != null);
assert(provider != null);
stateBuilders.forEach((element) => element._validate(requireTests));
}
}

enum Method { GET, POST, DELETE, PUT }

class StateBuilder {
String state;
late String state;
bool _tested = false;

final List<RequestBuilder> requests = [];

void _validate(bool requireTests) {
assert(state != null);
assert(requests != null);
assert(requests.isNotEmpty);
if (requireTests && !_tested) {
throw PactException('State "$state" not tested');
Expand Down Expand Up @@ -197,7 +193,7 @@ class RequestBuilder {

String description = '';
Method method = Method.GET;
ResponseBuilder _response;
late ResponseBuilder _response;

Map<String, String> query = {};

Expand All @@ -213,13 +209,7 @@ class RequestBuilder {
}

void _validate() {
assert(_path != null);
assert(_path != '');
assert(query != null);
assert(method != null);
assert(_response != null);
assert(body != null);
assert(headers != null);
_response._validate();
}

Expand All @@ -234,9 +224,6 @@ class ResponseBuilder {
Body body = Body.empty();

void _validate() {
assert(headers != null);
assert(status != null);
assert(body != null);
}

ResponseBuilder._();
Expand All @@ -263,11 +250,15 @@ class Body extends Union3<Json, String, Unit> implements CustomJson {

@override
dynamic toJson() {
return fold(
(js) => js.toJson(),
final result = fold<Object>(
(js) => js.toJson() as Object,
(str) => str,
(unit) => unit.toJson(),
(unit) => unit
);
if (result is Unit) {
return null;
}
return result;
}

static Body fromJsonToBody(dynamic body) {
Expand Down
5 changes: 1 addition & 4 deletions lib/src/ffi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

- allocate and free are manual. not good
- need native libraries for os/arch
- RUST ffi https://docs.rs/pact_mock_server_ffi/0.0.17/pact_mock_server_ffi
- RUST ffi https://docs.rs/pact_mock_server_ffi/0.1.1/pact_mock_server_ffi
- foreign code runs on an Isolate base. may be useful to clean resources
- PACT_MOCK_LOG_LEVEL variable name
- server ports are used to differentiate interactions on pacts.
Expand All @@ -17,6 +17,3 @@
## Conclusions
### ffi only support custom structs as of 2.12
Can't build pact file with library.

### ffi breaks as of 2.12 with current bindings
2.12 breaks compatibility anyways with null safe semantics
Loading

0 comments on commit 3014dc1

Please sign in to comment.