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

Add support to lists #1

Merged
merged 1 commit into from
Mar 24, 2020
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,6 @@ tools/**
*.xsd.cs

# diff
*.orig
*.orig

*.DS_Store
101 changes: 101 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Contributing to Pactify

Do you have great ideas which will make Pactify even better? That's awesome! Feel free to create **issues or pull request** here on GitHub!

The purpose of this text is to help you start working with the source code without any trouble. Simply, follow the steps below and... have fun :)

## Clone the repository
In order to start working with Pactify, you need to clone the repository from GitHub using the following command:
```
git clone https://github.com/GooRiOn/Pactify.git
```

## Create new branch
After you're done make sure you are one the **develop** branch:
```
git checkout develop
```

Then you can create your own branch for new feature, bug fix or whatever you want. We use very simple naming convention for naming branches:
```
feature/<issue_number_from_github>
```

However, if there's no issue related to your feature you can put the short description instead using **snake case** like in the example below:
```
feature/my_new_awesome_validation_rule
```

Having a proper name for your branch, create it directly from the develop:
```
git checkout -b <name_of_your_branch>
```

## Creating unit tests
We do our best to make Pactify a reliable library. That's why we pay attention to unit tests for each new functionality. You can find them inside ```tests``` folder. Select a subfolder which should contain new unit tests or create new if none of them suits new functionality. The name of each unit test should follow the convention:
```
Method_Result_When_Condition
```

Here's an example:
```
Float_IsPositive_Fails_When_Given_Value_Is_Null
```

Try to avaoid multiple ```Assert``` inside single unit tests.

When you're done make sure all tests passess. Navigate to the ```Pactify.Tests``` project and run the following command:
```
dotnet test
```

You can also run the following command for continuous testing:
```
dotnet watch test // this will compile the project and rerun the tests on every file change
```

Alternatively, you can use our **Cake script** which is placed in the root folder. Navigate there and run:
```
./build.sh //on Unix
```
```
./build.ps1 //on Windows
```

## Creating a pull request
When the code is stable, you can submit your changes by creating a pull request. First, push your branch to origin:
```
git push origin <name_of_your_branch>
```

Then go to the **GitHub -> Pull Request -> New Pull Request**.
Select **develop** as base and your branch as compare. We provide default template for PR description:

![PR_Template](http://foreverframe.net/wp-content/uploads/2017/09/Screen-Shot-2017-09-27-at-21.16.02.png)

Make sure:
- PR title is short and concludes work you've done
- GitHub issue number and link is inluded in the description
- You described changes to the codebase

When it's done, simply create your pull request. We use AppVeyor as CI system and Codecov as code coverage analyzer. After you push your chnges these two tools will take a look at your code. First, the AppVeyor will check whether project builds and all unit tests passess. Then Codecov bot will post a short report which will present code coverage after your changes:

![CC_Report](http://foreverframe.net/wp-content/uploads/2017/10/Screen-Shot-2017-10-22-at-13.13.15.png)

Each PR must fulfill certain conditions before it can be merged:

- The build must succeed on AppVeyor
- Code coverage can't be discreassed by the PR
- One of the owners must approve your changes


If some of the above won't be fulfilled (due to change request or some mistake) simply fix it locally on your machine, create new commit and push it to origin. This will update the exisitng PR and will kick off all checks again.

If everything will be fine, your changes will be merged into develop, branch will be deleted and related issue will be closed.

# WELL DONE AND THANK YOU VERY MUCH!





22 changes: 21 additions & 1 deletion src/Pactify/Builders/Http/HttpPactResponseBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.Serialization;
using Pactify.Definitions.Http;

Expand Down Expand Up @@ -37,7 +42,22 @@ public IHttpPactResponseBuilder WithBody(object body)
}

public IHttpPactResponseBuilder WithBody<TBody>()
=> WithBody(FormatterServices.GetUninitializedObject(typeof(TBody)));
{
var isListType = typeof(TBody).GetMethods().FirstOrDefault(x => x.Name.Equals("Add")) != null;

if (isListType)
{
var listItemType = typeof(TBody).GetGenericArguments().Single();
var list = new List<object>
{
Activator.CreateInstance(listItemType)
};

return WithBody(list);
}

return WithBody(FormatterServices.GetSafeUninitializedObject(typeof(TBody)));
}

public HttpPactResponse Build()
=> _pactResponse;
Expand Down
33 changes: 28 additions & 5 deletions src/Pactify/Verifiers/HttpInteractionVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Pactify.Definitions;
using Newtonsoft.Json.Linq;
using Pactify.Definitions.Http;
using Pactify.Messages;
using SmartFormat;
Expand All @@ -30,7 +30,9 @@ public async Task<PactVerificationResult> VerifyAsync(HttpInteractionDefinition
: Smart.Format(definition.Request.Path, templateObject);
var httpResponse = await getResult(requestPath);
var json = await httpResponse.Content.ReadAsStringAsync();
var providedBody = JsonConvert.DeserializeObject<ExpandoObject>(json);

object providedBody = GetProvidedBody(json);

var expectedBody = definition.Response.Body;
var errors = new List<string>();

Expand All @@ -46,6 +48,19 @@ public async Task<PactVerificationResult> VerifyAsync(HttpInteractionDefinition
return new PactVerificationResult(errors);
}

private static object GetProvidedBody(string json)
{
var data = JsonConvert.DeserializeObject(json);
object providedBody;

if (data is JArray)
providedBody = JsonConvert.DeserializeObject<List<ExpandoObject>>(json);
else
providedBody = JsonConvert.DeserializeObject<ExpandoObject>(json);

return providedBody;
}

private static void VerifyStatusCode(HttpInteractionDefinition definition, HttpResponseMessage response, List<string> errors)
{
if(response.StatusCode != definition.Response.Status)
Expand Down Expand Up @@ -83,17 +98,25 @@ private static string GetErrorMessage(string message, params object[] messagePar
=> string.Format(message, messageParams);

private static void VerifyBody(PactDefinitionOptions options, object expectedBody,
IDictionary<string, object> providedBody, List<string> errors)
object providedBody, List<string> errors)
{
foreach (var pair in (IDictionary<string, object>)expectedBody)
var provBody = (providedBody is IEnumerable<ExpandoObject>) ?
(providedBody as IEnumerable<ExpandoObject>).First() :
providedBody as ExpandoObject;

var expecBody = (expectedBody is IEnumerable<object>) ?
(expectedBody as IEnumerable<object>).First() as ExpandoObject:
expectedBody as ExpandoObject;

foreach (var pair in expecBody)
{
var stringComparision = options.IgnoreCasing
? StringComparison.InvariantCultureIgnoreCase
: StringComparison.InvariantCulture;
var propertyName = pair.Key;
var propertyValue = pair.Value;

var providedProperty = providedBody.FirstOrDefault(p => p.Key.Equals(propertyName, stringComparision)).Value;
var providedProperty = provBody.FirstOrDefault(p => p.Key.Equals(propertyName, stringComparision)).Value;

if (providedProperty is null)
{
Expand Down
37 changes: 37 additions & 0 deletions tests/Pactify.UnitTests/GeneralTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
Expand Down Expand Up @@ -32,6 +33,31 @@ await PactMaker
.MakeAsync();
}

[Fact]
public async Task Consumer_Should_Create_APact_For_Lists()
{
var options = new PactDefinitionOptions
{
IgnoreContractValues = true,
IgnoreCasing = true
};

await PactMaker
.Create(options)
.Between("orders", "parcels")
.WithHttpInteraction(cb => cb
.Given("There is a list of parcels")
.UponReceiving("A GET Request to retrieve the parcels")
.With(request => request
.WithMethod(HttpMethod.Get)
.WithPath("api/parcels"))
.WillRespondWith(response => response
.WithStatusCode(HttpStatusCode.OK)
.WithBody<List<ParcelReadModel>>()))
.PublishedAsFile("./Pacts")
.MakeAsync();
}

[Fact]
public async Task Provider_Should_Meet_Consumers_Expectations()
{
Expand All @@ -42,5 +68,16 @@ await PactVerifier
.RetrievedViaHttp("http://localhost:9292/pacts/provider/parcels/consumer/orders/latest")
.VerifyAsync();
}

[Fact]
public async Task Provider_Should_Meet_Consumers_List_Expectations()
{
await PactVerifier
.CreateFor<Startup>()
.UseEndpointTemplate(new ParcelReadModel())
.Between("orders", "parcels")
.RetrievedFromFile("./Pacts")
.VerifyAsync();
}
}
}