diff --git a/.gitignore b/.gitignore index ec7b0cc..5202850 100644 --- a/.gitignore +++ b/.gitignore @@ -313,4 +313,6 @@ tools/** *.xsd.cs # diff -*.orig \ No newline at end of file +*.orig + +*.DS_Store \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4365773 --- /dev/null +++ b/CONTRIBUTING.md @@ -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/ +``` + +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 +``` + +## 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 +``` + +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! + + + + + diff --git a/src/Pactify/Builders/Http/HttpPactResponseBuilder.cs b/src/Pactify/Builders/Http/HttpPactResponseBuilder.cs index 28e4a93..ae949ca 100644 --- a/src/Pactify/Builders/Http/HttpPactResponseBuilder.cs +++ b/src/Pactify/Builders/Http/HttpPactResponseBuilder.cs @@ -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; @@ -37,7 +42,22 @@ public IHttpPactResponseBuilder WithBody(object body) } public IHttpPactResponseBuilder WithBody() - => 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 + { + Activator.CreateInstance(listItemType) + }; + + return WithBody(list); + } + + return WithBody(FormatterServices.GetSafeUninitializedObject(typeof(TBody))); + } public HttpPactResponse Build() => _pactResponse; diff --git a/src/Pactify/Verifiers/HttpInteractionVerifier.cs b/src/Pactify/Verifiers/HttpInteractionVerifier.cs index b64ee9c..d4606f8 100644 --- a/src/Pactify/Verifiers/HttpInteractionVerifier.cs +++ b/src/Pactify/Verifiers/HttpInteractionVerifier.cs @@ -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; @@ -30,7 +30,9 @@ public async Task VerifyAsync(HttpInteractionDefinition : Smart.Format(definition.Request.Path, templateObject); var httpResponse = await getResult(requestPath); var json = await httpResponse.Content.ReadAsStringAsync(); - var providedBody = JsonConvert.DeserializeObject(json); + + object providedBody = GetProvidedBody(json); + var expectedBody = definition.Response.Body; var errors = new List(); @@ -46,6 +48,19 @@ public async Task 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>(json); + else + providedBody = JsonConvert.DeserializeObject(json); + + return providedBody; + } + private static void VerifyStatusCode(HttpInteractionDefinition definition, HttpResponseMessage response, List errors) { if(response.StatusCode != definition.Response.Status) @@ -83,9 +98,17 @@ private static string GetErrorMessage(string message, params object[] messagePar => string.Format(message, messageParams); private static void VerifyBody(PactDefinitionOptions options, object expectedBody, - IDictionary providedBody, List errors) + object providedBody, List errors) { - foreach (var pair in (IDictionary)expectedBody) + var provBody = (providedBody is IEnumerable) ? + (providedBody as IEnumerable).First() : + providedBody as ExpandoObject; + + var expecBody = (expectedBody is IEnumerable) ? + (expectedBody as IEnumerable).First() as ExpandoObject: + expectedBody as ExpandoObject; + + foreach (var pair in expecBody) { var stringComparision = options.IgnoreCasing ? StringComparison.InvariantCultureIgnoreCase @@ -93,7 +116,7 @@ private static void VerifyBody(PactDefinitionOptions options, object expectedBod 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) { diff --git a/tests/Pactify.UnitTests/GeneralTests.cs b/tests/Pactify.UnitTests/GeneralTests.cs index a227a67..6210a37 100644 --- a/tests/Pactify.UnitTests/GeneralTests.cs +++ b/tests/Pactify.UnitTests/GeneralTests.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading.Tasks; @@ -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>())) + .PublishedAsFile("./Pacts") + .MakeAsync(); + } + [Fact] public async Task Provider_Should_Meet_Consumers_Expectations() { @@ -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() + .UseEndpointTemplate(new ParcelReadModel()) + .Between("orders", "parcels") + .RetrievedFromFile("./Pacts") + .VerifyAsync(); + } } }