From 3c2ddc3619ce8d8c592d62aa9d31206267cded31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A0=9A=E7=84=B6?= <yanran.wwj@alipay.com>
Date: Sat, 5 May 2018 15:43:11 +0800
Subject: [PATCH] add test cases against the cli command

---
 test/jasmine.json                     |   3 +-
 test/spec_cli/anyproxy-ca_cli_spec.js |  27 ++
 test/spec_cli/anyproxy_cli_spec.js    |  27 ++
 test/spec_rule/no_rule_spec.js        | 439 ++------------------------
 test/spec_rule/no_rule_spec_exec.js   | 430 +++++++++++++++++++++++++
 5 files changed, 504 insertions(+), 422 deletions(-)
 create mode 100644 test/spec_cli/anyproxy-ca_cli_spec.js
 create mode 100644 test/spec_cli/anyproxy_cli_spec.js
 create mode 100644 test/spec_rule/no_rule_spec_exec.js

diff --git a/test/jasmine.json b/test/jasmine.json
index 057167a..2345a41 100644
--- a/test/jasmine.json
+++ b/test/jasmine.json
@@ -2,7 +2,8 @@
   "spec_dir": "test",
   "spec_files": [
     "spec_lib/*.js",
-    "spec_rule/*.js"
+    "spec_rule/*.js",
+    "spec_cli/*.js"
   ],
   "helpers": [
      "../node_modules/babel-register/lib/node.js",
diff --git a/test/spec_cli/anyproxy-ca_cli_spec.js b/test/spec_cli/anyproxy-ca_cli_spec.js
new file mode 100644
index 0000000..5ebc6a0
--- /dev/null
+++ b/test/spec_cli/anyproxy-ca_cli_spec.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const path = require('path');
+const childProcess = require('child_process');
+
+const pkg = require('../../package');
+
+const binFile = path.resolve(pkg.bin['anyproxy-ca']);
+
+// TODO: more cases are wanted
+describe('anyproxy line tool test', () => {
+  beforeAll(() => {
+    jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
+  });
+  it('should check the ca status', done => {
+    childProcess.execFile(binFile, [''], (error, stdout, stderr) => {
+      if (error) {
+        console.error(error);
+        done.fail('anyproxy-ca failed');
+      }
+      // If the cert is already generated, the cli will print the info,
+      // If the cert is not generated, the cli will prompt to install, also contains the `AnyProxy CA/
+      expect(stdout).toMatch(/AnyProxy CA/);
+      done();
+    })
+  });
+});
diff --git a/test/spec_cli/anyproxy_cli_spec.js b/test/spec_cli/anyproxy_cli_spec.js
new file mode 100644
index 0000000..95eec7d
--- /dev/null
+++ b/test/spec_cli/anyproxy_cli_spec.js
@@ -0,0 +1,27 @@
+'use strict';
+
+const path = require('path');
+const spawn = require('child_process').spawn;
+const NoRuleSpecExec = require('../spec_rule/no_rule_spec_exec');
+const { printLog } = require('../util/CommonUtil.js');
+const pkg = require('../../package');
+
+const binFile = path.resolve(pkg.bin.anyproxy);
+
+const startProxyFunc = function () {
+  printLog('Start AnyProxy by cli');
+  const childProcessInstance = spawn(binFile, ['-i'], {});
+  printLog('Start AnyProxy by cli successfully');
+  return childProcessInstance;
+}
+
+const closeProxyFunc = function (instance) {
+  printLog('Close AnyProxy in cli');
+  instance.kill('SIGINT');
+}
+
+function testRequestByCli() {
+  NoRuleSpecExec(startProxyFunc, closeProxyFunc);
+}
+
+testRequestByCli();
diff --git a/test/spec_rule/no_rule_spec.js b/test/spec_rule/no_rule_spec.js
index 32373ef..c7da345 100644
--- a/test/spec_rule/no_rule_spec.js
+++ b/test/spec_rule/no_rule_spec.js
@@ -1,427 +1,24 @@
 
 process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
-const path = require('path');
-const fs = require('fs');
-const Server = require('../server/server.js');
-const
-  {
-    proxyGet,
-    proxyPost,
-    directGet,
-    directPost,
-    directUpload,
-    proxyUpload,
-    generateUrl,
-    proxyPut,
-    directPut,
-    proxyDelete,
-    directDelete,
-    directHead,
-    proxyHead,
-    directOptions,
-    proxyOptions,
-    proxyPutUpload,
-    directPutUpload
-  } = require('../util/HttpUtil.js');
-const { CommonRequestHeader } = require('../data/headers.js');
-const { isCommonResHeaderEqual, isCommonReqEqual, printLog } = require('../util/CommonUtil.js');
-const streamEqual = require('stream-equal');
-
+const NoRuleSpecExec = require('./no_rule_spec_exec');
 const ProxyServerUtil = require('../util/ProxyServerUtil.js');
+const { printLog } = require('../util/CommonUtil.js');
 
-testRequest('http');
-testRequest('https');
-testRequest('http', false);
-testRequest('https', false);
-
-// Test suites for http and https request
-function testRequest(protocol = 'http', needWeb = true) {
-  function constructUrl(urlPath) {
-    return generateUrl(protocol, urlPath);
+const getStartProxyFunc = function (needWeb) {
+  return function () {
+    printLog('Start AnyProxy by node module');
+    return ProxyServerUtil.defaultProxyServer(needWeb);
   }
-
-  describe('Test request without proxy rules in protocol ' + protocol, () => {
-    let proxyServer;
-    let serverInstance;
-
-    beforeAll((done) => {
-      jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000;
-      printLog('Start server for no_rule_spec');
-
-      serverInstance = new Server();
-      proxyServer = ProxyServerUtil.defaultProxyServer(needWeb);
-      setTimeout(() => {
-        done();
-      }, 2000);
-    });
-
-    afterAll(() => {
-      serverInstance && serverInstance.close();
-      proxyServer && proxyServer.close();
-      printLog('Closed server for no_rule_spec');
-    });
-
-
-    it('Get should work as direct without proxy rules', (done) => {
-      const url = constructUrl('/test');
-      const getParam = {
-        param: 'nothing'
-      };
-
-      proxyGet(url, getParam, CommonRequestHeader).then((proxyRes) => {
-        directGet(url, getParam, CommonRequestHeader).then(directRes => {
-          expect(proxyRes.statusCode).toEqual(200);
-          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-          expect(proxyRes.statusCode).toEqual(directRes.statusCode);
-          expect(directRes.body).toEqual(proxyRes.body);
-
-          done();
-        }, error => {
-          console.error('error happend in direct get:', error);
-          done.fail('error happend in direct get');
-        });
-      }, error => {
-        console.log('error happened in proxy get:', error);
-        done.fail('error happend in proxy get');
-      });
-    });
-
-    it('Post should work as direct without proxy rules', (done) => {
-      const url = constructUrl('/test/getuser');
-      const param = {
-        param: 'postnothing'
-      };
-
-      proxyPost(url, param, CommonRequestHeader).then(proxyRes => {
-        directPost(url, param, CommonRequestHeader).then(directRes => {
-          expect(proxyRes.statusCode).toEqual(200);
-
-          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-          expect(proxyRes.statusCode).toEqual(directRes.statusCode);
-          expect(directRes.body).toEqual(proxyRes.body);
-
-          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-
-          done();
-        }, error => {
-          console.error('error in direct post:', error);
-          done.fail('error happend in direct post');
-        });
-      }, error => {
-        console.log('error happened in proxy post,', error);
-        done.fail('error happend in proxy post');
-      });
-    });
-
-    it('PUT should work as direct without proxy rules', done => {
-      const url = constructUrl('/test/put');
-      const param = {
-        param: 'putsomething'
-      };
-      proxyPut(url, param, CommonRequestHeader).then(proxyRes => {
-        directPut(url, param, CommonRequestHeader).then(directRes => {
-          expect(directRes.statusCode).toEqual(200);
-
-          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-          expect(directRes.statusCode).toEqual(proxyRes.statusCode);
-          expect(directRes.body).toEqual(proxyRes.body);
-          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-
-          done();
-        }, error => {
-          console.error('error happened in direct put', error);
-          done.fail('error happened in direct put');
-        });
-      }, error => {
-        console.error('error happened in proxy put', error);
-        done.fail('error happened in proxy put');
-      });
-    });
-
-    it('DELETE rquest should work as direct without proxy rules', (done) => {
-      const url = constructUrl('/test/delete/123456');
-
-      proxyDelete(url, {}, CommonRequestHeader).then(proxyRes => {
-        directDelete(url, {}, CommonRequestHeader).then(directRes => {
-          expect(directRes.statusCode).toEqual(200);
-
-          expect(directRes.statusCode).toEqual(proxyRes.statusCode);
-          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-          expect(directRes.body).toEqual(proxyRes.body);
-          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-
-          done();
-        }, error => {
-          console.error('error happened in direct delete :', error);
-          done.fail('error happened in direct delete');
-        });
-      }, error => {
-        console.error('error happened in proxy delete :', error);
-        done.fail('error happened in proxy delete');
-      });
-    });
-
-    it('HEAD request should work as direct without proxy rules', (done) => {
-      const url = constructUrl('/test/head');
-
-      proxyHead(url, CommonRequestHeader)
-        .then(proxyRes => {
-          directHead(url, CommonRequestHeader)
-            .then(directRes => {
-              expect(directRes.statusCode).toEqual(200);
-              expect(directRes.body).toEqual('');
-
-              expect(directRes.statusCode).toEqual(proxyRes.statusCode);
-              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-              expect(directRes.body).toEqual(proxyRes.body);
-              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-
-              done();
-            }, error => {
-              console.error('error happened in direct head request:', error);
-              done.fail('error happened in direct head request');
-            });
-        }, error => {
-          console.error('error happened in proxy head request:', error);
-          done.fail('error happened in proxy head request');
-        });
-    });
-
-    it('OPTIONS request should work as direct without proxy rules', (done) => {
-      const url = constructUrl('/test/options');
-
-      proxyOptions(url, CommonRequestHeader)
-        .then(proxyRes => {
-          directOptions(url, CommonRequestHeader)
-            .then(directRes => {
-              expect(directRes.statusCode).toEqual(200);
-              expect(directRes.body).toEqual('could_be_empty');
-
-              expect(directRes.statusCode).toEqual(proxyRes.statusCode);
-              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-              expect(directRes.body).toEqual(proxyRes.body);
-              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-
-              done();
-            }, error => {
-              console.error('error happened in direct options request:', error);
-              done.fail('error happened in direct options request');
-            });
-        }, error => {
-          console.error('error happened in proxy options request:', error);
-          done.fail('error happened in proxy options request');
-        });
-    });
-
-    it('304 should work as direct without proxy rules', (done) => {
-      const url = constructUrl('/test/response/304');
-
-      proxyGet(url, CommonRequestHeader)
-        .then(proxyRes => {
-          directGet(url, CommonRequestHeader)
-            .then(directRes => {
-              expect(directRes.statusCode).toEqual(304);
-              expect(directRes.body).toEqual('');
-
-              expect(directRes.statusCode).toEqual(proxyRes.statusCode);
-              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-              expect(directRes.body).toEqual(proxyRes.body);
-              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-              done();
-            }, error => {
-              console.error('error happened in direct 304 request:', error);
-              done.fail('error happened in direct 304 request');
-            });
-        }, error => {
-          console.error('error happened in proxy 304 request:', error);
-          done.fail('error happened in proxy 304 request');
-        });
-    })
-
-    describe('Response code should be honored as direct without proxy rules', () => {
-      [301, 302, 303].forEach(code => {
-        testRedirect(code);
-      });
-
-      function testRedirect(redirectCode) {
-        it(`${redirectCode} response should work as direct without proxy rules`, (done) => {
-          const url = constructUrl(`/test/response/${redirectCode}`);
-
-          proxyGet(url)
-            .then(proxyRes => {
-              directGet(url)
-                .then(directRes => {
-                  expect(directRes.statusCode).toEqual(redirectCode);
-                  expect(directRes.headers.location).toEqual(proxyRes.headers.location);
-                  expect(directRes.statusCode).toEqual(proxyRes.statusCode);
-                  expect(directRes.headers.location).toEqual('/test');
-                  done();
-                }).catch(error => {
-                  console.log(`error happened in direct ${redirectCode}:`, error);
-                  done.fail(`error happened in direct ${redirectCode}`);
-                });
-            }).catch(error => {
-              console.log(`error happened in proxy ${redirectCode}:`, error);
-              done.fail(`error happened in proxy ${redirectCode}`);
-            });
-        });
-      }
-    });
-
-    describe('Test file download ', () => {
-      const testArray = [
-        {
-          url: constructUrl('/test/download/png'),
-          type: 'png',
-          contentType: 'image/png'
-        },
-        {
-          url: constructUrl('/test/download/webp'),
-          type: 'WEBP',
-          contentType: 'image/webp'
-        },
-        {
-          url: constructUrl('/test/download/json'),
-          type: 'JSON',
-          contentType: 'application/json; charset=utf-8'
-        },
-        {
-          url: constructUrl('/test/download/css'),
-          type: 'CSS',
-          contentType: 'text/css; charset=utf-8'
-        },
-        {
-          url: constructUrl('/test/download/ttf'),
-          type: 'TTF',
-          contentType: 'application/x-font-ttf'
-        },
-        {
-          url: constructUrl('/test/download/eot'),
-          type: 'EOT',
-          contentType: 'application/vnd.ms-fontobject'
-        },
-        {
-          url: constructUrl('/test/download/svg'),
-          type: 'SVG',
-          contentType: 'image/svg+xml'
-        },
-        {
-          url: constructUrl('/test/download/woff'),
-          type: 'WOFF',
-          contentType: 'application/font-woff'
-        },
-        {
-          url: constructUrl('/test/download/woff2'),
-          type: 'WOFF2',
-          contentType: 'application/font-woff2'
-        }
-      ];
-
-      testArray.forEach(item => {
-        testFileDownload(item.url, item.type, item.contentType);
-      });
-
-      // 封装测试文件下载的测试工具类
-      function testFileDownload(url, filetype, contentType) {
-        const describe = `${filetype} file download without rules should be work as direct download`;
-        const param = {};
-
-        it(describe, (done) => {
-          proxyGet(url, param).then(proxyRes => {
-            directGet(url, param).then(directRes => {
-              expect(proxyRes.statusCode).toEqual(200);
-
-              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-              expect(proxyRes.statusCode).toEqual(directRes.statusCode);
-              expect(proxyRes.body).toEqual(directRes.body);
-              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-              done();
-            }, error => {
-              console.error('error in direct get :', filetype, error);
-              done.fail(`error happend in direct get ${filetype}`);
-            });
-          }, error => {
-            console.error('error in proxy get :', filetype, error);
-            done.fail(`error happend in proxy get ${filetype}`);
-          });
-        });
-      }
-    });
-
-    describe('Test file upload', () => {
-      const formParams = {
-        param1: 'param_1',
-        param2: 'param2'
-      };
-      it('POST upload should be working', (done) => {
-        const url = constructUrl('/test/upload/png');
-        const filePath = path.resolve(__dirname, '../data/test.png');
-
-        proxyUpload(url, filePath, formParams)
-          .then(proxyRes => {
-            directUpload(url, filePath, formParams)
-              .then((directRes) => {
-                expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-                expect(isCommonReqEqual(url, serverInstance)).toBe(true);
-                assertReponse(proxyRes, directRes, filePath, done);
-              }, error => {
-                console.error('error in direct upload:', error);
-                done.fail('error in direct upload');
-              });
-          }, error => {
-            console.error('error in proxy upload:', error);
-            done.fail('error in proxy upload:');
-          });
-      });
-
-      it('PUT upload should be working', (done) => {
-        const url = constructUrl('/test/upload/putpng');
-        const filePath = path.resolve(__dirname, '../data/test.png');
-        proxyPutUpload(url, filePath, formParams)
-          .then(proxyRes => {
-            directPutUpload(url, filePath, formParams)
-              .then((directRes) => {
-                expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
-
-                assertReponse(proxyRes, directRes, filePath, done);
-              }, error => {
-                console.error('error in direct upload:', error);
-                done.fail('error in direct upload');
-              });
-          }, error => {
-            console.error('error in proxy upload:', error);
-            done.fail('error in proxy upload:');
-          });
-      });
-
-      function assertReponse(proxyRes, directRes, originFilePath, done) {
-        expect(proxyRes.statusCode).toEqual(200);
-
-        expect(proxyRes.statusCode).toEqual(directRes.statusCode);
-        // expect(proxyRes.headers.reqbody).toEqual(directRes.headers.reqbody);
-
-        // the body will be the file path
-        const directUploadedStream = fs.createReadStream(directRes.body);
-        const proxyUploadedStream = fs.createReadStream(proxyRes.body);
-        const localFileStream = fs.createReadStream(originFilePath);
-        streamEqual(directUploadedStream, localFileStream)
-          .then(isLocalEqual => {
-            expect(isLocalEqual).toBe(true);
-            streamEqual(directUploadedStream, proxyUploadedStream)
-              .then(isUploadedEqual => {
-                expect(isUploadedEqual).toBe(true);
-                done();
-              }, error => {
-                console.error('error in comparing directUpload with proxy:\n', error);
-                done.fail('error in comparing directUpload with proxy');
-              });
-            done();
-          }, error => {
-            console.error('error in comparing directUpload with local:\n', error);
-            done.fail('error in comparing directUpload with local');
-          });
-      }
-    });
-  });
+}
+
+const closeProxyFunc = function (instance) {
+  printLog('Close AnyProxy by node module');
+  instance.close();
+}
+
+testRequest();
+testRequest(false);
+
+function testRequest(needWeb = true) {
+  NoRuleSpecExec(getStartProxyFunc(needWeb), closeProxyFunc);
 }
diff --git a/test/spec_rule/no_rule_spec_exec.js b/test/spec_rule/no_rule_spec_exec.js
new file mode 100644
index 0000000..4d94e26
--- /dev/null
+++ b/test/spec_rule/no_rule_spec_exec.js
@@ -0,0 +1,430 @@
+
+process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
+const path = require('path');
+const fs = require('fs');
+const Server = require('../server/server.js');
+const
+  {
+    proxyGet,
+    proxyPost,
+    directGet,
+    directPost,
+    directUpload,
+    proxyUpload,
+    generateUrl,
+    proxyPut,
+    directPut,
+    proxyDelete,
+    directDelete,
+    directHead,
+    proxyHead,
+    directOptions,
+    proxyOptions,
+    proxyPutUpload,
+    directPutUpload
+  } = require('../util/HttpUtil.js');
+const { CommonRequestHeader } = require('../data/headers.js');
+const { isCommonResHeaderEqual, isCommonReqEqual, printLog } = require('../util/CommonUtil.js');
+const streamEqual = require('stream-equal');
+
+/**
+ *
+ * @param {String} protocol the protocol to test
+ * @param {Function} proxyStartFunc the function to start an AnyProxy
+ * @param {Function} proxyCloseFunc the function to close the AnyProxy instance
+ */
+function testRequest(protocol = 'http', proxyStartFunc, proxyCloseFunc) {
+  function constructUrl(urlPath) {
+    return generateUrl(protocol, urlPath);
+  }
+
+  describe('Test request without proxy rules in protocol ' + protocol, () => {
+    let proxyServer;
+    let serverInstance;
+
+    beforeAll((done) => {
+      jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000;
+      printLog('Start server for no_rule_spec');
+
+      serverInstance = new Server();
+      proxyServer = proxyStartFunc();
+      setTimeout(() => {
+        done();
+      }, 2000);
+    });
+
+    afterAll(() => {
+      serverInstance && serverInstance.close();
+      proxyCloseFunc(proxyServer);
+      printLog('Closed server for no_rule_spec');
+    });
+
+
+    it('Get should work as direct without proxy rules', (done) => {
+      const url = constructUrl('/test');
+      const getParam = {
+        param: 'nothing'
+      };
+
+      proxyGet(url, getParam, CommonRequestHeader).then((proxyRes) => {
+        directGet(url, getParam, CommonRequestHeader).then(directRes => {
+          expect(proxyRes.statusCode).toEqual(200);
+          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+          expect(proxyRes.statusCode).toEqual(directRes.statusCode);
+          expect(directRes.body).toEqual(proxyRes.body);
+
+          done();
+        }, error => {
+          console.error('error happend in direct get:', error);
+          done.fail('error happend in direct get');
+        });
+      }, error => {
+        console.log('error happened in proxy get:', error);
+        done.fail('error happend in proxy get');
+      });
+    });
+
+    it('Post should work as direct without proxy rules', (done) => {
+      const url = constructUrl('/test/getuser');
+      const param = {
+        param: 'postnothing'
+      };
+
+      proxyPost(url, param, CommonRequestHeader).then(proxyRes => {
+        directPost(url, param, CommonRequestHeader).then(directRes => {
+          expect(proxyRes.statusCode).toEqual(200);
+
+          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+          expect(proxyRes.statusCode).toEqual(directRes.statusCode);
+          expect(directRes.body).toEqual(proxyRes.body);
+
+          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+
+          done();
+        }, error => {
+          console.error('error in direct post:', error);
+          done.fail('error happend in direct post');
+        });
+      }, error => {
+        console.log('error happened in proxy post,', error);
+        done.fail('error happend in proxy post');
+      });
+    });
+
+    it('PUT should work as direct without proxy rules', done => {
+      const url = constructUrl('/test/put');
+      const param = {
+        param: 'putsomething'
+      };
+      proxyPut(url, param, CommonRequestHeader).then(proxyRes => {
+        directPut(url, param, CommonRequestHeader).then(directRes => {
+          expect(directRes.statusCode).toEqual(200);
+
+          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+          expect(directRes.statusCode).toEqual(proxyRes.statusCode);
+          expect(directRes.body).toEqual(proxyRes.body);
+          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+
+          done();
+        }, error => {
+          console.error('error happened in direct put', error);
+          done.fail('error happened in direct put');
+        });
+      }, error => {
+        console.error('error happened in proxy put', error);
+        done.fail('error happened in proxy put');
+      });
+    });
+
+    it('DELETE rquest should work as direct without proxy rules', (done) => {
+      const url = constructUrl('/test/delete/123456');
+
+      proxyDelete(url, {}, CommonRequestHeader).then(proxyRes => {
+        directDelete(url, {}, CommonRequestHeader).then(directRes => {
+          expect(directRes.statusCode).toEqual(200);
+
+          expect(directRes.statusCode).toEqual(proxyRes.statusCode);
+          expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+          expect(directRes.body).toEqual(proxyRes.body);
+          expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+
+          done();
+        }, error => {
+          console.error('error happened in direct delete :', error);
+          done.fail('error happened in direct delete');
+        });
+      }, error => {
+        console.error('error happened in proxy delete :', error);
+        done.fail('error happened in proxy delete');
+      });
+    });
+
+    it('HEAD request should work as direct without proxy rules', (done) => {
+      const url = constructUrl('/test/head');
+
+      proxyHead(url, CommonRequestHeader)
+        .then(proxyRes => {
+          directHead(url, CommonRequestHeader)
+            .then(directRes => {
+              expect(directRes.statusCode).toEqual(200);
+              expect(directRes.body).toEqual('');
+
+              expect(directRes.statusCode).toEqual(proxyRes.statusCode);
+              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+              expect(directRes.body).toEqual(proxyRes.body);
+              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+
+              done();
+            }, error => {
+              console.error('error happened in direct head request:', error);
+              done.fail('error happened in direct head request');
+            });
+        }, error => {
+          console.error('error happened in proxy head request:', error);
+          done.fail('error happened in proxy head request');
+        });
+    });
+
+    it('OPTIONS request should work as direct without proxy rules', (done) => {
+      const url = constructUrl('/test/options');
+
+      proxyOptions(url, CommonRequestHeader)
+        .then(proxyRes => {
+          directOptions(url, CommonRequestHeader)
+            .then(directRes => {
+              expect(directRes.statusCode).toEqual(200);
+              expect(directRes.body).toEqual('could_be_empty');
+
+              expect(directRes.statusCode).toEqual(proxyRes.statusCode);
+              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+              expect(directRes.body).toEqual(proxyRes.body);
+              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+
+              done();
+            }, error => {
+              console.error('error happened in direct options request:', error);
+              done.fail('error happened in direct options request');
+            });
+        }, error => {
+          console.error('error happened in proxy options request:', error);
+          done.fail('error happened in proxy options request');
+        });
+    });
+
+    it('304 should work as direct without proxy rules', (done) => {
+      const url = constructUrl('/test/response/304');
+
+      proxyGet(url, CommonRequestHeader)
+        .then(proxyRes => {
+          directGet(url, CommonRequestHeader)
+            .then(directRes => {
+              expect(directRes.statusCode).toEqual(304);
+              expect(directRes.body).toEqual('');
+
+              expect(directRes.statusCode).toEqual(proxyRes.statusCode);
+              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+              expect(directRes.body).toEqual(proxyRes.body);
+              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+              done();
+            }, error => {
+              console.error('error happened in direct 304 request:', error);
+              done.fail('error happened in direct 304 request');
+            });
+        }, error => {
+          console.error('error happened in proxy 304 request:', error);
+          done.fail('error happened in proxy 304 request');
+        });
+    })
+
+    describe('Response code should be honored as direct without proxy rules', () => {
+      [301, 302, 303].forEach(code => {
+        testRedirect(code);
+      });
+
+      function testRedirect(redirectCode) {
+        it(`${redirectCode} response should work as direct without proxy rules`, (done) => {
+          const url = constructUrl(`/test/response/${redirectCode}`);
+
+          proxyGet(url)
+            .then(proxyRes => {
+              directGet(url)
+                .then(directRes => {
+                  expect(directRes.statusCode).toEqual(redirectCode);
+                  expect(directRes.headers.location).toEqual(proxyRes.headers.location);
+                  expect(directRes.statusCode).toEqual(proxyRes.statusCode);
+                  expect(directRes.headers.location).toEqual('/test');
+                  done();
+                }).catch(error => {
+                  console.log(`error happened in direct ${redirectCode}:`, error);
+                  done.fail(`error happened in direct ${redirectCode}`);
+                });
+            }).catch(error => {
+              console.log(`error happened in proxy ${redirectCode}:`, error);
+              done.fail(`error happened in proxy ${redirectCode}`);
+            });
+        });
+      }
+    });
+
+    describe('Test file download ', () => {
+      const testArray = [
+        {
+          url: constructUrl('/test/download/png'),
+          type: 'png',
+          contentType: 'image/png'
+        },
+        {
+          url: constructUrl('/test/download/webp'),
+          type: 'WEBP',
+          contentType: 'image/webp'
+        },
+        {
+          url: constructUrl('/test/download/json'),
+          type: 'JSON',
+          contentType: 'application/json; charset=utf-8'
+        },
+        {
+          url: constructUrl('/test/download/css'),
+          type: 'CSS',
+          contentType: 'text/css; charset=utf-8'
+        },
+        {
+          url: constructUrl('/test/download/ttf'),
+          type: 'TTF',
+          contentType: 'application/x-font-ttf'
+        },
+        {
+          url: constructUrl('/test/download/eot'),
+          type: 'EOT',
+          contentType: 'application/vnd.ms-fontobject'
+        },
+        {
+          url: constructUrl('/test/download/svg'),
+          type: 'SVG',
+          contentType: 'image/svg+xml'
+        },
+        {
+          url: constructUrl('/test/download/woff'),
+          type: 'WOFF',
+          contentType: 'application/font-woff'
+        },
+        {
+          url: constructUrl('/test/download/woff2'),
+          type: 'WOFF2',
+          contentType: 'application/font-woff2'
+        }
+      ];
+
+      testArray.forEach(item => {
+        testFileDownload(item.url, item.type, item.contentType);
+      });
+
+      // 封装测试文件下载的测试工具类
+      function testFileDownload(url, filetype, contentType) {
+        const describe = `${filetype} file download without rules should be work as direct download`;
+        const param = {};
+
+        it(describe, (done) => {
+          proxyGet(url, param).then(proxyRes => {
+            directGet(url, param).then(directRes => {
+              expect(proxyRes.statusCode).toEqual(200);
+
+              expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+              expect(proxyRes.statusCode).toEqual(directRes.statusCode);
+              expect(proxyRes.body).toEqual(directRes.body);
+              expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+              done();
+            }, error => {
+              console.error('error in direct get :', filetype, error);
+              done.fail(`error happend in direct get ${filetype}`);
+            });
+          }, error => {
+            console.error('error in proxy get :', filetype, error);
+            done.fail(`error happend in proxy get ${filetype}`);
+          });
+        });
+      }
+    });
+
+    describe('Test file upload', () => {
+      const formParams = {
+        param1: 'param_1',
+        param2: 'param2'
+      };
+      it('POST upload should be working', (done) => {
+        const url = constructUrl('/test/upload/png');
+        const filePath = path.resolve(__dirname, '../data/test.png');
+
+        proxyUpload(url, filePath, formParams)
+          .then(proxyRes => {
+            directUpload(url, filePath, formParams)
+              .then((directRes) => {
+                expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+                expect(isCommonReqEqual(url, serverInstance)).toBe(true);
+                assertReponse(proxyRes, directRes, filePath, done);
+              }, error => {
+                console.error('error in direct upload:', error);
+                done.fail('error in direct upload');
+              });
+          }, error => {
+            console.error('error in proxy upload:', error);
+            done.fail('error in proxy upload:');
+          });
+      });
+
+      it('PUT upload should be working', (done) => {
+        const url = constructUrl('/test/upload/putpng');
+        const filePath = path.resolve(__dirname, '../data/test.png');
+        proxyPutUpload(url, filePath, formParams)
+          .then(proxyRes => {
+            directPutUpload(url, filePath, formParams)
+              .then((directRes) => {
+                expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
+
+                assertReponse(proxyRes, directRes, filePath, done);
+              }, error => {
+                console.error('error in direct upload:', error);
+                done.fail('error in direct upload');
+              });
+          }, error => {
+            console.error('error in proxy upload:', error);
+            done.fail('error in proxy upload:');
+          });
+      });
+
+      function assertReponse(proxyRes, directRes, originFilePath, done) {
+        expect(proxyRes.statusCode).toEqual(200);
+
+        expect(proxyRes.statusCode).toEqual(directRes.statusCode);
+        // expect(proxyRes.headers.reqbody).toEqual(directRes.headers.reqbody);
+
+        // the body will be the file path
+        const directUploadedStream = fs.createReadStream(directRes.body);
+        const proxyUploadedStream = fs.createReadStream(proxyRes.body);
+        const localFileStream = fs.createReadStream(originFilePath);
+        streamEqual(directUploadedStream, localFileStream)
+          .then(isLocalEqual => {
+            expect(isLocalEqual).toBe(true);
+            streamEqual(directUploadedStream, proxyUploadedStream)
+              .then(isUploadedEqual => {
+                expect(isUploadedEqual).toBe(true);
+                done();
+              }, error => {
+                console.error('error in comparing directUpload with proxy:\n', error);
+                done.fail('error in comparing directUpload with proxy');
+              });
+            done();
+          }, error => {
+            console.error('error in comparing directUpload with local:\n', error);
+            done.fail('error in comparing directUpload with local');
+          });
+      }
+    });
+  });
+}
+
+module.exports = function (startProxyFunc, closeProxyFunc) {
+  testRequest('http', startProxyFunc, closeProxyFunc);
+  testRequest('https', startProxyFunc, closeProxyFunc);
+};