microcks / microcks-testcontainers-java

Java lib for Testcontainers that enables embedding Microcks into your JUnit tests with lightweight, throwaway instance thanks to containers.
https://microcks.io
Apache License 2.0
22 stars 5 forks source link

Contract Tests - JsonMappingException when adding operations headers #18

Closed mathieu-amblard closed 1 year ago

mathieu-amblard commented 1 year ago

Describe the bug

First of all, thank you for your great work about Microcks !

I am playing around with it to replace PACT testing that does not suit our needs. I am trying to execute contract tests as defined in this example : https://github.com/microcks/microcks-testcontainers-java#launching-new-contract-tests and some of the operations required technical headers.

Therefore I have setup my test like this :

    @Test
    void validate_open_api_contract() throws Exception {
        Header header1= new Header();
        header1.setName("my-required-header-1");
        header1.setValues("value-1");
        Header header2 = new Header();
        header2 .setName("my-required-header-2");
        header2 .setValues("value-2");
        TestRequest testRequest = new TestRequest.Builder()
                .serviceId("some-api:1.0")
                .runnerType(TestRunnerType.OPEN_API_SCHEMA.name())
                .testEndpoint("http://host.testcontainers.internal:" + port + "/path")
                .filteredOperations(List.of("GET /resources}"))
                .operationsHeaders(Map.of("GET /resources", List.of(header1, header2)))
                .build();
        TestResult testResult = microcks.testEndpoint(testRequest);
        ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
        System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(testResult));
        assertThat(testResult.isSuccess()).isTrue();
    }

When I execute, I got a org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException due to headers.

I am using io.github.microcks:microcks-testcontainers version 0.1.3.

Expected behavior

I expect the deserialization of the response to a TestResult works.

Actual behavior

When Microcks tries to deserialize the response to a TestResult, I got the following exception :

org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_ARRAY token
 at [Source: {"id":"651ea7baa485fb277f71cddb","version":0,"testNumber":1,"testDate":1696507834409,"testedEndpoint":"http://host.testcontainers.internal:33907/path","serviceId":"651ea7b9a485fb277f71cdd2","timeout":10000,"elapsedTime":0,"success":false,"inProgress":true,"runnerType":"OPEN_API_SCHEMA","operationsHeaders":{"GET /resources}":[{"name":"my-required-header-1","values":["value-1"]},{"name":"my-required-header-2","values":["value-2"]}]},"testCaseResults":[{"success":false,"elapsedTime":-1,"operationName":"GET /resources}","testStepResults":[]}]}; line: 1, column: 380] (through reference chain: io.github.microcks.testcontainers.model.TestResult["operationsHeaders"]->io.github.microcks.testcontainers.model.OperationsHeaders["GET /resources}"]->java.util.HashSet[0]->io.github.microcks.testcontainers.model.Header["values"])

    at org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.java:1234)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1122)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.StringDeserializer._deserializeFromArray(StringDeserializer.java:91)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:41)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:287)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:259)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:517)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:362)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:27)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
    at org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
    at io.github.microcks.testcontainers.MicrocksContainer.testEndpoint(MicrocksContainer.java:203)
    at io.github.microcks.testcontainers.MicrocksContainer.testEndpoint(MicrocksContainer.java:165)

This is because, the mapper tries to deserialize the array ["value-2"] to the field values : https://github.com/microcks/microcks-testcontainers-java/blob/main/src/main/java/io/github/microcks/testcontainers/model/Header.java#L42 which is a String.

As a quick workaround, I have created a custom deserializer but I though that Jackson was able to handle it by default...

package io.github.microcks.testcontainers.model;

import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonParser;
import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException;
import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonToken;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.DeserializationContext;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonDeserializer;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Header {

    private String name;
    private String values;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValues() {
        return values;
    }

    @JsonDeserialize(using = ArrayToStringDeserializer.class)
    public void setValues(String values) {
        this.values = values;
    }
}

class ArrayToStringDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        if (jsonParser.currentToken() == JsonToken.START_ARRAY) {
            List<String> values = new ArrayList<>();
            jsonParser.nextToken();
            while (jsonParser.hasCurrentToken() && jsonParser.currentToken() != JsonToken.END_ARRAY) {
                values.add(jsonParser.getValueAsString());
                jsonParser.nextToken();
            }
            return String.join(",", values);
        }
        return null;
    }
}

How to Reproduce?

No response

Microcks version or git rev

No response

Install method (docker-compose, helm chart, operator, docker-desktop extension,...)

No response

Additional information

No response

github-actions[bot] commented 1 year ago

👋 @mathieu-amblard

Welcome to the Microcks community! 💖

Thanks and congrats 🎉 for opening your first issue here! Be sure to follow the issue template or please update it accordingly.

📢 If you're using Microcks in your organization, please add your company name to this list. 🙏 It really helps the project to gain momentum and credibility. It's a small contribution back to the project with a big impact.

If you need to know why and how to add yourself to the list, please read the blog post "Join the Microcks Adopters list and Empower the vibrant open source Community 🙌"

Hope you have a great time there!

🌟 ~~~~~ 🌟

📢 If you like Microcks, please ⭐ star ⭐ our repo to support it!

🙏 It really helps the project to gain momentum and credibility. It's a small contribution back to the project with a big impact.

lbroudoux commented 1 year ago

Hey! Thank you for trying it out and sorry for the bug... We still have to enrich the test suite. I an going to check it out right now.

lbroudoux commented 1 year ago

I think I had the same belief that it would be handled by default by Jackson... Anyway, you spotted the issue and the fix. Cool! 😎 Would you be interested in contributing the fix with a PR? If you don't have time, I'll push it by myself. Let me know.

mathieu-amblard commented 1 year ago

@lbroudoux I would be interested in contributing. I will have some time tomorrow for creating a PR if that's ok for you. Thanks.

lbroudoux commented 1 year ago

Awesome! I'll release new version as soon as I get your PR.

lbroudoux commented 1 year ago

@mathieu-amblard one thing: do not forget to sign your commit 😉