request

with a callback

should invoke .end()
request
.get(uri + '/login', function(err, res){
  assert(res.status == 200);
  done();
})

.end()

should issue a request
request
.get(uri + '/login')
.end(function(err, res){
  assert(res.status == 200);
  done();
});

res.error

should should be an Error object
request
.get(uri + '/error')
.end(function(err, res){
  if (NODE) {
    res.error.message.should.equal('cannot GET /error (500)');
  }
  else {
    res.error.message.should.equal('cannot GET ' + uri + '/error (500)');
  }
  assert(res.error.status === 500);
  assert(err, 'should have an error for 500');
  assert.equal(err.message, 'Internal Server Error');
  done();
});

res.header

should be an object
request
.get(uri + '/login')
.end(function(err, res){
  assert('Express' == res.header['x-powered-by']);
  done();
});

res.charset

should be set when present
request
.get(uri + '/login')
.end(function(err, res){
  res.charset.should.equal('utf-8');
  done();
});

res.statusType

should provide the first digit
request
.get(uri + '/login')
.end(function(err, res){
  assert(!err, 'should not have an error for success responses');
  assert(200 == res.status);
  assert(2 == res.statusType);
  done();
});

res.type

should provide the mime-type void of params
request
.get(uri + '/login')
.end(function(err, res){
  res.type.should.equal('text/html');
  res.charset.should.equal('utf-8');
  done();
});

req.set(field, val)

should set the header field
request
.post(uri + '/echo')
.set('X-Foo', 'bar')
.set('X-Bar', 'baz')
.end(function(err, res){
  assert('bar' == res.header['x-foo']);
  assert('baz' == res.header['x-bar']);
  done();
})

req.set(obj)

should set the header fields
request
.post(uri + '/echo')
.set({ 'X-Foo': 'bar', 'X-Bar': 'baz' })
.end(function(err, res){
  assert('bar' == res.header['x-foo']);
  assert('baz' == res.header['x-bar']);
  done();
})

req.type(str)

should set the Content-Type
request
.post(uri + '/echo')
.type('text/x-foo')
.end(function(err, res){
  res.header['content-type'].should.equal('text/x-foo');
  done();
});
should map "json"
request
.post(uri + '/echo')
.type('json')
.send('{"a": 1}')
.end(function(err, res){
  res.should.be.json;
  done();
});
should map "html"
request
.post(uri + '/echo')
.type('html')
.end(function(err, res){
  res.header['content-type'].should.equal('text/html');
  done();
});

req.accept(str)

should set Accept
request
.get(uri + '/echo')
.accept('text/x-foo')
.end(function(err, res){
   res.header['accept'].should.equal('text/x-foo');
   done();
});
should map "json"
request
.get(uri + '/echo')
.accept('json')
.end(function(err, res){
  res.header['accept'].should.equal('application/json');
  done();
});
should map "xml"
request
.get(uri + '/echo')
.accept('xml')
.end(function(err, res){
  res.header['accept'].should.equal('application/xml');
  done();
});
should map "html"
request
.get(uri + '/echo')
.accept('html')
.end(function(err, res){
  res.header['accept'].should.equal('text/html');
  done();
});

req.send(str)

should write the string
request
.post(uri + '/echo')
.type('json')
.send('{"name":"tobi"}')
.end(function(err, res){
  res.text.should.equal('{"name":"tobi"}');
  done();
});

req.send(Object)

should default to json
request
.post(uri + '/echo')
.send({ name: 'tobi' })
.end(function(err, res){
  res.should.be.json
  res.text.should.equal('{"name":"tobi"}');
  done();
});

when called several times

should merge the objects
request
.post(uri + '/echo')
.send({ name: 'tobi' })
.send({ age: 1 })
.end(function(err, res){
  res.should.be.json
  if (NODE) {
    res.buffered.should.be.true;
  }
  res.text.should.equal('{"name":"tobi","age":1}');
  done();
});

.end(fn)

should check arity
request
.post(uri + '/echo')
.send({ name: 'tobi' })
.end(function(err, res){
  assert(null == err);
  res.text.should.equal('{"name":"tobi"}');
  done();
});
should emit request
var req = request.post(uri + '/echo');
req.on('request', function(request){
  assert(req == request);
  done();
});
req.end();
should emit response
request
.post(uri + '/echo')
.send({ name: 'tobi' })
.on('response', function(res){
  res.text.should.equal('{"name":"tobi"}');
  done();
})
.end();

.then(fulfill, reject)

should support successful fulfills with .then(fulfill)
request
.post(uri + '/echo')
.send({ name: 'tobi' })
.then(function(res) {
  res.text.should.equal('{"name":"tobi"}');
  done();
})
should reject an error with .then(null, reject)
request
.get(uri + '/error')
.then(null, function(err) {
  assert(err.status == 500);
  assert(err.response.text == 'boom');
  done();
})

.abort()

should abort the request
var req = request
.get(uri + '/delay/3000')
.end(function(err, res){
  assert(false, 'should not complete the request');
});
req.on('abort', done);
setTimeout(function() {
  req.abort();
}, 1000);

request

persistent agent

should gain a session on POST
agent3
  .post('http://localhost:4000/signin')
  .end(function(err, res) {
    should.not.exist(err);
    res.should.have.status(200);
    should.not.exist(res.headers['set-cookie']);
    res.text.should.containEql('dashboard');
    done();
  });
should start with empty session (set cookies)
agent1
  .get('http://localhost:4000/dashboard')
  .end(function(err, res) {
    should.exist(err);
    res.should.have.status(401);
    should.exist(res.headers['set-cookie']);
    done();
  });
should gain a session (cookies already set)
agent1
  .post('http://localhost:4000/signin')
  .end(function(err, res) {
    should.not.exist(err);
    res.should.have.status(200);
    should.not.exist(res.headers['set-cookie']);
    res.text.should.containEql('dashboard');
    done();
  });
should persist cookies across requests
agent1
  .get('http://localhost:4000/dashboard')
  .end(function(err, res) {
    should.not.exist(err);
    res.should.have.status(200);
    done();
  });
should have the cookie set in the end callback
agent4
  .post('http://localhost:4000/setcookie')
  .end(function(err, res) {
    agent4
      .get('http://localhost:4000/getcookie')
      .end(function(err, res) {
        should.not.exist(err);
        res.should.have.status(200);
        assert(res.text === 'jar');
        done();
      });
  });
should not share cookies
agent2
  .get('http://localhost:4000/dashboard')
  .end(function(err, res) {
    should.exist(err);
    res.should.have.status(401);
    done();
  });
should not lose cookies between agents
agent1
  .get('http://localhost:4000/dashboard')
  .end(function(err, res) {
    should.not.exist(err);
    res.should.have.status(200);
    done();
  });
should be able to follow redirects
agent1
  .get('http://localhost:4000/')
  .end(function(err, res) {
    should.not.exist(err);
    res.should.have.status(200);
    res.text.should.containEql('dashboard');
    done();
  });
should be able to post redirects
agent1
  .post('http://localhost:4000/redirect')
  .send({ foo: 'bar', baz: 'blaaah' })
  .end(function(err, res) {
    should.not.exist(err);
    res.should.have.status(200);
    res.text.should.containEql('simple');
    res.redirects.should.eql(['http://localhost:4000/simple']);
    done();
  });
should be able to limit redirects
agent1
  .get('http://localhost:4000/')
  .redirects(0)
  .end(function(err, res) {
    should.exist(err);
    res.should.have.status(302);
    res.redirects.should.eql([]);
    res.header.location.should.equal('/dashboard');
    done();
  });
should be able to create a new session (clear cookie)
agent1
  .post('http://localhost:4000/signout')
  .end(function(err, res) {
    should.not.exist(err);
    res.should.have.status(200);
    should.exist(res.headers['set-cookie']);
    done();
  });
should regenerate with an empty session
agent1
  .get('http://localhost:4000/dashboard')
  .end(function(err, res) {
    should.exist(err);
    res.should.have.status(401);
    should.not.exist(res.headers['set-cookie']);
    done();
  });

Basic auth

when credentials are present in url

should set Authorization
request
.get('http://tobi:learnboost@localhost:3010')
.end(function(err, res){
  res.status.should.equal(200);
  done();
});

req.auth(user, pass)

should set Authorization
request
.get('http://localhost:3010')
.auth('tobi', 'learnboost')
.end(function(err, res){
  res.status.should.equal(200);
  done();
});

req.auth(user + ":" + pass)

should set authorization
request
.get('http://localhost:3010/again')
.auth('tobi')
.end(function(err, res){
  res.status.should.eql(200);
  done();
});

[node] request

res.statusCode

should set statusCode
request
.get('http://localhost:5000/login', function(err, res){
  assert(res.statusCode === 200);
  done();
})

with an object

should format the url
request
.get(url.parse('http://localhost:5000/login'))
.end(function(err, res){
  assert(res.ok);
  done();
})

without a schema

should default to http
request
.get('localhost:5000/login')
.end(function(err, res){
  assert(res.status == 200);
  done();
})

req.toJSON()

should describe the request
request
.post(':5000/echo')
.send({ foo: 'baz' })
.end(function(err, res){
  var obj = res.request.toJSON();
  assert('POST' == obj.method);
  assert(':5000/echo' == obj.url);
  assert('baz' == obj.data.foo);
  done();
});

should allow the send shorthand

with callback in the method call
request
.get('http://localhost:5000/login', function(err, res) {
    assert(res.status == 200);
    done();
});
with data in the method call
request
.post('http://localhost:5000/echo', { foo: 'bar' })
.end(function(err, res) {
  assert('{"foo":"bar"}' == res.text);
  done();
});
with callback and data in the method call
request
.post('http://localhost:5000/echo', { foo: 'bar' }, function(err, res) {
  assert('{"foo":"bar"}' == res.text);
  done();
});

res.toJSON()

should describe the response
request
.post('http://localhost:5000/echo')
.send({ foo: 'baz' })
.end(function(err, res){
  var obj = res.toJSON();
  assert('object' == typeof obj.header);
  assert('object' == typeof obj.req);
  assert(200 == obj.status);
  assert('{"foo":"baz"}' == obj.text);
  done();
});

res.links

should default to an empty object
request
.get('http://localhost:5000/login')
.end(function(err, res){
  res.links.should.eql({});
  done();
})
should parse the Link header field
request
.get('http://localhost:5000/links')
.end(function(err, res){
  res.links.next.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=2');
  done();
})

req.unset(field)

should remove the header field
request
.post('http://localhost:5000/echo')
.unset('User-Agent')
.end(function(err, res){
  assert(void 0 == res.header['user-agent']);
  done();
})

req.write(str)

should write the given data
var req = request.post('http://localhost:5000/echo');
req.set('Content-Type', 'application/json');
req.write('{"name"').should.be.a.boolean;
req.write(':"tobi"}').should.be.a.boolean;
req.end(function(err, res){
  res.text.should.equal('{"name":"tobi"}');
  done();
});

req.pipe(stream)

should pipe the response to the given stream
var stream = new EventEmitter;
stream.buf = '';
stream.writable = true;
stream.write = function(chunk){
  this.buf += chunk;
};
stream.end = function(){
  this.buf.should.equal('{"name":"tobi"}');
  done();
};
request
.post('http://localhost:5000/echo')
.send('{"name":"tobi"}')
.pipe(stream);

.buffer()

should enable buffering
request
.get('http://localhost:5000/custom')
.buffer()
.end(function(err, res){
  assert(null == err);
  assert('custom stuff' == res.text);
  assert(res.buffered);
  done();
});

.buffer(false)

should disable buffering
request
.post('http://localhost:5000/echo')
.type('application/x-dog')
.send('hello this is dog')
.buffer(false)
.end(function(err, res){
  assert(null == err);
  assert(null == res.text);
  res.body.should.eql({});
  var buf = '';
  res.setEncoding('utf8');
  res.on('data', function(chunk){ buf += chunk });
  res.on('end', function(){
    buf.should.equal('hello this is dog');
    done();
  });
});

.agent()

should return the defaut agent
var req = request.post('http://localhost:5000/echo');
req.agent().should.equal(false);
done();

.agent(undefined)

should set an agent to undefined and ensure it is chainable
var req = request.get('http://localhost:5000/echo');
var ret = req.agent(undefined);
ret.should.equal(req);
assert(req.agent() === undefined);
done();

.agent(new http.Agent())

should set passed agent
var http = require('http');
var req = request.get('http://localhost:5000/echo');
var agent = new http.Agent();
var ret = req.agent(agent);
ret.should.equal(req);
req.agent().should.equal(agent)
done();

with a content type other than application/json or text/*

should disable buffering
request
.post('http://localhost:5000/echo')
.type('application/x-dog')
.send('hello this is dog')
.end(function(err, res){
  assert(null == err);
  assert(null == res.text);
  res.body.should.eql({});
  var buf = '';
  res.setEncoding('utf8');
  res.buffered.should.be.false;
  res.on('data', function(chunk){ buf += chunk });
  res.on('end', function(){
    buf.should.equal('hello this is dog');
    done();
  });
});

content-length

should be set to the byte length of a non-buffer object
var decoder = new StringDecoder('utf8');
var img = fs.readFileSync(__dirname + '/fixtures/test.png');
img = decoder.write(img);
request
.post('http://localhost:5000/echo')
.type('application/x-image')
.send(img)
.buffer(false)
.end(function(err, res){
  assert(null == err);
  assert(!res.buffered);
  assert(res.header['content-length'] == Buffer.byteLength(img));
  done();
});
should be set to the length of a buffer object
var img = fs.readFileSync(__dirname + '/fixtures/test.png');
request
.post('http://localhost:5000/echo')
.type('application/x-image')
.send(img)
.buffer(true)
.end(function(err, res){
  assert(null == err);
  assert(res.buffered);
  assert(res.header['content-length'] == img.length);
  done();
});

req.set("Content-Type", contentType)

should work with just the contentType component
request
.post('http://localhost:3005/echo')
.set('Content-Type', 'application/json')
.send({ name: 'tobi' })
.end(function(err, res){
  assert(!err);
  done();
});
should work with the charset component
request
.post('http://localhost:3005/echo')
.set('Content-Type', 'application/json; charset=utf-8')
.send({ name: 'tobi' })
.end(function(err, res){
  assert(!err);
  done();
});

exports

should expose Part
request.Part.should.be.a.function;
should expose .protocols
Object.keys(request.protocols)
  .should.eql(['http:', 'https:']);
should expose .serialize
Object.keys(request.serialize)
  .should.eql(['application/x-www-form-urlencoded', 'application/json']);
should expose .parse
Object.keys(request.parse)
  .should.eql(['application/x-www-form-urlencoded', 'application/json', 'text', 'image']);

flags

with 4xx response

should set res.error and res.clientError
request
.get('http://localhost:3004/notfound')
.end(function(err, res){
  assert(err);
  assert(!res.ok, 'response should not be ok');
  assert(res.error, 'response should be an error');
  assert(res.clientError, 'response should be a client error');
  assert(!res.serverError, 'response should not be a server error');
  done();
});

with 5xx response

should set res.error and res.serverError
request
.get('http://localhost:3004/error')
.end(function(err, res){
  assert(err);
  assert(!res.ok, 'response should not be ok');
  assert(!res.notFound, 'response should not be notFound');
  assert(res.error, 'response should be an error');
  assert(!res.clientError, 'response should not be a client error');
  assert(res.serverError, 'response should be a server error');
  done();
});

with 404 Not Found

should res.notFound
request
.get('http://localhost:3004/notfound')
.end(function(err, res){
  assert(err);
  assert(res.notFound, 'response should be .notFound');
  done();
});

with 400 Bad Request

should set req.badRequest
request
.get('http://localhost:3004/bad-request')
.end(function(err, res){
  assert(err);
  assert(res.badRequest, 'response should be .badRequest');
  done();
});

with 401 Bad Request

should set res.unauthorized
request
.get('http://localhost:3004/unauthorized')
.end(function(err, res){
  assert(err);
  assert(res.unauthorized, 'response should be .unauthorized');
  done();
});

with 406 Not Acceptable

should set res.notAcceptable
request
.get('http://localhost:3004/not-acceptable')
.end(function(err, res){
  assert(err);
  assert(res.notAcceptable, 'response should be .notAcceptable');
  done();
});

with 204 No Content

should set res.noContent
request
.get('http://localhost:3004/no-content')
.end(function(err, res){
  assert(!err);
  assert(res.noContent, 'response should be .noContent');
  done();
});

req.send(Object) as "form"

with req.type() set to form

should send x-www-form-urlencoded data
request
.post('http://localhost:3002/echo')
.type('form')
.send({ name: 'tobi' })
.end(function(err, res){
  res.header['content-type'].should.equal('application/x-www-form-urlencoded');
  res.text.should.equal('name=tobi');
  done();
});

when called several times

should merge the objects
request
.post('http://localhost:3002/echo')
.type('form')
.send({ name: { first: 'tobi', last: 'holowaychuk' } })
.send({ age: '1' })
.end(function(err, res){
  res.header['content-type'].should.equal('application/x-www-form-urlencoded');
  res.text.should.equal('name%5Bfirst%5D=tobi&name%5Blast%5D=holowaychuk&age=1');
  done();
});

req.send(String)

should default to "form"
request
.post('http://localhost:3002/echo')
.send('user[name]=tj')
.send('user[email]=tj@vision-media.ca')
.end(function(err, res){
  res.header['content-type'].should.equal('application/x-www-form-urlencoded');
  res.body.should.eql({ user: { name: 'tj', email: 'tj@vision-media.ca' } });
  done();
})

res.body

application/x-www-form-urlencoded

should parse the body
request
.get('http://localhost:3002/form-data')
.end(function(err, res){
  res.text.should.equal('pet[name]=manny');
  res.body.should.eql({ pet: { name: 'manny' }});
  done();
});

https

request

should give a good response
request
.get('https://localhost:8443/')
.ca(cert)
.end(function(err, res){
  assert(res.ok);
  assert('Safe and secure!' === res.text);
  done();
});

.agent

should be able to make multiple requests without redefining the certificate
var agent = request.agent({ca: cert});
agent
.get('https://localhost:8443/')
.end(function(err, res){
  assert(res.ok);
  assert('Safe and secure!' === res.text);
  agent
  .get(url.parse('https://localhost:8443/'))
  .end(function(err, res){
    assert(res.ok);
    assert('Safe and secure!' === res.text);
    done();
  });
});

res.body

image/png

should parse the body
request
.get('http://localhost:3011/image')
.end(function(err, res){
  (res.body.length - img.length).should.equal(0);
  done();
});

zlib

should deflate the content
request
  .get('http://localhost:3080')
  .end(function(err, res){
    res.should.have.status(200);
    res.text.should.equal(subject);
    res.headers['content-length'].should.be.below(subject.length);
    done();
  });
should handle corrupted responses
request
  .get('http://localhost:3080/corrupt')
  .end(function(err, res){
    assert(err, 'missing error');
    assert(!res, 'response should not be defined');
    done();
  });

without encoding set

should emit buffers
request
  .get('http://localhost:3080/binary')
  .end(function(err, res){
    res.should.have.status(200);
    res.headers['content-length'].should.be.below(subject.length);
    res.on('data', function(chunk){
      chunk.should.have.length(subject.length);
    });
    res.on('end', done);
  });

req.send(Object) as "json"

should default to json
request
.post('http://localhost:3005/echo')
.send({ name: 'tobi' })
.end(function(err, res){
  res.should.be.json
  res.text.should.equal('{"name":"tobi"}');
  done();
});
should work with arrays
request
.post('http://localhost:3005/echo')
.send([1,2,3])
.end(function(err, res){
  res.should.be.json
  res.text.should.equal('[1,2,3]');
  done();
});
should work with value null
request
.post('http://localhost:3005/echo')
.type('json')
.send('null')
.end(function(err, res){
  res.should.be.json
  assert(res.body === null);
  done();
});
should work with value false
request
.post('http://localhost:3005/echo')
.type('json')
.send('false')
.end(function(err, res){
  res.should.be.json
  res.body.should.equal(false);
  done();
});
should work with value 0
request
.post('http://localhost:3005/echo')
.type('json')
.send('0')
.end(function(err, res){
  res.should.be.json
  res.body.should.equal(0);
  done();
});
should work with empty string value
request
.post('http://localhost:3005/echo')
.type('json')
.send('""')
.end(function(err, res){
  res.should.be.json
  res.body.should.equal("");
  done();
});
should work with GET
request
.get('http://localhost:3005/echo')
.send({ tobi: 'ferret' })
.end(function(err, res){
  res.should.be.json
  res.text.should.equal('{"tobi":"ferret"}');
  done();
});
should work with vendor MIME type
request
.post('http://localhost:3005/echo')
.set('Content-Type', 'application/vnd.example+json')
.send({ name: 'vendor' })
.end(function(err, res){
  res.text.should.equal('{"name":"vendor"}');
  done();
});

when called several times

should merge the objects
request
.post('http://localhost:3005/echo')
.send({ name: 'tobi' })
.send({ age: 1 })
.end(function(err, res){
  res.should.be.json
  res.text.should.equal('{"name":"tobi","age":1}');
  done();
});

res.body

application/json

should parse the body
request
.get('http://localhost:3005/json')
.end(function(err, res){
  res.text.should.equal('{"name":"manny"}');
  res.body.should.eql({ name: 'manny' });
  done();
});

HEAD requests

should not throw a parse error
request
.head('http://localhost:3005/json')
.end(function(err, res){
  assert(err === null);
  assert(res.text === undefined)
  assert(Object.keys(res.body).length === 0)
  done();
});

Invalid JSON response

should return the raw response
request
.get('http://localhost:3005/invalid-json')
.end(function(err, res){
  assert.deepEqual(err.rawResponse, ")]}', {'header':{'code':200,'text':'OK','version':'1.0'},'data':'some data'}");
  done();
});

No content

should not throw a parse error
request
.get('http://localhost:3005/no-content')
.end(function(err, res){
  assert(err === null);
  assert(res.text === '');
  assert(Object.keys(res.body).length === 0);
  done();
});

application/json+hal

should parse the body
request
.get('http://localhost:3005/json-hal')
.end(function(err, res){
  if (err) return done(err);
  res.text.should.equal('{"name":"hal 5000"}');
  res.body.should.eql({ name: 'hal 5000' });
  done();
});

vnd.collection+json

should parse the body
request
.get('http://localhost:3005/collection-json')
.end(function(err, res){
  res.text.should.equal('{"name":"chewbacca"}');
  res.body.should.eql({ name: 'chewbacca' });
  done();
});

Request

#attach(name, path, filename)

should use the custom filename
request
.post(':3005/echo')
.attach('document', 'test/node/fixtures/user.html', 'doc.html')
.end(function(err, res){
  if (err) return done(err);
  var html = res.files.document;
  html.name.should.equal('doc.html');
  html.type.should.equal('text/html');
  read(html.path).should.equal('<h1>name</h1>');
  done();
})
should fire progress event
var loaded = 0;
var total = 0;
request
.post(':3005/echo')
.attach('document', 'test/node/fixtures/user.html')
.on('progress', function (event) {
  total = event.total;
  loaded = event.loaded;
})
.end(function(err, res){
  if (err) return done(err);
  var html = res.files.document;
  html.name.should.equal('user.html');
  html.type.should.equal('text/html');
  read(html.path).should.equal('<h1>name</h1>');
  total.should.equal(221);
  loaded.should.equal(221);
  done();
})

with network error

should error
request
.get('http://localhost:' + this.port + '/')
.end(function(err, res){
  assert(err, 'expected an error');
  done();
});

request

not modified

should start with 200
request
.get('http://localhost:3008/')
.end(function(err, res){
  res.should.have.status(200)
  res.text.should.match(/^\d+$/);
  ts = +res.text;
  done();
});
should then be 304
request
.get('http://localhost:3008/')
.set('If-Modified-Since', new Date(ts).toUTCString())
.end(function(err, res){
  res.should.have.status(304)
  // res.text.should.be.empty
  done();
});

req.parse(fn)

should take precedence over default parsers
request
.get('http://localhost:3033/manny')
.parse(request.parse['application/json'])
.end(function(err, res){
  assert(res.ok);
  assert('{"name":"manny"}' == res.text);
  assert('manny' == res.body.name);
  done();
});
should be the only parser
request
.get('http://localhost:3033/image')
.parse(function(res, fn) {
  res.on('data', function() {});
})
.end(function(err, res){
  assert(res.ok);
  assert(res.text === undefined);
  res.body.should.eql({});
  done();
});
should emit error if parser throws
request
.get('http://localhost:3033/manny')
.parse(function() {
  throw new Error('I am broken');
})
.on('error', function(err) {
  err.message.should.equal('I am broken');
  done();
})
.end();
should emit error if parser returns an error
request
.get('http://localhost:3033/manny')
.parse(function(res, fn) {
  fn(new Error('I am broken'));
})
.on('error', function(err) {
  err.message.should.equal('I am broken');
  done();
})
.end()
should not emit error on chunked json
request
.get('http://localhost:3033/chunked-json')
.end(function(err){
  assert(!err);
  done();
});
should not emit error on aborted chunked json
var req = request
.get('http://localhost:3033/chunked-json')
.end(function(err){
  assert(!err);
  done();
});
setTimeout(function(){req.abort()},50);

pipe on redirect

should follow Location
var stream = fs.createWriteStream('test/node/fixtures/pipe.txt');
var redirects = [];
var req = request
  .get('http://localhost:3012/')
  .on('redirect', function (res) {
    redirects.push(res.headers.location);
  })
  .on('end', function () {
    var arr = [];
    arr.push('/movies');
    arr.push('/movies/all');
    arr.push('/movies/all/0');
    redirects.should.eql(arr);
    fs.readFileSync('test/node/fixtures/pipe.txt', 'utf8').should.eql('first movie page');
    done();
  });
  req.pipe(stream);

request pipe

should act as a writable stream
var req = request.post('http://localhost:3020');
var stream = fs.createReadStream('test/node/fixtures/user.json');
req.type('json');
req.on('response', function(res){
  res.body.should.eql({ name: 'tobi' });
  done();
});
stream.pipe(req);
should act as a readable stream
var stream = fs.createWriteStream('test/node/fixtures/tmp.json');
var req = request.get('http://localhost:3025');
req.type('json');
req.on('end', function(){
  JSON.parse(fs.readFileSync('test/node/fixtures/tmp.json', 'utf8')).should.eql({ name: 'tobi' });
  done();
});
req.pipe(stream);

req.query(String)

should supply uri malformed error to the callback
request
.get('http://localhost:3006')
.query('name=toby')
.query('a=\uD800')
.query({ b: '\uD800' })
.end(function(err, res){
  assert(err instanceof Error);
  assert('URIError' == err.name);
  done();
});
should support passing in a string
request
.del('http://localhost:3006')
.query('name=t%F6bi')
.end(function(err, res){
  res.body.should.eql({ name: 't%F6bi' });
  done();
});
should work with url query-string and string for query
request
.del('http://localhost:3006/?name=tobi')
.query('age=2%20')
.end(function(err, res){
  res.body.should.eql({ name: 'tobi', age: '2 ' });
  done();
});
should support compound elements in a string
request
  .del('http://localhost:3006/')
  .query('name=t%F6bi&age=2')
  .end(function(err, res){
    res.body.should.eql({ name: 't%F6bi', age: '2' });
    done();
  });
should work when called multiple times with a string
request
.del('http://localhost:3006/')
.query('name=t%F6bi')
.query('age=2%F6')
.end(function(err, res){
  res.body.should.eql({ name: 't%F6bi', age: '2%F6' });
  done();
});
should work with normal `query` object and query string
request
.del('http://localhost:3006/')
.query('name=t%F6bi')
.query({ age: '2' })
.end(function(err, res){
  res.body.should.eql({ name: 't%F6bi', age: '2' });
  done();
});

req.query(Object)

should construct the query-string
request
.del('http://localhost:3006/')
.query({ name: 'tobi' })
.query({ order: 'asc' })
.query({ limit: ['1', '2'] })
.end(function(err, res){
  res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
  done();
});
should not error on dates
var date = new Date(0);
request
.del('http://localhost:3006/')
.query({ at: date })
.end(function(err, res){
  assert(date.toISOString() == res.body.at);
  done();
});
should work after setting header fields
request
.del('http://localhost:3006/')
.set('Foo', 'bar')
.set('Bar', 'baz')
.query({ name: 'tobi' })
.query({ order: 'asc' })
.query({ limit: ['1', '2'] })
.end(function(err, res){
  res.body.should.eql({ name: 'tobi', order: 'asc', limit: ['1', '2'] });
  done();
});
should append to the original query-string
request
.del('http://localhost:3006/?name=tobi')
.query({ order: 'asc' })
.end(function(err, res) {
  res.body.should.eql({ name: 'tobi', order: 'asc' });
  done();
});
should retain the original query-string
request
.del('http://localhost:3006/?name=tobi')
.end(function(err, res) {
  res.body.should.eql({ name: 'tobi' });
  done();
});

request.get

on 301 redirect

should follow Location with a GET request
var req = request
  .get('http://localhost:3210/test-301')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

on 302 redirect

should follow Location with a GET request
var req = request
  .get('http://localhost:3210/test-302')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

on 303 redirect

should follow Location with a GET request
var req = request
  .get('http://localhost:3210/test-303')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

on 307 redirect

should follow Location with a GET request
var req = request
  .get('http://localhost:3210/test-307')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

on 308 redirect

should follow Location with a GET request
var req = request
  .get('http://localhost:3210/test-308')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

request.post

on 301 redirect

should follow Location with a GET request
var req = request
  .post('http://localhost:3210/test-301')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

on 302 redirect

should follow Location with a GET request
var req = request
  .post('http://localhost:3210/test-302')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

on 303 redirect

should follow Location with a GET request
var req = request
  .post('http://localhost:3210/test-303')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('GET');
    done();
  });

on 307 redirect

should follow Location with a POST request
var req = request
  .post('http://localhost:3210/test-307')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('POST');
    done();
  });

on 308 redirect

should follow Location with a POST request
var req = request
  .post('http://localhost:3210/test-308')
  .redirects(1)
  .end(function(err, res){
    req.req._headers.host.should.eql('localhost:3211');
    res.status.should.eql(200);
    res.text.should.eql('POST');
    done();
  });

request

on redirect

should follow Location
var redirects = [];
request
.get('http://localhost:3003/')
.on('redirect', function(res){
  redirects.push(res.headers.location);
})
.end(function(err, res){
  var arr = [];
  arr.push('/movies');
  arr.push('/movies/all');
  arr.push('/movies/all/0');
  redirects.should.eql(arr);
  res.text.should.equal('first movie page');
  done();
});
should retain header fields
request
.get('http://localhost:3003/header')
.set('X-Foo', 'bar')
.end(function(err, res){
  res.body.should.have.property('x-foo', 'bar');
  done();
});
should remove Content-* fields
request
.post('http://localhost:3003/header')
.type('txt')
.set('X-Foo', 'bar')
.set('X-Bar', 'baz')
.send('hey')
.end(function(err, res){
  res.body.should.have.property('x-foo', 'bar');
  res.body.should.have.property('x-bar', 'baz');
  res.body.should.not.have.property('content-type');
  res.body.should.not.have.property('content-length');
  res.body.should.not.have.property('transfer-encoding');
  done();
});
should retain cookies
request
.get('http://localhost:3003/header')
.set('Cookie', 'foo=bar;')
.end(function(err, res){
  res.body.should.have.property('cookie', 'foo=bar;');
  done();
});
should preserve timeout across redirects
request
.get('http://localhost:3003/movies/random')
.timeout(250)
.end(function(err, res){
  assert(err instanceof Error, 'expected an error');
  err.should.have.property('timeout', 250);
  done();
});
should not resend query parameters
var redirects = [];
var query = [];
request
.get('http://localhost:3003/?foo=bar')
.on('redirect', function(res){
  query.push(res.headers.query);
  redirects.push(res.headers.location);
})
.end(function(err, res){
  var arr = [];
  arr.push('/movies');
  arr.push('/movies/all');
  arr.push('/movies/all/0');
  redirects.should.eql(arr);
  res.text.should.equal('first movie page');
  query.should.eql(['{"foo":"bar"}', '{}', '{}']);
  res.headers.query.should.eql('{}');
  done();
});
should handle no location header
request
.get('http://localhost:3003/bad-redirect')
.end(function(err, res){
  err.message.should.equal('No location header for redirect');
  done();
});

when relative

should redirect to a sibling path
var redirects = [];
request
.get('http://localhost:3003/relative')
.on('redirect', function(res){
  redirects.push(res.headers.location);
})
.end(function(err, res){
  var arr = [];
  redirects.should.eql(['tobi']);
  res.text.should.equal('tobi');
  done();
});
should redirect to a parent path
var redirects = [];
request
.get('http://localhost:3003/relative/sub')
.on('redirect', function(res){
  redirects.push(res.headers.location);
})
.end(function(err, res){
  var arr = [];
  redirects.should.eql(['../tobi']);
  res.text.should.equal('tobi');
  done();
});

req.redirects(n)

should alter the default number of redirects to follow
var redirects = [];
request
.get('http://localhost:3003/')
.redirects(2)
.on('redirect', function(res){
  redirects.push(res.headers.location);
})
.end(function(err, res){
  var arr = [];
  assert(res.redirect, 'res.redirect');
  arr.push('/movies');
  arr.push('/movies/all');
  redirects.should.eql(arr);
  res.text.should.match(/Moved Temporarily|Found/);
  done();
});

on POST

should redirect as GET
var redirects = [];
request
.post('http://localhost:3003/movie')
.send({ name: 'Tobi' })
.redirects(2)
.on('redirect', function(res){
  redirects.push(res.headers.location);
})
.end(function(err, res){
  var arr = [];
  arr.push('/movies/all/0');
  redirects.should.eql(arr);
  res.text.should.equal('first movie page');
  done();
});

on 303

should redirect with same method
request
.put('http://localhost:3003/redirect-303')
.send({msg: "hello"})
.redirects(1)
.on('redirect', function(res) {
  res.headers.location.should.equal('/reply-method')
})
.end(function(err, res){
  res.text.should.equal('method=get');
  done();
})

on 307

should redirect with same method
request
.put('http://localhost:3003/redirect-307')
.send({msg: "hello"})
.redirects(1)
.on('redirect', function(res) {
  res.headers.location.should.equal('/reply-method')
})
.end(function(err, res){
  res.text.should.equal('method=put');
  done();
})

on 308

should redirect with same method
request
.put('http://localhost:3003/redirect-308')
.send({msg: "hello"})
.redirects(1)
.on('redirect', function(res) {
  res.headers.location.should.equal('/reply-method')
})
.end(function(err, res){
  res.text.should.equal('method=put');
  done();
})

response

should act as a readable stream
var req = request
  .get('http://localhost:3025')
  .buffer(false);
req.end(function(err,res){
  if (err) return done(err);
  var trackEndEvent = 0;
  var trackCloseEvent = 0;
  res.on('end',function(){
    trackEndEvent++;
    trackEndEvent.should.equal(1);
    trackCloseEvent.should.equal(0);  // close should not have been called
    done();
  });
  res.on('close',function(){
    trackCloseEvent++;
  });

  (function(){ res.pause() }).should.not.throw();
  (function(){ res.resume() }).should.not.throw();
  (function(){ res.destroy() }).should.not.throw();
});

.timeout(ms)

when timeout is exceeded

should error
request
.get('http://localhost:3009/500')
.timeout(150)
.end(function(err, res){
  assert(err, 'expected an error');
  assert('number' == typeof err.timeout, 'expected an error with .timeout');
  assert('ECONNABORTED' == err.code, 'expected abort error code')
  done();
});

res.toError()

should return an Error
request
.get('http://localhost:' + server.address().port)
.end(function(err, res){
  var err = res.toError();
  assert(err.status == 400);
  assert(err.method == 'GET');
  assert(err.path == '/');
  assert(err.message == 'cannot GET / (400)');
  assert(err.text == 'invalid json');
  done();
});

req.get()

should set a default user-agent
request
.get('http://localhost:3345/ua')
.end(function(err, res){
  assert(res.headers);
  assert(res.headers['user-agent']);
  assert(/^node-superagent\/\d+\.\d+\.\d+$/.test(res.headers['user-agent']));
  done();
});
should be able to override user-agent
request
.get('http://localhost:3345/ua')
.set('User-Agent', 'foo/bar')
.end(function(err, res){
  assert(res.headers);
  assert(res.headers['user-agent'] == 'foo/bar');
  done();
});
should be able to wipe user-agent
request
.get('http://localhost:3345/ua')
.unset('User-Agent')
.end(function(err, res){
  assert(res.headers);
  assert(res.headers['user-agent'] == void 0);
  done();
});

utils.type(str)

should return the mime type
utils.type('application/json; charset=utf-8')
  .should.equal('application/json');
utils.type('application/json')
  .should.equal('application/json');

utils.params(str)

should return the field parameters
var str = 'application/json; charset=utf-8; foo  = bar';
var obj = utils.params(str);
obj.charset.should.equal('utf-8');
obj.foo.should.equal('bar');
var str = 'application/json';
utils.params(str).should.eql({});

utils.parseLinks(str)

should parse links
var str = '<https://api.github.com/repos/visionmedia/mocha/issues?page=2>; rel="next", <https://api.github.com/repos/visionmedia/mocha/issues?page=5>; rel="last"';
var ret = utils.parseLinks(str);
ret.next.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=2');
ret.last.should.equal('https://api.github.com/repos/visionmedia/mocha/issues?page=5');
Fork me on GitHub