spring-cloud / spring-cloud-contract

Support for Consumer Driven Contracts in Spring
https://cloud.spring.io/spring-cloud-contract
Apache License 2.0
719 stars 438 forks source link

Bug (pact-to-scc): If pact contains array, SCC generates isEmpty() check which will always fail the test #1043

Closed schustes closed 4 years ago

schustes commented 5 years ago

This might relate to #797 and I checked already with 2.1.1.RELEASE, because I thought this should resolve the issue. But it still persists, so I thought it may have to do with the translation from pact to scc contracts.

Problem: When a pact contains an array (of objects, this is what I tested), then SCC generates an isEmpty() check on this array. This does not correspond to the pact and also not the example in the pact.

This is the pact file:

    "provider": {
        "name": "book-catalog-service"
    },
    "consumer": {
        "name": "books-client-catalog-rest-consumer"
    },
    "interactions": [
        {
            "description": "A successful Api GET call",
            "request": {
                "method": "GET",
                "path": "/books",
                "headers": {
                    "Content-Type": "application/json;charset=UTF-8"
                }
            },
            "response": {
                "status": 200,
                "headers": {
                    "Content-Type": "application/json;charset=UTF-8"
                },
                "body": [
                    {
                        "isbn": "978-3-86680-192-9",
                        "id": 100,
                        "title": "A book",
                        "authors": [
                            {
                                "firstName": "string",
                                "lastName": "string"
                            }
                        ]
                    }
                ],
                "matchingRules": {
                    "body": {
                        "$[0].id": {
                            "matchers": [
                                {
                                    "match": "integer"
                                }
                            ],
                            "combine": "AND"
                        },
                        "$[0].title": {
                            "matchers": [
                                {
                                    "match": "regex",
                                    "regex": ".*"
                                }
                            ],
                            "combine": "AND"
                        },
                        "$[0].authors[0].firstName": {
                            "matchers": [
                                {
                                    "match": "type"
                                }
                            ],
                            "combine": "AND"
                        },
                        "$[0].authors[0].lastName": {
                            "matchers": [
                                {
                                    "match": "type"
                                }
                            ],
                            "combine": "AND"
                        },
                        "$[0].isbn": {
                            "matchers": [
                                {
                                    "match": "regex",
                                    "regex": "[0-9]{3}-[0-9]{1}-[0-9]{5}-[0-9]{3}-[0-9]{1}"
                                }
                            ],
                            "combine": "AND"
                        }
                    }
                },
                "generators": {
                    "body": {
                        "$[0].id": {
                            "type": "RandomInt",
                            "min": 0,
                            "max": 2147483647
                        },
                        "$[0].authors[0].firstName": {
                            "type": "RandomString",
                            "size": 20
                        },
                        "$[0].authors[0].lastName": {
                            "type": "RandomString",
                            "size": 20
                        }
                    }
                }
            },
            "providerStates": [
                {
                    "name": "get"
                }
            ]
        }
    ],
    "metadata": {
        "pact-specification": {
            "version": "3.0.0"
        },
        "pact-jvm": {
            "version": "3.5.13"
        }
    }
} 

Here is the generated contract test:

    public void validate_0_book_catalog_service_pact_0() throws Exception {
        // given:
            MockMvcRequestSpecification request = given()
                    .header("X-ROLE", "unprivileged use")
                    .header("Content-Type", "application/json;charset=UTF-8");

        // when:
            ResponseOptions response = given().spec(request)
                    .get("/books");

        // then:
            assertThat(response.statusCode()).isEqualTo(200);
            assertThat(response.header("Content-Type")).isEqualTo("application/json;charset=UTF-8");
        // and:
            DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
            assertThatJson(parsedJson).array().array("['authors']").isEmpty();
        // and:
            assertThat(parsedJson.read("$[0].id", String.class)).matches("-?(\\d+)");
            assertThat(parsedJson.read("$[0].title", String.class)).matches(".*");
            assertThat((Object) parsedJson.read("$[0].authors[0].firstName")).isInstanceOf(java.util.regex.Pattern.class);
            assertThat((Object) parsedJson.read("$[0].authors[0].lastName")).isInstanceOf(java.util.regex.Pattern.class);
            assertThat(parsedJson.read("$[0].isbn", String.class)).matches("[0-9]{3}-[0-9]{1}-[0-9]{5}-[0-9]{3}-[0-9]{1}");
    }
marcingrzejszczak commented 5 years ago

Can you please check out the latest snapshots?

schustes commented 5 years ago

I've tried with 2.2.0.BUILD-SNAPSHOT, also cleaned all old gradle artifacts to be sure that nothing else is be on the classpath. The result is the same (my trials can be found at https://github.com/schustes/sidion-contract-examples/tree/nested-pact-objects/books-catalog-service-pact-provided-scc-test).

marcingrzejszczak commented 4 years ago

I couldn't replicate it with neither 2.1.x nor master (2.2.0). This is the generated test from the master branch

package example;

import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import org.junit.Test;
import org.junit.Rule;
import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
import io.restassured.response.ResponseOptions;

import static org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions.assertThat;
import static org.springframework.cloud.contract.verifier.util.ContractVerifierUtil.*;
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
import static io.restassured.module.mockmvc.RestAssuredMockMvc.*;

@SuppressWarnings("rawtypes")
public class ContractVerifierTest {

    @Test
    public void validate_pact_1043() throws Exception {
        // given:
            MockMvcRequestSpecification request = given()
                    .header("Content-Type", "application/json;charset=UTF-8");

        // when:
            ResponseOptions response = given().spec(request)
                    .get("/books");

        // then:
            assertThat(response.statusCode()).isEqualTo(200);
            assertThat(response.header("Content-Type")).isEqualTo("application/json;charset=UTF-8");

        // and:
            DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());

        // and:
            assertThat(parsedJson.read("$[0].id", String.class)).matches("-?(\\d+)");
            assertThat(parsedJson.read("$[0].title", String.class)).matches(".*");
            assertThat((Object) parsedJson.read("$[0].authors[0].firstName")).isInstanceOf(java.util.regex.Pattern.class);
            assertThat((Object) parsedJson.read("$[0].authors[0].lastName")).isInstanceOf(java.util.regex.Pattern.class);
            assertThat(parsedJson.read("$[0].isbn", String.class)).matches("[0-9]{3}-[0-9]{1}-[0-9]{5}-[0-9]{3}-[0-9]{1}");
    }

}