perlmojoliciousmodule-build

File uploaded with Test::Mojo post is empty


I have implemented a Mojolicious Web Service as a module that accepts file uploads via POST. An example cURL command:

curl -X POST http://localhost:3000/process -F inputFile=@file.txt

This works as expected, the file is processed and the result is returned.

I am now trying to test it using Test::Mojo like this:

my $t = Test::Mojo->new( 'TK::Proxy' );

my $data = {
    inputFile => { filename => 't/file.txt' },
};

$t->post_ok('/process' => form => $data)
    ->status_is(200)

The test fails:

$ ./Build test
[...]
#   Failed test '200 OK'
#   at t/20_app.t line 44.
#          got: '400'
#     expected: '200'

Debugging the code reveals that the uploaded content is empty.

I have verified that it finds the file by adding a simple print before the test:

open FILE,'<', 't/file.pdf' or die("Could not read file");

while (my $line = <FILE>) {
    print STDERR ($line . "\n");
}

This outputs the file as expected.

My conclusion is hence that the error is in the post_ok call and/or the structure of $data, but I could not figure out where. As far as I can tell, the call looks exactly like in the example given in the documentation.

This is how the file content is processed on the server-side:

my $self = shift()->openapi()->valid_input() or return;

my $input  = $self->validation()->output();

my $content;
eval {
    my $document = $input->{inputFile}->slurp;

    $content = $self->textractor()
        ->process(
            $input->{source},
            $input->{target},
            $document,
            _parse_runtime_params($input->{runtimeParams}),
        );
};

It turns out that the result of $input->{inputFile}->slurp; is an empty string for the test. In the cURL call, however, it correctly contains the file content.


Solution

  • The solution, as indicated by @Boroding, was indeed to replace fileName with file:

    my $data = {
      inputFile => { file => 't/file.txt' },
    };
    $t->post_ok('/process' => form => $data)->status_is(200);
    

    Presumably, the reason why this is missing in the documentation example is that test should not depend on external files. So the cleaner way to do this is:

    my $data = {
      inputFile => { content => "File content", fileName => 'file.txt' },
    };
    $t->post_ok('/process' => form => $data)->status_is(200);