RPCInterface.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. const _ = require('lodash');
  2. const assert = require('chai').assert;
  3. const Promise = require('bluebird');
  4. const { Provider } = require('nconf');
  5. const nconf = new Provider();
  6. const rpc = require('jrpc2');
  7. const getPort = require('get-port');
  8. const temp = require('temp');
  9. const fs = require('fs');
  10. nconf.use('memory');
  11. require(`${__dirname}/../src/nconf_load_env.js`)(nconf);
  12. nconf.defaults(require(`${__dirname}/../src/default_config.js`));
  13. const { ControlServer } = require('../');
  14. const { WAIT_FOR_CREATE } = require('./constants');
  15. Promise.promisifyAll(fs);
  16. Promise.promisifyAll(temp);
  17. let rpcControlServer = new ControlServer(nconf);
  18. let rpcControlPort;
  19. let rpcClient;
  20. describe('ControlServer - RPC Interface', function () {
  21. before('setup control server', async function () {
  22. rpcControlPort = await getPort();
  23. await rpcControlServer.listen(rpcControlPort);
  24. rpcClient = new rpc.Client(new rpc.tcpTransport({ port: rpcControlPort, hostname: 'localhost' }));
  25. Promise.promisifyAll(rpcClient);
  26. });
  27. describe('#createInstances(number_of_instances)', function () {
  28. this.timeout(WAIT_FOR_CREATE*2);
  29. it('should create an instance', async function () {
  30. await rpcClient.invokeAsync('createInstances', [{ Name: 'instance-1', Group: "foo" }]);
  31. });
  32. });
  33. describe('#queryInstanceNames()', function () {
  34. it("should have an instance named \"instance-1\"", async function () {
  35. let raw = await rpcClient.invokeAsync('queryInstanceNames', [ ]);
  36. let instances = JSON.parse(raw).result;
  37. assert.deepEqual(instances, [ 'instance-1' ]);
  38. });
  39. });
  40. describe('#queryGroupNames()', function () {
  41. it("should have a group named \"foo\"", async function () {
  42. let raw = await rpcClient.invokeAsync('queryGroupNames', [ ]);
  43. let groups = JSON.parse(raw).result;
  44. assert.deepEqual(groups, [ 'foo' ]);
  45. });
  46. });
  47. describe('#queryInstancesByGroup()', function () {
  48. it("should return an instance named \"instance-1\"", async function () {
  49. let raw = await rpcClient.invokeAsync('queryInstancesByGroup', [ 'foo' ]);
  50. let instances = JSON.parse(raw).result;
  51. assert.equal(instances.length, 1);
  52. assert.ok(instances[0]);
  53. assert.equal(instances[0].name, 'instance-1');
  54. });
  55. });
  56. describe('#queryInstances()', function () {
  57. this.timeout(3000);
  58. it('should return a list of instances', async function () {
  59. let raw = await rpcClient.invokeAsync('queryInstances', []);
  60. let instances = JSON.parse(raw).result;
  61. assert.isArray(instances, 'Did not return an array');
  62. assert.isNotEmpty(instances, 'Returned an empty array');
  63. assert.isTrue(instances.every((instance) => ( typeof(instance.name) !== 'undefined' ) && ( instance.name !== null )), 'Objects were not valid');
  64. });
  65. });
  66. describe('#addInstances(definitions)', function () {
  67. this.timeout(WAIT_FOR_CREATE);
  68. it("should add an instance based on a defintion", async function () {
  69. let def = {
  70. Name: 'instance-2',
  71. Group: 'bar'
  72. };
  73. await rpcClient.invokeAsync('addInstances', [ def ]);
  74. });
  75. it("tor pool should now contain and instance that has the same name as the name specified in the defintion", function () {
  76. assert.ok(rpcControlServer.tor_pool.instance_by_name('instance-2'));
  77. });
  78. });
  79. describe('#queryInstanceByName(instance_name)', function () {
  80. this.timeout(3000);
  81. it('should return a single instance by name', async function () {
  82. let raw = await rpcClient.invokeAsync('queryInstanceByName', ['instance-1']);
  83. let instance = JSON.parse(raw).result;
  84. assert.isOk(instance);
  85. });
  86. });
  87. describe('#queryInstanceAt(index)', function () {
  88. this.timeout(3000);
  89. it('should return a single instance by index', async function () {
  90. let raw = await rpcClient.invokeAsync('queryInstanceAt', [0]);
  91. let instance = JSON.parse(raw).result;
  92. assert.isOk(instance);
  93. });
  94. });
  95. describe('#addInstanceToGroupByName()', function () {
  96. it(`should add "instance-1" to baz`, async function () {
  97. await rpcClient.invokeAsync('addInstanceToGroupByName', [ 'baz', "instance-1" ]);
  98. });
  99. it('"instance-1" should be added to "baz"', function () {
  100. assert.include(rpcControlServer.tor_pool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
  101. });
  102. after('remove from group', function () {
  103. rpcControlServer.tor_pool.groups['baz'].remove_by_name('instance-1');
  104. });
  105. });
  106. describe('#addInstanceToGroupAt()', function () {
  107. it(`should add "instance-1" to baz`, async function () {
  108. await rpcClient.invokeAsync('addInstanceToGroupAt', [ 'baz', 0 ]);
  109. });
  110. it('"instance-1" should be added to "baz"', function () {
  111. assert.include(rpcControlServer.tor_pool.instances_by_group('baz').map((i) => i.instance_name), "instance-1");
  112. });
  113. after('remove from group', function () {
  114. rpcControlServer.tor_pool.groups['baz'].remove_by_name('instance-1');
  115. });
  116. });
  117. describe('#removeInstanceFromGroupByName()', function () {
  118. before('add to group', function () {
  119. rpcControlServer.tor_pool.groups['baz'].add_by_name('instance-1');
  120. });
  121. it(`should remove "instance-1" from baz`, async function () {
  122. await rpcClient.invokeAsync('removeInstanceFromGroupByName', [ 'baz', "instance-1" ]);
  123. });
  124. it('"instance-1" should be remove from to "baz"', function () {
  125. assert.notInclude(rpcControlServer.tor_pool.group_names, "baz");
  126. });
  127. });
  128. describe('#removeInstanceFromGroupAt()', function () {
  129. before('add to group', function () {
  130. rpcControlServer.tor_pool.groups['baz'].add_by_name('instance-1');
  131. });
  132. it(`should remove "instance-1" from baz`, async function () {
  133. await rpcClient.invokeAsync('removeInstanceFromGroupAt', [ 'baz', 0 ]);
  134. });
  135. it('"instance-1" should be remove from to "baz"', function () {
  136. assert.notInclude(rpcControlServer.tor_pool.group_names, "baz");
  137. });
  138. });
  139. describe('#newIdentites()', function () {
  140. this.timeout(3000);
  141. it('should request new identities for all instances', async function () {
  142. await rpcClient.invokeAsync('newIdentites', []);
  143. });
  144. });
  145. describe('#newIdentityByName(instance_name)', function () {
  146. this.timeout(3000);
  147. it('should request new identities for all instances', async function () {
  148. await rpcClient.invokeAsync('newIdentityByName', ['instance-1']);
  149. });
  150. });
  151. describe('#newIdentityAt(index)', function () {
  152. this.timeout(3000);
  153. it('should request new identities for all instances', async function () {
  154. await rpcClient.invokeAsync('newIdentityAt', [0]);
  155. });
  156. });
  157. describe('#newIdentitiesByGroup()', function () {
  158. it(`should get new identites for all instances in group`, async function () {
  159. await rpcClient.invokeAsync('newIdentitiesByGroup', [ 'foo' ]);
  160. });
  161. });
  162. describe("#setTorConfig(config_object)", function () {
  163. this.timeout(3000);
  164. it('should set several config variables on all instances', async function () {
  165. await rpcClient.invokeAsync('setTorConfig', [ { TestSocks: 1, ProtocolWarnings: 1 } ]);
  166. });
  167. it('all instances should have the modified variables', async function() {
  168. await Promise.all(rpcControlServer.tor_pool.instances.map(async (instance) => {
  169. let var1 = await instance.get_config('TestSocks');
  170. let var2 = await instance.get_config('ProtocolWarnings');
  171. assert.equal(var1, 1);
  172. assert.equal(var2, 1);
  173. }));
  174. });
  175. after('unset config variables', async function () {
  176. await rpcControlServer.tor_pool.set_config_all('TestSocks', 0);
  177. await rpcControlServer.tor_pool.set_config_all('ProtocolWarnings', 0);
  178. });
  179. });
  180. describe('#setConfig(key, value)', function () {
  181. it('should set the default config of new instances', async function () {
  182. this.timeout(3000);
  183. await rpcClient.invokeAsync('setConfig', [ 'foo', 'bar' ]);
  184. });
  185. it('a new instance should be created with the modified property', function () {
  186. assert.equal(nconf.get('foo'), 'bar');;
  187. });
  188. after('remove instance', function () {
  189. nconf.reset();
  190. });
  191. });
  192. describe('#setTorConfigByGroup()', function () {
  193. it(`should set the config value on all instances`, async function () {
  194. await rpcClient.invokeAsync('setTorConfigByGroup', [ 'foo', { 'ProtocolWarnings': 1 } ]);
  195. });
  196. it('all instances should have the config value set', async function () {
  197. let values = _.flatten(await Promise.all(rpcControlServer.tor_pool.instances_by_group('foo').map((i) => i.get_config('ProtocolWarnings'))));
  198. assert.isTrue(values.every((v) => v === "1"));
  199. });
  200. after('unset config values', async function () {
  201. rpcControlServer.tor_pool.set_config_by_group('foo', 'ProtocolWarnings', 0);
  202. });
  203. });
  204. describe('#getConfig()', function () {
  205. before('set a property', function () {
  206. nconf.set('foo', 'bar');
  207. });
  208. it('should return the property that was set', async function () {
  209. this.timeout(6000);
  210. let raw = await rpcClient.invokeAsync('getConfig', [ 'foo' ]);
  211. let value = JSON.parse(raw).result;
  212. assert.equal(value, 'bar');
  213. });
  214. after('unset property', function () {
  215. nconf.reset();
  216. });
  217. });
  218. describe('#saveConfig()', function () {
  219. let file_path;
  220. before('set a property and create temp file', async function () {
  221. file_path = temp.path({ suffix: '.json' });
  222. nconf.remove('memory');
  223. nconf.file({ file: file_path });
  224. nconf.set('foo', 'bar');
  225. });
  226. it('should save the config to the the temp file', async function () {
  227. this.timeout(6000);
  228. await rpcClient.invokeAsync('saveConfig', []);
  229. });
  230. it('the temp file should contain the property', async function () {
  231. let tmp_file = await fs.readFileAsync(file_path, 'utf8');
  232. let tmp_json = JSON.parse(tmp_file);
  233. assert.equal(tmp_json.foo, 'bar');
  234. });
  235. after('unset property and delete file', async function () {
  236. nconf.remove('file');
  237. nconf.use('memory');
  238. nconf.reset();
  239. await fs.unlinkAsync(file_path);
  240. });
  241. });
  242. describe('#loadConfig()', function () {
  243. let file;
  244. before('create temp file with property', async function () {
  245. file = await temp.openAsync({ suffix: '.json' });
  246. await fs.writeFileAsync(file.fd, JSON.stringify({ foo: 'bar' }));
  247. nconf.remove('memory');
  248. nconf.file({ file: file.path });
  249. });
  250. it('should load the config from the the temp file', async function () {
  251. this.timeout(6000);
  252. await rpcClient.invokeAsync('loadConfig', []);
  253. });
  254. it("the application's config should contain the property", async function () {
  255. assert.equal(nconf.get('foo'), 'bar');
  256. });
  257. after('unset property and delete file', async function () {
  258. nconf.remove('file');
  259. nconf.use('memory');
  260. nconf.reset();
  261. await fs.unlinkAsync(file.path);
  262. });
  263. });
  264. describe('#getLoadBalanceMethod()', function () {
  265. this.timeout(3000);
  266. before(function () {
  267. rpcControlServer.tor_pool.load_balance_method = 'round_robin';
  268. });
  269. it('should return the current load balance method', async function () {
  270. let raw = await rpcClient.invokeAsync('getLoadBalanceMethod', []);
  271. let lb_method = JSON.parse(raw).result;
  272. assert.equal(lb_method, 'round_robin');
  273. });
  274. });
  275. describe('#setLoadBalanceMethod(load_balance_method)', function () {
  276. this.timeout(3000);
  277. it('should set the load balance method', async function () {
  278. await rpcClient.invokeAsync('setLoadBalanceMethod', ['weighted']);
  279. });
  280. it('the load balance method should be changed', function () {
  281. assert.equal(rpcControlServer.tor_pool.load_balance_method, 'weighted');
  282. });
  283. after(function () {
  284. rpcControlServer.tor_pool.load_balance_method = 'round_robin';
  285. });
  286. });
  287. describe("#getInstanceConfigByName(instance_name)", function () {
  288. this.timeout(3000);
  289. before('set config property', async function () {
  290. await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 1);
  291. });
  292. it('should retrieve the property from the tor instance', async function () {
  293. let raw = await rpcClient.invokeAsync('getInstanceConfigByName', ['instance-1', "TestSocks"]);
  294. let values = JSON.parse(raw).result;
  295. assert.isNotEmpty(values);
  296. assert.equal(values[0], "1");
  297. });
  298. after('unset config property', async function () {
  299. await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 0);
  300. });
  301. });
  302. describe("#getInstanceConfigAt(index)", function () {
  303. this.timeout(3000);
  304. before('set config property', async function () {
  305. await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 1);
  306. });
  307. it('should retrieve the property from the tor instance', async function () {
  308. let raw = await rpcClient.invokeAsync('getInstanceConfigAt', [0, "TestSocks"]);
  309. let values = JSON.parse(raw).result;
  310. assert.isNotEmpty(values);
  311. assert.equal(values[0], "1");
  312. });
  313. after('unset config property', async function () {
  314. await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 0);
  315. });
  316. });
  317. describe("#setInstanceConfigByName(instance_name)", function () {
  318. this.timeout(3000);
  319. before('set config property', async function () {
  320. await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 0);
  321. });
  322. it('should set the property for the tor instance', async function () {
  323. await rpcClient.invokeAsync('setInstanceConfigByName', ['instance-1', 'TestSocks', 1]);
  324. });
  325. it('tor instance should have the modified property', async function () {
  326. let value = await rpcControlServer.tor_pool.instance_by_name('instance-1').get_config('TestSocks');
  327. assert.equal(value, 1);
  328. });
  329. after('unset config property', async function () {
  330. await rpcControlServer.tor_pool.instance_by_name('instance-1').set_config('TestSocks', 0);
  331. });
  332. });
  333. describe("#setInstanceConfigAt(index)", function () {
  334. this.timeout(3000);
  335. before('set config property', async function () {
  336. await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 0);
  337. });
  338. it('should set the property for the tor instance', async function () {
  339. await rpcClient.invokeAsync('setInstanceConfigAt', [0, 'TestSocks', 1]);
  340. });
  341. it('tor instance should have the modified property', async function () {
  342. let value = await rpcControlServer.tor_pool.instance_at(0).get_config('TestSocks');
  343. assert.equal(value, 1);
  344. });
  345. after('unset config property', async function () {
  346. await rpcControlServer.tor_pool.instance_at(0).set_config('TestSocks', 0);
  347. });
  348. });
  349. describe('#signalAllInstances(signal)', function () {
  350. this.timeout(3000);
  351. it('should signal to all interfaces', async function () {
  352. await rpcClient.invokeAsync('signalAllInstances', [ 'DEBUG' ]);
  353. });
  354. });
  355. describe('#signalInstanceAt(signal)', function () {
  356. this.timeout(3000);
  357. it('should signal to all interfaces', async function () {
  358. await rpcClient.invokeAsync('signalInstanceAt', [ 0, 'DEBUG' ]);
  359. });
  360. });
  361. describe('#signalAllInstances(signal)', function () {
  362. this.timeout(3000);
  363. it('should signal to all interfaces', async function () {
  364. await rpcClient.invokeAsync('signalInstanceByName', [ 'instance-1', 'DEBUG' ]);
  365. });
  366. });
  367. describe('#signalInstancesByGroup()', function () {
  368. it(`should get new identites for all instances in group`, async function () {
  369. await rpcClient.invokeAsync('signalInstancesByGroup', [ 'foo', 'DEBUG' ]);
  370. });
  371. });
  372. describe("#nextInstance()", function () {
  373. this.timeout(3000);
  374. let instance_name;
  375. it('should rotate the 0th item in the instances array', async function () {
  376. instance_name = rpcControlServer.tor_pool.instances[0].instance_name;
  377. await rpcClient.invokeAsync('nextInstance', []);
  378. });
  379. it('0th item in the instances array should be different after nextInstance is called', function () {
  380. assert.notEqual(rpcControlServer.tor_pool.instances[0].instance_name, instance_name);
  381. });
  382. });
  383. describe('#nextInstanceByGroup(group)', function () {
  384. before('add "instance-1" to "foo"', function () {
  385. rpcControlServer.tor_pool.add_instance_to_group_by_name('foo', 'instance-2');
  386. });
  387. it('should rotate the instances in group "foo"', async function () {
  388. this.timeout(5000);
  389. let first_instance_name_before = rpcControlServer.tor_pool.groups['foo'][0].instance_name;
  390. await rpcClient.invokeAsync('nextInstanceByGroup', [ 'foo' ]);
  391. let first_instance_name_after = rpcControlServer.tor_pool.groups['foo'][0].instance_name;
  392. assert.notEqual(first_instance_name_after, first_instance_name_before);
  393. });
  394. after('remove "instance-1" from "foo"', function () {
  395. rpcControlServer.tor_pool.remove_instance_from_group_by_name('foo', 'instance-2');
  396. })
  397. });
  398. var instance_num1, instance_num2, i_num;
  399. describe('#removeInstanceAt(index)', function () {
  400. this.timeout(10000);
  401. it("should remove an instance at the position specified", async function () {
  402. instance_num1 = rpcControlServer.tor_pool.instances.length;
  403. await rpcClient.invokeAsync('removeInstanceAt', [0]);
  404. });
  405. it('the tor pool should contain one instance fewer', function () {
  406. assert.equal(rpcControlServer.tor_pool.instances.length, (instance_num1 - 1));
  407. });
  408. });
  409. describe('#removeInstanceByName(instance_name)', function () {
  410. this.timeout(10000);
  411. it("should remove an instance at the position specified", async function () {
  412. instance_num2 = rpcControlServer.tor_pool.instances.length;
  413. await rpcClient.invokeAsync('removeInstanceByName', [ "instance-1" ]);
  414. });
  415. it('the tor pool should contain one instance fewer', function () {
  416. assert.equal(rpcControlServer.tor_pool.instances.length, (instance_num2 - 1));
  417. });
  418. });
  419. describe('#closeInstances()', function () {
  420. this.timeout(10000);
  421. it('should shutdown all instances', async function () {
  422. instance_num = rpcControlServer.tor_pool.instances.length;
  423. await rpcClient.invokeAsync('closeInstances', [ ]);
  424. });
  425. it('no instances should be present in the pool', function () {
  426. assert.equal(rpcControlServer.tor_pool.instances.length, 0);
  427. });
  428. });
  429. after('shutdown tor pool', async function () {
  430. this.timeout(10000);
  431. await rpcControlServer.tor_pool.exit();
  432. });
  433. });