1

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.

Carsten
  • 1,912
  • 1
  • 28
  • 55
  • Can you show the relevant application code as well please? How is Module::Build related to this? I don't see the relevance. – simbabque Jun 15 '18 at 14:49
  • I suppose Module::Build is not related, it just causes the test to be run with `./Build test`. However, I was not sure there might be a non-obvious relation. I've added the relevant server-side code. – Carsten Jun 15 '18 at 14:52
  • Why are you accessing a URL path `/process` with curl, but `/v1/documents/process` in your Perl code? Likewise, why are you sending `file.txt` with curl but `t/file.txt` with Perl? I suspect the problem is that you are using `filename` instead of `file` to specify the content of the `inputFile` form field. Without either `file` or `content` there is nothing to specify the data to be sent. If you write `inputFile => { file => 't/file.txt' }` then Mojolicious will add appropriate `filename` and `Content-Type` values. – Borodin Jun 15 '18 at 18:38
  • @Borodin thanks, I had shortened the endpoints for readability, but unfortunately not consistently; fixed. It seems like you are right that it needs to be `file` instead of `fileName`, but I could not try yet. By following the inheritance, I have been able to locate a probably suitable example in the Mojo::UserAgent documentation (https://mojolicious.org/perldoc/Mojo/UserAgent/Transactor#tx). – Carsten Jun 16 '18 at 11:07

1 Answers1

1

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);
Carsten
  • 1,912
  • 1
  • 28
  • 55