Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question- How to write tests for file upload? #1543

Closed
petermilan opened this issue Mar 29, 2014 · 11 comments
Closed

Question- How to write tests for file upload? #1543

petermilan opened this issue Mar 29, 2014 · 11 comments
Labels
support Questions, discussions, and general support

Comments

@petermilan
Copy link
Contributor

Hi,
I am trying to create a test for API call with mandatory file parameter. When I try to do this using the real API call using following form:

    <h2>Simple upload / POST</h2>
    <form action="http://localhost:8000/api/upload/simple" method="post"
enctype="multipart/form-data">
      Name: <input type="file" name="file" /> <br /> 
      <input type="submit" value="Call" />
    </form>

It is working as expected, but if I try to write test for that, it is not working.

I am using following route in (routes.attachHandlers(server)):

  server.route({
    method: 'POST',
    path: '/api/upload/simple',
    config: {
      validate : {
        payload: {file: Joi.object().required()}
      },
      handler: function(request, reply) {
        // Some upload functionality.
        reply('Done').type('text/plain');
      },
    }
  });

And here is the test I trying to be succesfull:

describe('simple upload', function() {
  var server = Hapi.createServer(0);
  before(function(done) {
    routes.attachHandlers(server);
    server.start(done);
  });

  it('successfull', function(done) {
    var payload = {
      file : fs.readFileSync('./test_file')
    };
    server.inject({url : '/api/upload/simple', method: 'post',  payload : payload}, function(res) {
      console.log(res);
      //expect(res.statusCode).to.equal(200);
      done();
    });
  });
});

In the response is filed following result:

{ statusCode: 400,
     error: 'Bad Request',
     message: 'the value of file must be an object',
     validation: { source: 'payload', keys: [] } } }

It seems to be problem with serve.inject, I am not sure how to pass testing file into the inject method. If I send there buffer, inject method will somehow alter that. What is the proper format of file, which should be passed to inject method?

thanks

@papajuans
Copy link

It's not a problem with server.inject - your route configuration is incorrect. If you're uploading a file, your config needs to include payload configuration that is not under validate.

config: {
    handler: function(request, reply) {
        // do something
      },
      payload: {
        maxBytes: 1048576 * 10, // 10MB
        output: 'stream',
        parse: false
     }
}

As far as testing POSTs with binary data, I don't think server.inject handles that. Try superagent?

@hueniverse
Copy link
Contributor

@pity your inject call is missing the headers needed to know what file is being uploaded and how to process it. Make your request with curl -v and add the missing headers to the test.

@papajuans the payload defaults are fine, no need to add that.

@pon
Copy link

pon commented Apr 11, 2014

@pity - Did you get this working? If you have an example I would love to see it. I'm having some trouble getting the headers correct with Server.inject

@hueniverse
Copy link
Contributor

@PeteOtto look at the tests in tests/payload.js for some examples.

@petermilan
Copy link
Contributor Author

here is example:

  it('upload succeds', function(done) {
    var payload = [
      '------WebKitFormBoundaryS4AeNzAzUP7OArMi',
      'Content-Disposition: form-data; name="filename"',
      '',
      'CIMG3456.JPG',
      '------WebKitFormBoundaryS4AeNzAzUP7OArMi',
      'Content-Disposition: form-data; name="filesize"',
      '',
      '2897308',
      '------WebKitFormBoundaryS4AeNzAzUP7OArMi',
      'Content-Disposition: form-data; name="file"; filename="CIMG3456.JPG"',
      'Content-Type: image/jpeg',
      '',
      '',
      '------WebKitFormBoundaryS4AeNzAzUP7OArMi--'
    ];
    payload = payload.join('\r\n');
    var headers = {
      'content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryS4AeNzAzUP7OArMi'
    };
    server.inject({url : '/upload', method: 'post', payload : payload, headers: headers}, function(res) {
      expect(res.statusCode).to.equal(200);
      expect(res.result.success).to.equal(true);
      done();
    });
  });

My request was more complicated, I simplified during write of the post. I took original string from chrome from network tab (from headers of API request part Request Payload).

But still I am not sure how the real content of file should be sent. Now I got my tests running, the file will be created where it should be. There is just one thing - the content of it is broken. I am not sure how to send it.

@pon
Copy link

pon commented Apr 14, 2014

I've gotten the multi-part form created with the boundaries and I can send a text file across. However, when I create try to send a PDF using the following logic the file that ends up in the handler is corrupted. Am I doing something wrong with how I add the file to the payload?

var file = fs.readFileSync(pathToFile);
var payload = '--AaB03x\r\n' +
      'Content-Disposition: form-data; name="setting_id"\r\n' +
        '\r\n' +
        '201\r\n' +
        '--AaB03x\r\n' +
        'Content-Disposition: form-data; name="double_sided"\r\n' +
        '\r\n' +
        '1\r\n' +
        '--AaB03x\r\n' +
        'Content-Disposition: form-data; name="name"\r\n' +
        '\r\n' +
        'test\r\n' +
        '--AaB03x\r\n' +
        'Content-Disposition: form-data; name="file"; filename="4x6.pdf"\r\n' +
        'Content-Type: application/pdf\r\n' +
        '\r\n' +
        file + '\r\n' +
        '--AaB03x--\r\n';
var headers = {
    'Content-Type': 'multipart/form-data; boundary=AaB03x'
};

@hueniverse
Copy link
Contributor

HTTP is limited character set and all binary content has to be encoded.

@KeKs0r
Copy link

KeKs0r commented Jun 23, 2015

I already played a lot with encodings etc. I know that I sent the data binary from my frontend. But not sure how I can make the tests work:
This is my current approach:

        var filePath = __dirname + '/fixtures/sir.png';
        var file = fs.readFileSync(filePath);
        var payload = [
            '------WebKitFormBoundaryS4AeNzAzUP7OArMi',
            'Content-Disposition: form-data; name="file"; filename="CIMG3456.png"',
            'Content-Type: image/png',
            '',
            file,
            '------WebKitFormBoundaryS4AeNzAzUP7OArMi--'
        ];
        payload = payload.join('\r\n');
        var binaryPayload = new Buffer(payload, 'binary');

I know that my "file" is already a Buffer, but not sure in which encoding. So not sure what happens if I join it with my other strings. I also tried to convert it to utf8 and then back to binary. None of these approaches creates a valid image in my testcase. How do you guys decode your upload content?

@petermilan : you skipped the actual content of the file, how did u encode/decode in your testcase?

@wojtekk
Copy link

wojtekk commented Mar 10, 2016

@dazza5000
Copy link

dazza5000 commented Oct 4, 2018

I got it to work this way:

`it('should return results in bounds', function(done) {
let form = new FormData();
const ne = {'latitude': 30.28051, 'longitude': -97.73246};
const sw = {'latitude': 30.22830, 'longitude': -97.77017};

    let stream = new Stream.Writable();
    stream.data = [];

    stream._write = function(chunk, encoding, callback) {
        this.data.push(chunk);
        callback();
    };

    stream.on('finish', function() {
        let buffer = Buffer.concat(this.data);

        const injectOptions = {
            method: 'POST',
            url: '/search-service/v0/search?ne=30.280514708088386,-97.7324666082859&sw=30.22830287490867,-97.77017880231143&pageSize=20&page=1',
            payload: buffer,
            headers: form.getHeaders()
        };
        return server.inject(injectOptions).then(function(response) {
            expect(response.statusCode).to.equal(200);
            done();
        });
    });

    form.append('bitmap', fs.createReadStream(path.resolve('tests/unit/server/routes/integration/bitmap.png')));
    form.pipe(stream);
});`

@lock
Copy link

lock bot commented Jan 9, 2020

This thread has been automatically locked due to inactivity. Please open a new issue for related bugs or questions following the new issue template instructions.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
support Questions, discussions, and general support
Projects
None yet
Development

No branches or pull requests

7 participants