fnproject/fdk-java

Input from test.json does not pass a simple string to java method correctly

Opened this issue · 3 comments

Considering the following function class

public class PojoFunction {
    public Object greet(String name) {
        if (name == null || name.isEmpty()) {
            name = "World";
        }
        return new Greeting(name);
    }
    public static class Greeting {
        public final String name;
        public Greeting(String name) {
            this.name = name;
        }
    }
}

and this content for test.json

{
    "tests": [
        {
            "input": {"body": "Johnny"},
            "output": {"body": {"name": "Johnny"}}
        }
    ]
}

causes a test failure.

Test fails due to the String value in the greet function being of the value "Johnny" instead of value Johnny.

Console output:

FAILED           mismatched output found.
                 expected:
                 {"name":"Johnny"}
                 got:
                 HTTP/1.1 200 INVOKED
                 Content-Type: application/json
                 Content-length: 21
                 
                 {"name":"\"Johnny\""}

Thanks for raising. This is intended behaviour (see explanation below)

Looking a bit deeper the verbatim frame being sent to the container is :

                 POST hello
		 Content-Length: 8
		 Content-Type:  application/json
....

		 "Johnny"

The CLI is sending a raw json doc of "Johnny" which the FDK is trying to coerce to a string.

The FDK coerces inputs to paramers based on a set of (configurable) coercion rules based on the type of the parameter and (e.g.) the content type of the input.

When the content type is application/json and the param type is a String, the FDK has a choice of either giving you the raw JSON doc ("Johnny") or to assume that the doc is only a JSON string (and not an object or number or array) and to unmarshal it , giving you the JSON value Johnny

We chose the former (Strings and byte array parameter types are assumed to be copies of the body of the request) as it creates more intuitive behaviour when the doc is an object (which would otherwise cause JSON error).

You can override this behaviour (always prefer JSON unmarshalling):

Import the FDK runtime as a maven runtime dep.

  <dependency>
            <groupId>com.fnproject.fn</groupId>
            <artifactId>runtime</artifactId>
            <version>${fnproject.version}</version>
        </dependency>

In your function add an @InputBinding annotation to the input parameter that forces the JacksonCoercion (this will then skip string coercion)

  import com.fnproject.fn.runtime.coercion.jackson.JacksonCoercion; 
  import com.fnproject.fn.api.InputBinding; 

  public Object greet(@InputBinding(JacksonCoercion.class) String name) {

Hope that helps

Ok. It helped as far a the generated JSON to look the same as the JSON declared as the body in the test. What it does not help is in succeeding the test.

The full output, I've cut it to size previously due to the "obvious" discrepancy in the expected JSON body output.

Test 1
In gomega FailHandler: Actual 'HTTP/1.1 200 INVOKED
Content-Type: application/json
Content-length: 17

{"name":"Johnny"}' should be valid JSON, but it is not.
Underlying error:invalid character 'H' looking for beginning of value
FAILED           mismatched output found.
                 expected:
                 {"name":"Johnny"}
                 got:
                 HTTP/1.1 200 INVOKED
                 Content-Type: application/json
                 Content-length: 17
                 
                 {"name":"Johnny"}
                 logs:
                 
 -    ( 1.352177808s )

To me it looks like that the evaluation of the response does not extract the response body correctly in order to check against the expected body (as declared in file test.json).

yeah this looks like an issue with the cli - fnproject/cli#184