resolveCache.js 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049
  1. var path = require('path');
  2. var mout = require('mout');
  3. var rimraf = require('rimraf');
  4. var fs = require('graceful-fs');
  5. var Q = require('q');
  6. var expect = require('expect.js');
  7. var mkdirp = require('mkdirp');
  8. var ResolveCache = require('../../lib/core/ResolveCache');
  9. var defaultConfig = require('../../lib/config');
  10. var cmd = require('../../lib/util/cmd');
  11. var copy = require('../../lib/util/copy');
  12. var md5 = require('../../lib/util/md5');
  13. describe('ResolveCache', function () {
  14. var resolveCache;
  15. var testPackage = path.resolve(__dirname, '../assets/package-a');
  16. var tempPackage = path.resolve(__dirname, '../tmp/temp-package');
  17. var tempPackage2 = path.resolve(__dirname, '../tmp/temp2-package');
  18. var cacheDir = path.join(__dirname, '../tmp/temp-resolve-cache');
  19. before(function (next) {
  20. // Delete cache folder
  21. rimraf.sync(cacheDir);
  22. // Instantiate resolver cache
  23. resolveCache = new ResolveCache(defaultConfig({
  24. storage: {
  25. packages: cacheDir
  26. }
  27. }));
  28. // Checkout test package version 0.2.0
  29. cmd('git', ['checkout', '0.2.0'], { cwd: testPackage })
  30. .then(next.bind(next, null), next);
  31. });
  32. beforeEach(function () {
  33. // Reset in memory cache for each test
  34. resolveCache.reset();
  35. });
  36. after(function () {
  37. // Remove cache folder afterwards
  38. rimraf.sync(cacheDir);
  39. });
  40. describe('.constructor', function () {
  41. beforeEach(function () {
  42. // Delete temp folder
  43. rimraf.sync(tempPackage);
  44. });
  45. after(function () {
  46. // Delete temp folder
  47. rimraf.sync(tempPackage);
  48. });
  49. function initialize(cacheDir) {
  50. return new ResolveCache(defaultConfig({
  51. storage: {
  52. packages: cacheDir
  53. }
  54. }));
  55. }
  56. it('should create the cache folder if it doesn\'t exists', function () {
  57. initialize(tempPackage);
  58. expect(fs.existsSync(tempPackage)).to.be(true);
  59. });
  60. it('should not error out if the cache folder already exists', function () {
  61. mkdirp.sync(tempPackage);
  62. initialize(tempPackage);
  63. });
  64. });
  65. describe('.store', function () {
  66. var oldFsRename = fs.rename;
  67. beforeEach(function (next) {
  68. // Restore oldFsRename
  69. fs.rename = oldFsRename;
  70. // Create a fresh copy of the test package into temp
  71. rimraf.sync(tempPackage);
  72. copy.copyDir(testPackage, tempPackage, { ignore: ['.git'] })
  73. .then(next.bind(next, null), next);
  74. });
  75. it('should move the canonical dir to source-md5/version/ folder if package meta has a version', function (next) {
  76. resolveCache.store(tempPackage, {
  77. name: 'foo',
  78. version: '1.0.0',
  79. _source: 'foo',
  80. _target: '*'
  81. })
  82. .then(function (dir) {
  83. expect(dir).to.equal(path.join(cacheDir, md5('foo'), '1.0.0'));
  84. expect(fs.existsSync(dir)).to.be(true);
  85. expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
  86. expect(fs.existsSync(tempPackage)).to.be(false);
  87. next();
  88. })
  89. .done();
  90. });
  91. it('should move the canonical dir to source-md5/target/ folder if package meta has no version', function (next) {
  92. resolveCache.store(tempPackage, {
  93. name: 'foo',
  94. _source: 'foo',
  95. _target: 'some-branch'
  96. })
  97. .then(function (dir) {
  98. expect(dir).to.equal(path.join(cacheDir, md5('foo'), 'some-branch'));
  99. expect(fs.existsSync(dir)).to.be(true);
  100. expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
  101. expect(fs.existsSync(tempPackage)).to.be(false);
  102. next();
  103. })
  104. .done();
  105. });
  106. it('should move the canonical dir to source-md5/_wildcard/ folder if package meta has no version and target is *', function (next) {
  107. resolveCache.store(tempPackage, {
  108. name: 'foo',
  109. _source: 'foo',
  110. _target: '*'
  111. })
  112. .then(function (dir) {
  113. expect(dir).to.equal(path.join(cacheDir, md5('foo'), '_wildcard'));
  114. expect(fs.existsSync(dir)).to.be(true);
  115. expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
  116. expect(fs.existsSync(tempPackage)).to.be(false);
  117. next();
  118. })
  119. .done();
  120. });
  121. it('should read the package meta if not present', function (next) {
  122. var pkgMeta = path.join(tempPackage, '.bower.json');
  123. // Copy bower.json to .bower.json and add some props
  124. copy.copyFile(path.join(tempPackage, 'component.json'), pkgMeta)
  125. .then(function () {
  126. return Q.nfcall(fs.readFile, pkgMeta)
  127. .then(function (contents) {
  128. var json = JSON.parse(contents.toString());
  129. json._target = '~0.2.0';
  130. json._source = 'git://github.com/bower/test-package.git';
  131. return Q.nfcall(fs.writeFile, pkgMeta, JSON.stringify(json, null, ' '));
  132. });
  133. })
  134. // Store as usual
  135. .then(function () {
  136. return resolveCache.store(tempPackage);
  137. })
  138. .then(function (dir) {
  139. expect(dir).to.equal(path.join(cacheDir, md5('git://github.com/bower/test-package.git'), '0.2.0'));
  140. expect(fs.existsSync(dir)).to.be(true);
  141. expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
  142. expect(fs.existsSync(tempPackage)).to.be(false);
  143. next();
  144. })
  145. .done();
  146. });
  147. it('should error out when reading the package meta if the file does not exist', function (next) {
  148. resolveCache.store(tempPackage)
  149. .then(function () {
  150. next(new Error('Should have failed'));
  151. }, function (err) {
  152. expect(err).to.be.an(Error);
  153. expect(err.code).to.equal('ENOENT');
  154. expect(err.message).to.contain(path.join(tempPackage, '.bower.json'));
  155. next();
  156. })
  157. .done();
  158. });
  159. it('should error out when reading an invalid package meta', function (next) {
  160. var pkgMeta = path.join(tempPackage, '.bower.json');
  161. return Q.nfcall(fs.writeFile, pkgMeta, 'w00t')
  162. .then(function () {
  163. return resolveCache.store(tempPackage)
  164. .then(function () {
  165. next(new Error('Should have failed'));
  166. }, function (err) {
  167. expect(err).to.be.an(Error);
  168. expect(err.code).to.equal('EMALFORMED');
  169. expect(err.message).to.contain(path.join(tempPackage, '.bower.json'));
  170. next();
  171. });
  172. })
  173. .done();
  174. });
  175. it('should move the canonical dir, even if it is in a different drive', function (next) {
  176. var hittedMock = false;
  177. fs.rename = function (src, dest, cb) {
  178. hittedMock = true;
  179. setTimeout(function () {
  180. var err = new Error();
  181. err.code = 'EXDEV';
  182. cb(err);
  183. }, 10);
  184. };
  185. resolveCache.store(tempPackage, {
  186. name: 'foo',
  187. _source: 'foobar',
  188. _target: 'some-branch'
  189. })
  190. .then(function (dir) {
  191. // Ensure mock was called
  192. expect(hittedMock).to.be(true);
  193. expect(dir).to.equal(path.join(cacheDir, md5('foobar'), 'some-branch'));
  194. expect(fs.existsSync(dir)).to.be(true);
  195. expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
  196. expect(fs.existsSync(tempPackage)).to.be(false);
  197. next();
  198. })
  199. .done();
  200. });
  201. it('should update the in-memory cache', function (next) {
  202. // Feed the cache
  203. resolveCache.versions('test-in-memory')
  204. // Copy temp package to temp package 2
  205. .then(function () {
  206. return copy.copyDir(tempPackage, tempPackage2, { ignore: ['.git'] });
  207. })
  208. // Store the two packages
  209. .then(function () {
  210. return resolveCache.store(tempPackage, {
  211. name: 'foo',
  212. version: '1.0.0',
  213. _source: 'test-in-memory',
  214. _target: '*'
  215. });
  216. })
  217. .then(function () {
  218. return resolveCache.store(tempPackage2, {
  219. name: 'foo',
  220. version: '1.0.1',
  221. _source: 'test-in-memory',
  222. _target: '*'
  223. });
  224. })
  225. // Cache should have been updated
  226. .then(function () {
  227. return resolveCache.versions('test-in-memory')
  228. .then(function (versions) {
  229. expect(versions).to.eql(['1.0.1', '1.0.0']);
  230. next();
  231. });
  232. })
  233. .done();
  234. });
  235. it('should url encode target when storing to the fs', function (next) {
  236. resolveCache.store(tempPackage, {
  237. name: 'foo',
  238. _source: 'foo',
  239. _target: 'foo/bar'
  240. })
  241. .then(function (dir) {
  242. expect(dir).to.equal(path.join(cacheDir, md5('foo'), 'foo%2Fbar'));
  243. expect(fs.existsSync(dir)).to.be(true);
  244. expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
  245. expect(fs.existsSync(tempPackage)).to.be(false);
  246. next();
  247. })
  248. .done();
  249. });
  250. it('should be possible to store two package at same time', function (next) {
  251. var store = resolveCache.store.bind(resolveCache, tempPackage, {
  252. name: 'foo',
  253. _source: 'foo',
  254. _target: 'foo/bar'
  255. });
  256. var store2 = resolveCache.store.bind(resolveCache, tempPackage2, {
  257. name: 'foo',
  258. _source: 'foo',
  259. _target: 'foo/bar'
  260. });
  261. Q.all([store(), store2()]).then(function (dirs) {
  262. var dir = dirs[0];
  263. expect(dir).to.equal(path.join(cacheDir, md5('foo'), 'foo%2Fbar'));
  264. expect(fs.existsSync(dir)).to.be(true);
  265. expect(fs.existsSync(path.join(dir, 'baz'))).to.be(true);
  266. expect(fs.existsSync(tempPackage)).to.be(false);
  267. expect(fs.existsSync(tempPackage2)).to.be(false);
  268. next();
  269. }).done();
  270. });
  271. });
  272. describe('.versions', function () {
  273. it('should resolve to an array', function (next) {
  274. resolveCache.versions(String(Math.random()))
  275. .then(function (versions) {
  276. expect(versions).to.be.an('array');
  277. next();
  278. })
  279. .done();
  280. });
  281. it('should ignore non-semver folders of the source', function (next) {
  282. var source = String(Math.random());
  283. var sourceId = md5(source);
  284. var sourceDir = path.join(cacheDir, sourceId);
  285. // Create some versions
  286. fs.mkdirSync(sourceDir);
  287. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  288. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  289. fs.mkdirSync(path.join(sourceDir, 'foo'));
  290. resolveCache.versions(source)
  291. .then(function (versions) {
  292. expect(versions).to.not.contain('foo');
  293. expect(versions).to.contain('0.0.1');
  294. expect(versions).to.contain('0.1.0');
  295. next();
  296. })
  297. .done();
  298. });
  299. it('should order the versions', function (next) {
  300. var source = String(Math.random());
  301. var sourceId = md5(source);
  302. var sourceDir = path.join(cacheDir, sourceId);
  303. // Create some versions
  304. fs.mkdirSync(sourceDir);
  305. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  306. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  307. fs.mkdirSync(path.join(sourceDir, '0.1.0-rc.1'));
  308. resolveCache.versions(source)
  309. .then(function (versions) {
  310. expect(versions).to.eql(['0.1.0', '0.1.0-rc.1', '0.0.1']);
  311. next();
  312. })
  313. .done();
  314. });
  315. it('should cache versions to speed-up subsequent calls', function (next) {
  316. var source = String(Math.random());
  317. var sourceId = md5(source);
  318. var sourceDir = path.join(cacheDir, sourceId);
  319. // Create some versions
  320. fs.mkdirSync(sourceDir);
  321. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  322. resolveCache.versions(source)
  323. .then(function () {
  324. // Remove folder
  325. rimraf.sync(sourceDir);
  326. return resolveCache.versions(source);
  327. })
  328. .then(function (versions) {
  329. expect(versions).to.eql(['0.0.1']);
  330. next();
  331. })
  332. .done();
  333. });
  334. });
  335. describe('.retrieve', function () {
  336. it('should resolve to empty if there are no packages for the requested source', function (next) {
  337. resolveCache.retrieve(String(Math.random()))
  338. .spread(function () {
  339. expect(arguments.length).to.equal(0);
  340. next();
  341. })
  342. .done();
  343. });
  344. it('should resolve to empty if there are no suitable packages for the requested target', function (next) {
  345. var source = String(Math.random());
  346. var sourceId = md5(source);
  347. var sourceDir = path.join(cacheDir, sourceId);
  348. // Create some versions
  349. fs.mkdirSync(sourceDir);
  350. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  351. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  352. fs.mkdirSync(path.join(sourceDir, '0.1.9'));
  353. fs.mkdirSync(path.join(sourceDir, '0.2.0'));
  354. resolveCache.retrieve(source, '~0.3.0')
  355. .spread(function () {
  356. expect(arguments.length).to.equal(0);
  357. return resolveCache.retrieve(source, 'some-branch');
  358. })
  359. .spread(function () {
  360. expect(arguments.length).to.equal(0);
  361. next();
  362. })
  363. .done();
  364. });
  365. it('should remove invalid packages from the cache if their package meta is missing or invalid', function (next) {
  366. var source = String(Math.random());
  367. var sourceId = md5(source);
  368. var sourceDir = path.join(cacheDir, sourceId);
  369. // Create some versions
  370. fs.mkdirSync(sourceDir);
  371. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  372. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  373. fs.mkdirSync(path.join(sourceDir, '0.1.9'));
  374. fs.mkdirSync(path.join(sourceDir, '0.2.0'));
  375. // Create an invalid package meta
  376. fs.writeFileSync(path.join(sourceDir, '0.2.0', '.bower.json'), 'w00t');
  377. resolveCache.retrieve(source, '~0.1.0')
  378. .spread(function () {
  379. var dirs = fs.readdirSync(sourceDir);
  380. expect(arguments.length).to.equal(0);
  381. expect(dirs).to.contain('0.0.1');
  382. expect(dirs).to.contain('0.2.0');
  383. next();
  384. })
  385. .done();
  386. });
  387. it('should resolve to the highest package that matches a range target, ignoring pre-releases', function (next) {
  388. var source = String(Math.random());
  389. var sourceId = md5(source);
  390. var sourceDir = path.join(cacheDir, sourceId);
  391. var json = { name: 'foo' };
  392. // Create some versions
  393. fs.mkdirSync(sourceDir);
  394. json.version = '0.0.1';
  395. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  396. fs.writeFileSync(path.join(sourceDir, '0.0.1', '.bower.json'), JSON.stringify(json, null, ' '));
  397. json.version = '0.1.0';
  398. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  399. fs.writeFileSync(path.join(sourceDir, '0.1.0', '.bower.json'), JSON.stringify(json, null, ' '));
  400. json.version = '0.1.0-rc.1';
  401. fs.mkdirSync(path.join(sourceDir, '0.1.0-rc.1'));
  402. fs.writeFileSync(path.join(sourceDir, '0.1.0-rc.1', '.bower.json'), JSON.stringify(json, null, ' '));
  403. json.version = '0.1.9';
  404. fs.mkdirSync(path.join(sourceDir, '0.1.9'));
  405. fs.writeFileSync(path.join(sourceDir, '0.1.9', '.bower.json'), JSON.stringify(json, null, ' '));
  406. json.version = '0.2.0';
  407. fs.mkdirSync(path.join(sourceDir, '0.2.0'));
  408. fs.writeFileSync(path.join(sourceDir, '0.2.0', '.bower.json'), JSON.stringify(json, null, ' '));
  409. resolveCache.retrieve(source, '~0.1.0')
  410. .spread(function (canonicalDir, pkgMeta) {
  411. expect(pkgMeta).to.be.an('object');
  412. expect(pkgMeta.version).to.equal('0.1.9');
  413. expect(canonicalDir).to.equal(path.join(sourceDir, '0.1.9'));
  414. return resolveCache.retrieve(source, '*');
  415. })
  416. .spread(function (canonicalDir, pkgMeta) {
  417. expect(pkgMeta).to.be.an('object');
  418. expect(pkgMeta.version).to.equal('0.2.0');
  419. expect(canonicalDir).to.equal(path.join(sourceDir, '0.2.0'));
  420. next();
  421. })
  422. .done();
  423. });
  424. it('should resolve to the highest package that matches a range target, not ignoring pre-releases if they are the only versions', function (next) {
  425. var source = String(Math.random());
  426. var sourceId = md5(source);
  427. var sourceDir = path.join(cacheDir, sourceId);
  428. var json = { name: 'foo' };
  429. // Create some versions
  430. fs.mkdirSync(sourceDir);
  431. json.version = '0.1.0-rc.1';
  432. fs.mkdirSync(path.join(sourceDir, '0.1.0-rc.1'));
  433. fs.writeFileSync(path.join(sourceDir, '0.1.0-rc.1', '.bower.json'), JSON.stringify(json, null, ' '));
  434. json.version = '0.1.0-rc.2';
  435. fs.mkdirSync(path.join(sourceDir, '0.1.0-rc.2'));
  436. fs.writeFileSync(path.join(sourceDir, '0.1.0-rc.2', '.bower.json'), JSON.stringify(json, null, ' '));
  437. resolveCache.retrieve(source, '~0.1.0')
  438. .spread(function (canonicalDir, pkgMeta) {
  439. expect(pkgMeta).to.be.an('object');
  440. expect(pkgMeta.version).to.equal('0.1.0-rc.2');
  441. expect(canonicalDir).to.equal(path.join(sourceDir, '0.1.0-rc.2'));
  442. next();
  443. })
  444. .done();
  445. });
  446. it('should resolve to exact match (including build metadata) if available', function (next) {
  447. var source = String(Math.random());
  448. var sourceId = md5(source);
  449. var sourceDir = path.join(cacheDir, sourceId);
  450. var json = { name: 'foo' };
  451. var encoded;
  452. // Create some versions
  453. fs.mkdirSync(sourceDir);
  454. json.version = '0.1.0';
  455. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  456. fs.writeFileSync(path.join(sourceDir, '0.1.0', '.bower.json'), JSON.stringify(json, null, ' '));
  457. json.version = '0.1.0+build.4';
  458. encoded = encodeURIComponent('0.1.0+build.4');
  459. fs.mkdirSync(path.join(sourceDir, encoded));
  460. fs.writeFileSync(path.join(sourceDir, encoded, '.bower.json'), JSON.stringify(json, null, ' '));
  461. json.version = '0.1.0+build.5';
  462. encoded = encodeURIComponent('0.1.0+build.5');
  463. fs.mkdirSync(path.join(sourceDir, encoded));
  464. fs.writeFileSync(path.join(sourceDir, encoded, '.bower.json'), JSON.stringify(json, null, ' '));
  465. json.version = '0.1.0+build.6';
  466. encoded = encodeURIComponent('0.1.0+build.6');
  467. fs.mkdirSync(path.join(sourceDir, encoded));
  468. fs.writeFileSync(path.join(sourceDir, encoded, '.bower.json'), JSON.stringify(json, null, ' '));
  469. resolveCache.retrieve(source, '0.1.0+build.5')
  470. .spread(function (canonicalDir, pkgMeta) {
  471. expect(pkgMeta).to.be.an('object');
  472. expect(pkgMeta.version).to.equal('0.1.0+build.5');
  473. expect(canonicalDir).to.equal(path.join(sourceDir, encodeURIComponent('0.1.0+build.5')));
  474. next();
  475. })
  476. .done();
  477. });
  478. it('should resolve to the _wildcard package if target is * and there are no semver versions', function (next) {
  479. var source = String(Math.random());
  480. var sourceId = md5(source);
  481. var sourceDir = path.join(cacheDir, sourceId);
  482. var json = { name: 'foo' };
  483. // Create some versions
  484. fs.mkdirSync(sourceDir);
  485. fs.mkdirSync(path.join(sourceDir, '_wildcard'));
  486. fs.writeFileSync(path.join(sourceDir, '_wildcard', '.bower.json'), JSON.stringify(json, null, ' '));
  487. resolveCache.retrieve(source, '*')
  488. .spread(function (canonicalDir, pkgMeta) {
  489. expect(pkgMeta).to.be.an('object');
  490. expect(canonicalDir).to.equal(path.join(sourceDir, '_wildcard'));
  491. next();
  492. })
  493. .done();
  494. });
  495. it('should resolve to the exact target it\'s not a semver range', function (next) {
  496. var source = String(Math.random());
  497. var sourceId = md5(source);
  498. var sourceDir = path.join(cacheDir, sourceId);
  499. var json = { name: 'foo' };
  500. // Create some versions
  501. fs.mkdirSync(sourceDir);
  502. fs.mkdirSync(path.join(sourceDir, 'some-branch'));
  503. fs.writeFileSync(path.join(sourceDir, 'some-branch', '.bower.json'), JSON.stringify(json, null, ' '));
  504. fs.mkdirSync(path.join(sourceDir, 'other-branch'));
  505. fs.writeFileSync(path.join(sourceDir, 'other-branch', '.bower.json'), JSON.stringify(json, null, ' '));
  506. resolveCache.retrieve(source, 'some-branch')
  507. .spread(function (canonicalDir, pkgMeta) {
  508. expect(pkgMeta).to.be.an('object');
  509. expect(pkgMeta).to.not.have.property('version');
  510. next();
  511. })
  512. .done();
  513. });
  514. });
  515. describe('.eliminate', function () {
  516. beforeEach(function () {
  517. mkdirp.sync(cacheDir);
  518. });
  519. it('should delete the source-md5/version folder', function (next) {
  520. var source = String(Math.random());
  521. var sourceId = md5(source);
  522. var sourceDir = path.join(cacheDir, sourceId);
  523. // Create some versions
  524. fs.mkdirSync(sourceDir);
  525. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  526. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  527. resolveCache.eliminate({
  528. name: 'foo',
  529. version: '0.0.1',
  530. _source: source,
  531. _target: '*'
  532. })
  533. .then(function () {
  534. expect(fs.existsSync(path.join(sourceDir, '0.0.1'))).to.be(false);
  535. expect(fs.existsSync(path.join(sourceDir, '0.1.0'))).to.be(true);
  536. next();
  537. })
  538. .done();
  539. });
  540. it('should delete the source-md5/target folder', function (next) {
  541. var source = String(Math.random());
  542. var sourceId = md5(source);
  543. var sourceDir = path.join(cacheDir, sourceId);
  544. // Create some versions
  545. fs.mkdirSync(sourceDir);
  546. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  547. fs.mkdirSync(path.join(sourceDir, 'some-branch'));
  548. resolveCache.eliminate({
  549. name: 'foo',
  550. _source: source,
  551. _target: 'some-branch'
  552. })
  553. .then(function () {
  554. expect(fs.existsSync(path.join(sourceDir, 'some-branch'))).to.be(false);
  555. expect(fs.existsSync(path.join(sourceDir, '0.0.1'))).to.be(true);
  556. next();
  557. })
  558. .done();
  559. });
  560. it('should delete the source-md5/_wildcard folder', function (next) {
  561. var source = String(Math.random());
  562. var sourceId = md5(source);
  563. var sourceDir = path.join(cacheDir, sourceId);
  564. // Create some versions
  565. fs.mkdirSync(sourceDir);
  566. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  567. fs.mkdirSync(path.join(sourceDir, '_wildcard'));
  568. resolveCache.eliminate({
  569. name: 'foo',
  570. _source: source,
  571. _target: '*'
  572. })
  573. .then(function () {
  574. expect(fs.existsSync(path.join(sourceDir, '_wildcard'))).to.be(false);
  575. expect(fs.existsSync(path.join(sourceDir, '0.0.1'))).to.be(true);
  576. next();
  577. })
  578. .done();
  579. });
  580. it('should delete the source-md5 folder if empty', function (next) {
  581. var source = String(Math.random());
  582. var sourceId = md5(source);
  583. var sourceDir = path.join(cacheDir, sourceId);
  584. // Create some versions
  585. fs.mkdirSync(sourceDir);
  586. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  587. resolveCache.eliminate({
  588. name: 'foo',
  589. version: '0.0.1',
  590. _source: source,
  591. _target: '*'
  592. })
  593. .then(function () {
  594. expect(fs.existsSync(path.join(sourceDir, '0.0.1'))).to.be(false);
  595. expect(fs.existsSync(path.join(sourceDir))).to.be(false);
  596. next();
  597. })
  598. .done();
  599. });
  600. it('should remove entry from in memory cache if the source-md5 folder was deleted', function (next) {
  601. var source = String(Math.random());
  602. var sourceId = md5(source);
  603. var sourceDir = path.join(cacheDir, sourceId);
  604. // Create some versions
  605. fs.mkdirSync(sourceDir);
  606. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  607. // Feed up the cache
  608. resolveCache.versions(source)
  609. // Eliminate
  610. .then(function () {
  611. return resolveCache.eliminate({
  612. name: 'foo',
  613. version: '0.0.1',
  614. _source: source,
  615. _target: '*'
  616. });
  617. })
  618. .then(function () {
  619. // At this point the parent folder should be deleted
  620. // To test against the in-memory cache, we create a folder
  621. // manually and request the versions
  622. mkdirp.sync(path.join(sourceDir, '0.0.2'));
  623. resolveCache.versions(source)
  624. .then(function (versions) {
  625. expect(versions).to.eql(['0.0.2']);
  626. next();
  627. });
  628. })
  629. .done();
  630. });
  631. });
  632. describe('.clear', function () {
  633. beforeEach(function () {
  634. mkdirp.sync(cacheDir);
  635. });
  636. it('should empty the whole cache folder', function (next) {
  637. resolveCache.clear()
  638. .then(function () {
  639. var files;
  640. expect(fs.existsSync(cacheDir)).to.be(true);
  641. files = fs.readdirSync(cacheDir);
  642. expect(files.length).to.be(0);
  643. next();
  644. })
  645. .done();
  646. });
  647. it('should erase the in-memory cache', function (next) {
  648. var source = String(Math.random());
  649. var sourceId = md5(source);
  650. var sourceDir = path.join(cacheDir, sourceId);
  651. // Create some versions
  652. fs.mkdirSync(sourceDir);
  653. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  654. // Feed the in-memory cache
  655. resolveCache.versions(source)
  656. // Clear
  657. .then(function () {
  658. return resolveCache.clear();
  659. })
  660. .then(function () {
  661. // To test against the in-memory cache, we create a folder
  662. // manually and request the versions
  663. mkdirp.sync(path.join(sourceDir, '0.0.2'));
  664. resolveCache.versions(source)
  665. .then(function (versions) {
  666. expect(versions).to.eql(['0.0.2']);
  667. next();
  668. });
  669. })
  670. .done();
  671. });
  672. });
  673. describe('.reset', function () {
  674. it('should erase the in-memory cache', function (next) {
  675. var source = String(Math.random());
  676. var sourceId = md5(source);
  677. var sourceDir = path.join(cacheDir, sourceId);
  678. // Create some versions
  679. fs.mkdirSync(sourceDir);
  680. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  681. // Feed the in-memory cache
  682. resolveCache.versions(source)
  683. .then(function () {
  684. // Delete 0.0.1 and create 0.0.2
  685. fs.rmdirSync(path.join(sourceDir, '0.0.1'));
  686. fs.mkdirSync(path.join(sourceDir, '0.0.2'));
  687. // Reset cache
  688. resolveCache.reset();
  689. // Get versions
  690. return resolveCache.versions(source);
  691. })
  692. .then(function (versions) {
  693. expect(versions).to.eql(['0.0.2']);
  694. next();
  695. })
  696. .done();
  697. });
  698. });
  699. describe('.list', function () {
  700. beforeEach(function () {
  701. rimraf.sync(cacheDir);
  702. mkdirp.sync(cacheDir);
  703. });
  704. it('should resolve to an empty array if the cache is empty', function (next) {
  705. resolveCache.list()
  706. .then(function (entries) {
  707. expect(entries).to.be.an('array');
  708. expect(entries.length).to.be(0);
  709. next();
  710. })
  711. .done();
  712. });
  713. it('should resolve to an ordered array of entries (name ASC, release ASC)', function (next) {
  714. var source = 'list-package-1';
  715. var sourceId = md5(source);
  716. var sourceDir = path.join(cacheDir, sourceId);
  717. var source2 = 'list-package-2';
  718. var sourceId2 = md5(source2);
  719. var sourceDir2 = path.join(cacheDir, sourceId2);
  720. var json = {
  721. name: 'foo'
  722. };
  723. // Create some versions for different sources
  724. fs.mkdirSync(sourceDir);
  725. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  726. json.version = '0.0.1';
  727. fs.writeFileSync(path.join(sourceDir, '0.0.1', '.bower.json'), JSON.stringify(json, null, ' '));
  728. fs.mkdirSync(path.join(sourceDir, '0.1.0'));
  729. json.version = '0.1.0';
  730. fs.writeFileSync(path.join(sourceDir, '0.1.0', '.bower.json'), JSON.stringify(json, null, ' '));
  731. delete json.version;
  732. fs.mkdirSync(path.join(sourceDir, 'foo'));
  733. json._target = 'foo';
  734. fs.writeFileSync(path.join(sourceDir, 'foo', '.bower.json'), JSON.stringify(json, null, ' '));
  735. fs.mkdirSync(path.join(sourceDir, 'bar'));
  736. json._target = 'bar';
  737. fs.writeFileSync(path.join(sourceDir, 'bar', '.bower.json'), JSON.stringify(json, null, ' '));
  738. fs.mkdirSync(path.join(sourceDir, 'aa'));
  739. json._target = 'aa';
  740. fs.writeFileSync(path.join(sourceDir, 'aa', '.bower.json'), JSON.stringify(json, null, ' '));
  741. delete json._target;
  742. fs.mkdirSync(sourceDir2);
  743. fs.mkdirSync(path.join(sourceDir2, '0.2.1'));
  744. json.version = '0.2.1';
  745. fs.writeFileSync(path.join(sourceDir2, '0.2.1', '.bower.json'), JSON.stringify(json, null, ' '));
  746. fs.mkdirSync(path.join(sourceDir2, '0.2.0'));
  747. json.name = 'abc';
  748. json.version = '0.2.0';
  749. fs.writeFileSync(path.join(sourceDir2, '0.2.0', '.bower.json'), JSON.stringify(json, null, ' '));
  750. resolveCache.list()
  751. .then(function (entries) {
  752. var expectedJson;
  753. var bowerDir = path.join(__dirname, '../..');
  754. expect(entries).to.be.an('array');
  755. expectedJson = fs.readFileSync(path.join(__dirname, '../assets/resolve-cache/list-json-1.json'));
  756. expectedJson = expectedJson.toString().trim();
  757. mout.object.forOwn(entries, function (entry) {
  758. // Trim absolute bower path from json
  759. entry.canonicalDir = entry.canonicalDir.substr(bowerDir.length);
  760. // Convert windows \ paths to /
  761. entry.canonicalDir = entry.canonicalDir.replace(/\\/g, '/');
  762. });
  763. json = JSON.stringify(entries, null, ' ');
  764. expect(json).to.equal(expectedJson);
  765. next();
  766. })
  767. .done();
  768. });
  769. it('should ignore lurking files where dirs are expected', function (next) {
  770. var source = 'list-package-1';
  771. var sourceId = md5(source);
  772. var sourceDir = path.join(cacheDir, sourceId);
  773. var json = {
  774. name: 'foo'
  775. };
  776. // Create some versions
  777. fs.mkdirSync(sourceDir);
  778. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  779. json.version = '0.0.1';
  780. fs.writeFileSync(path.join(sourceDir, '0.0.1', '.bower.json'), JSON.stringify(json, null, ' '));
  781. // Create lurking files
  782. fs.writeFileSync(path.join(cacheDir, 'foo'), 'w00t');
  783. fs.writeFileSync(path.join(cacheDir, '.DS_Store'), '');
  784. fs.writeFileSync(path.join(sourceDir, 'foo'), 'w00t');
  785. fs.writeFileSync(path.join(sourceDir, '.DS_Store'), '');
  786. // It should not error out
  787. resolveCache.list()
  788. .then(function (entries) {
  789. expect(entries).to.be.an('array');
  790. expect(entries.length).to.be(1);
  791. expect(entries[0].pkgMeta).to.eql(json);
  792. // Lurking file should have been removed
  793. expect(fs.existsSync(path.join(cacheDir, 'foo'))).to.be(false);
  794. expect(fs.existsSync(path.join(cacheDir, '.DS_Store'))).to.be(false);
  795. expect(fs.existsSync(path.join(sourceDir, 'foo'))).to.be(false);
  796. expect(fs.existsSync(path.join(sourceDir, '.DS_Store'))).to.be(false);
  797. next();
  798. })
  799. .done();
  800. });
  801. it('should delete entries if failed to read package meta', function (next) {
  802. var source = 'list-package-1';
  803. var sourceId = md5(source);
  804. var sourceDir = path.join(cacheDir, sourceId);
  805. var json = {
  806. name: 'foo'
  807. };
  808. // Create invalid versions
  809. fs.mkdirSync(sourceDir);
  810. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  811. fs.mkdirSync(path.join(sourceDir, '0.0.2'));
  812. fs.writeFileSync(path.join(sourceDir, '0.0.2', '.bower.json'), 'w00t');
  813. // Create valid version
  814. fs.mkdirSync(path.join(sourceDir, '0.0.3'));
  815. json.version = '0.0.3';
  816. fs.writeFileSync(path.join(sourceDir, '0.0.3', '.bower.json'), JSON.stringify(json, null, ' '));
  817. // It should not error out
  818. resolveCache.list()
  819. .then(function (entries) {
  820. expect(entries).to.be.an('array');
  821. expect(entries.length).to.be(1);
  822. expect(entries[0].pkgMeta).to.eql(json);
  823. // Packages with invalid metas should have been removed
  824. expect(fs.existsSync(path.join(sourceDir, '0.0.1'))).to.be(false);
  825. expect(fs.existsSync(path.join(sourceDir, '0.0.2'))).to.be(false);
  826. next();
  827. })
  828. .done();
  829. });
  830. });
  831. describe('#clearRuntimeCache', function () {
  832. it('should clear the in-memory cache for all sources', function (next) {
  833. var source = String(Math.random());
  834. var sourceId = md5(source);
  835. var sourceDir = path.join(cacheDir, sourceId);
  836. var source2 = String(Math.random());
  837. var sourceId2 = md5(source2);
  838. var sourceDir2 = path.join(cacheDir, sourceId2);
  839. // Create some versions
  840. fs.mkdirSync(sourceDir);
  841. fs.mkdirSync(path.join(sourceDir, '0.0.1'));
  842. fs.mkdirSync(sourceDir2);
  843. fs.mkdirSync(path.join(sourceDir2, '0.0.2'));
  844. // Feed the cache
  845. resolveCache.versions(source)
  846. .then(function () {
  847. return resolveCache.versions(source2);
  848. })
  849. .then(function () {
  850. // Create some more
  851. fs.mkdirSync(path.join(sourceDir, '0.0.3'));
  852. fs.mkdirSync(path.join(sourceDir2, '0.0.4'));
  853. // Reset cache
  854. ResolveCache.clearRuntimeCache();
  855. })
  856. .then(function () {
  857. return resolveCache.versions(source)
  858. .then(function (versions) {
  859. expect(versions).to.eql(['0.0.3', '0.0.1']);
  860. return resolveCache.versions(source2);
  861. })
  862. .then(function (versions) {
  863. expect(versions).to.eql(['0.0.4', '0.0.2']);
  864. next();
  865. });
  866. })
  867. .done();
  868. });
  869. });
  870. });