const http = require('http')
const request = require('request')
const FormData = require('form-data')
const concat = require('concat-stream')
const Promise = require('bluebird')
const fs = require('fs')
const db = require('../app/lib/db')
const path = require('path')

if (!/test$/.exec(process.env.NODE_ENV)) {
  console.error('Must be in test environment: expected, NODE_ENV=test, got NODE_ENV='+process.env.NODE_ENV)
  process.exit(1)
}

function loadDatabase(callback) {
  const lines = Buffer.concat([
    fs.readFileSync(path.join(__dirname, '..', 'schema.sql')),
    fs.readFileSync(path.join(__dirname, 'test-data.sql')),
  ]).toString('utf8')
    .trim()
    .split(';')
    .map(function (s) {return s.trim()})
    .filter(Boolean)

  ;(function next(i) {
    const sql = lines[i]
    if (!sql) return callback()

    db.query(sql, function (err) {
      if (err) return callback(err)
      return next(++i)
    })

  })(0)
}

module.exports = function spawner(app, callback) {
  function makeRequestFn(port) {
    const baseUrl = 'http://127.0.0.1:'+port
    function requester(method, url, form) {
      const deferred = Promise.defer()

      function resolve(status, body, headers) {
        return deferred.resolve({statusCode: status, body: body, headers: headers})
      }

      if (form) {
        const formData = new FormData()

        for (var field in form) {
          formData.append(field, form[field])
        }

        const options = {
          url: baseUrl + url,
          method: method.toUpperCase(),
          headers: formData.getHeaders(),
        }

        const req = request(options)
        formData.pipe(req)
        req.pipe(concat(function (body) {
          resolve(req.response.statusCode, JSON.parse(body), req.response.headers)
        }))

      } else {
        request[method.toLowerCase()](baseUrl + url, function (err, res, body) {
          if (err) throw err
          resolve(res.statusCode, JSON.parse(body), res.headers)
        })
      }
      return deferred.promise
    }
    return {
      makeUrl: function (url) {return baseUrl + url},
      get: requester.bind(null, 'get'),
      post: requester.bind(null, 'post'),
      put: requester.bind(null, 'put'),
      del: requester.bind(null, 'del'),
      fail: function fail(t) {
        return function catcher(err) {
          t.fail('Error: ' + err.message)
          console.log(err.stack)
          t.end()
        }
      },
      done: function done() {
        db.close()
      }
    }
  }

  const deferred = Promise.defer()

  loadDatabase(function (err, result) {
    if (err) throw err

    const server = app.listen(0, function () {
      deferred.resolve(makeRequestFn(this.address().port))
    })

    server.unref()
  })

  return deferred.promise
}

@@ -1,12 +1,45 @@
+const Stream = require('stream')%0A
 const http =
@@ -963,16 +963,213 @@
 )(0)%0A%7D%0A%0A
+function hasBufferOrStream(obj) %7B%0A  for (var field in obj) %7B%0A    if (Buffer.isBuffer(obj%5Bfield%5D))%0A      return true;%0A    if (obj%5Bfield%5D instanceof Stream)%0A      return true;%0A  %7D%0A  return false;%0A%7D%0A%0A
 module.e
@@ -1540,21 +1540,189 @@
         
-const
+var req, formData;%0A        const options = %7B%0A          url: baseUrl + url,%0A          method: method.toUpperCase(),%0A        %7D%0A        if (hasBufferOrStream(form)) %7B%0A         
  formDat
@@ -1737,24 +1737,26 @@
 FormData()%0A%0A
+  
         for 
@@ -1779,32 +1779,34 @@
 rm) %7B%0A          
+  
 formData.append(
@@ -1825,16 +1825,18 @@
 field%5D)%0A
+  
         
@@ -1842,37 +1842,33 @@
  %7D%0A%0A        
-const
+ 
  options
  = %7B%0A       
@@ -1859,125 +1859,290 @@
 ions
- = %7B%0A          url: baseUrl + url,%0A          method: method.toUpperCase(),%0A          headers: formData.getHeaders(),%0A
+.headers = formData.getHeaders()%0A%0A          req = request(options)%0A          formData.pipe(req)%0A        %7D else %7B%0A          formData = JSON.stringify(form)%0A          options.headers = %7B%0A            'Content-Type': 'application/json',%0A            'Content-Length': formData.length,%0A  
     
@@ -2147,25 +2147,24 @@
       %7D%0A
-%0A
         
 const re
@@ -2155,21 +2155,17 @@
         
-const
+ 
  req = r
@@ -2188,34 +2188,67 @@
         
-formData.pipe(req)
+  req.write(formData)%0A          req.end()%0A        %7D
 %0A       
@@ -2377,17 +2377,16 @@
     %7D))%0A
-%0A
       %7D 

Test harness: Use JSON unless multipart is necessary