config.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. const sinon = require('sinon');
  2. describe('config', function () {
  3. let config, fakes;
  4. beforeEach(function () {
  5. fakes = {
  6. logger: sinon.spy(),
  7. prepareCertificate: sinon.stub(),
  8. prepareToken: sinon.stub(),
  9. prepareCA: sinon.stub(),
  10. };
  11. config = require('../lib/config')(fakes);
  12. });
  13. it('supplies sensible defaults', function () {
  14. expect(config()).to.deep.equal({
  15. token: null,
  16. cert: 'cert.pem',
  17. key: 'key.pem',
  18. ca: null,
  19. pfx: null,
  20. passphrase: null,
  21. production: false,
  22. address: 'api.sandbox.push.apple.com',
  23. port: 443,
  24. proxy: null,
  25. rejectUnauthorized: true,
  26. connectionRetryLimit: 10,
  27. heartBeat: 60000,
  28. requestTimeout: 5000,
  29. });
  30. });
  31. describe('address configuration', function () {
  32. let originalEnv;
  33. before(function () {
  34. originalEnv = process.env.NODE_ENV;
  35. });
  36. after(function () {
  37. process.env.NODE_ENV = originalEnv;
  38. });
  39. beforeEach(function () {
  40. process.env.NODE_ENV = '';
  41. });
  42. it('should use api.sandbox.push.apple.com as the default connection address', function () {
  43. expect(config()).to.have.property('address', 'api.sandbox.push.apple.com');
  44. });
  45. it('should use api.push.apple.com when NODE_ENV=production', function () {
  46. process.env.NODE_ENV = 'production';
  47. expect(config()).to.have.property('address', 'api.push.apple.com');
  48. });
  49. it('should give precedence to production flag over NODE_ENV=production', function () {
  50. process.env.NODE_ENV = 'production';
  51. expect(config({ production: false })).to.have.property(
  52. 'address',
  53. 'api.sandbox.push.apple.com'
  54. );
  55. });
  56. it('should use api.push.apple.com when production:true', function () {
  57. expect(config({ production: true })).to.have.property('address', 'api.push.apple.com');
  58. });
  59. it('should use a custom address when passed', function () {
  60. expect(config({ address: 'testaddress' })).to.have.property('address', 'testaddress');
  61. });
  62. describe('address is passed', function () {
  63. it('sets production to true when using production address', function () {
  64. expect(config({ address: 'api.push.apple.com' })).to.have.property('production', true);
  65. });
  66. it('sets production to false when using sandbox address', function () {
  67. process.env.NODE_ENV = 'production';
  68. expect(config({ address: 'api.sandbox.push.apple.com' })).to.have.property(
  69. 'production',
  70. false
  71. );
  72. });
  73. });
  74. });
  75. describe('credentials', function () {
  76. context('`token` not supplied, use certificate', function () {
  77. describe('passphrase', function () {
  78. it('throws an error when supplied passphrase is not a string', function () {
  79. expect(() => config({ passphrase: 123 })).to.throw('Passphrase must be a string');
  80. });
  81. it('does not throw when passphrase is a string', function () {
  82. expect(() => config({ passphrase: 'seekrit' })).to.not.throw();
  83. });
  84. it('does not throw when passphrase is not supplied', function () {
  85. expect(() => config({})).to.not.throw();
  86. });
  87. });
  88. context('pfx value is supplied without cert and key', function () {
  89. it('includes the value of `pfx`', function () {
  90. expect(config({ pfx: 'apn.pfx' })).to.have.property('pfx', 'apn.pfx');
  91. });
  92. it('does not include a value for `cert`', function () {
  93. expect(config({ pfx: 'apn.pfx' }).cert).to.be.undefined;
  94. });
  95. it('does not include a value for `key`', function () {
  96. expect(config({ pfx: 'apn.pfx' }).key).to.be.undefined;
  97. });
  98. });
  99. context('pfx value is supplied along with a cert and key', function () {
  100. it('includes the value of `pfx`', function () {
  101. expect(config({ pfx: 'apn.pfx', cert: 'cert.pem', key: 'key.pem' })).to.have.property(
  102. 'pfx',
  103. 'apn.pfx'
  104. );
  105. });
  106. it('does not include a value for `cert`', function () {
  107. expect(config({ pfx: 'apn.pfx', cert: 'cert.pem', key: 'key.pem' })).to.have.property(
  108. 'cert',
  109. 'cert.pem'
  110. );
  111. });
  112. it('does not include a value for `key`', function () {
  113. expect(config({ pfx: 'apn.pfx', cert: 'cert.pem', key: 'key.pem' })).to.have.property(
  114. 'key',
  115. 'key.pem'
  116. );
  117. });
  118. });
  119. context('pfxData value is supplied without cert and key', function () {
  120. it('includes the value of `pfxData`', function () {
  121. expect(config({ pfxData: 'apnData' })).to.have.property('pfxData', 'apnData');
  122. });
  123. it('does not include a value for `cert`', function () {
  124. expect(config({ pfxData: 'apnData' }).cert).to.be.undefined;
  125. });
  126. it('does not include a value for `key`', function () {
  127. expect(config({ pfxData: 'apnData' }).key).to.be.undefined;
  128. });
  129. });
  130. context('pfxData value is supplied along with a cert and key', function () {
  131. it('includes the value of `pfxData`', function () {
  132. expect(config({ pfxData: 'apnData', cert: 'cert.pem', key: 'key.pem' })).to.have.property(
  133. 'pfxData',
  134. 'apnData'
  135. );
  136. });
  137. it('does not include a value for `cert`', function () {
  138. expect(config({ pfxData: 'apnData', cert: 'cert.pem', key: 'key.pem' })).to.have.property(
  139. 'cert',
  140. 'cert.pem'
  141. );
  142. });
  143. it('does not include a value for `key`', function () {
  144. expect(config({ pfxData: 'apnData', cert: 'cert.pem', key: 'key.pem' })).to.have.property(
  145. 'key',
  146. 'key.pem'
  147. );
  148. });
  149. });
  150. it('loads and validates the TLS credentials', function () {
  151. fakes.prepareCertificate.returns({ cert: 'certData', key: 'keyData', pfx: 'pfxData' });
  152. const configuration = config({});
  153. expect(configuration).to.have.property('cert', 'certData');
  154. expect(configuration).to.have.property('key', 'keyData');
  155. expect(configuration).to.have.property('pfx', 'pfxData');
  156. });
  157. it('prepares the CA certificates', function () {
  158. fakes.prepareCA.returns({ ca: 'certificate1' });
  159. const configuration = config({});
  160. expect(configuration).to.have.property('ca', 'certificate1');
  161. });
  162. });
  163. context('`token` supplied', function () {
  164. const key = 'testKey';
  165. const keyId = 'abckeyId';
  166. const teamId = 'teamId123';
  167. // Clear these to ensure tls.Socket doesn't attempt to do client-auth
  168. it('clears the `pfx` property', function () {
  169. expect(config({ token: { key, keyId, teamId } })).to.not.have.property('pfx');
  170. });
  171. it('clears the `key` property', function () {
  172. expect(config({ token: { key, keyId, teamId } })).to.not.have.property('key');
  173. });
  174. it('clears the `cert` property', function () {
  175. expect(config({ token: { key, keyId, teamId } })).to.not.have.property('cert');
  176. });
  177. describe('token', function () {
  178. it('throws an error if keyId is missing', function () {
  179. expect(() => config({ token: { key, teamId } })).to.throw(/token\.keyId is missing/);
  180. });
  181. it('throws an error if keyId is not a string', function () {
  182. expect(() => config({ token: { key, teamId, keyId: 123 } })).to.throw(
  183. /token\.keyId must be a string/
  184. );
  185. });
  186. it('throws an error if teamId is missing', function () {
  187. expect(() => config({ token: { key, keyId } })).to.throw(/token\.teamId is missing/);
  188. });
  189. it('throws an error if teamId is not a string', function () {
  190. expect(() => config({ token: { key, keyId, teamId: 123 } })).to.throw(
  191. /token\.teamId must be a string/
  192. );
  193. });
  194. });
  195. it('does not invoke prepareCertificate', function () {
  196. config({ token: { key, keyId, teamId } });
  197. expect(fakes.prepareCertificate).to.have.not.been.called;
  198. });
  199. it('prepares a token generator', function () {
  200. const testConfig = { key, keyId, teamId };
  201. fakes.prepareToken.withArgs(sinon.match(testConfig)).returns(() => 'fake-token');
  202. const configuration = config({ token: testConfig });
  203. expect(fakes.prepareToken).to.have.been.called;
  204. expect(configuration.token()).to.equal('fake-token');
  205. });
  206. it('prepares the CA certificates', function () {
  207. fakes.prepareCA.returns({ ca: 'certificate1' });
  208. const configuration = config({});
  209. expect(configuration).to.have.property('ca', 'certificate1');
  210. });
  211. });
  212. });
  213. context('a null config value is passed', function () {
  214. it('should log a message with `debug`', function () {
  215. config({ address: null });
  216. expect(fakes.logger).to.be.calledWith(
  217. 'Option [address] is null. This may cause unexpected behaviour.'
  218. );
  219. });
  220. });
  221. context('a config value is undefined', function () {
  222. it('should log a message with `debug`', function () {
  223. config({ anOption: undefined });
  224. expect(fakes.logger).to.be.calledWith(
  225. 'Option [anOption] is undefined. This may cause unexpected behaviour.'
  226. );
  227. });
  228. });
  229. });