test_lockfile.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. import pytest
  2. import io
  3. from build.plugins.lib.nots.package_manager.pnpm.lockfile import PnpmLockfile
  4. @pytest.fixture()
  5. def patch_open_correct_version(monkeypatch):
  6. def mock_open(a, b):
  7. file_like = io.BytesIO(b'lockfileVersion: 5.4')
  8. return io.BufferedReader(file_like)
  9. monkeypatch.setattr(io, "open", mock_open)
  10. @pytest.fixture()
  11. def patch_open_v6(monkeypatch):
  12. def mock_open(a, b):
  13. file_like = io.BytesIO(b'lockfileVersion: "6.0"')
  14. return io.BufferedReader(file_like)
  15. monkeypatch.setattr(io, "open", mock_open)
  16. @pytest.fixture()
  17. def patch_open_incorrect_version(monkeypatch):
  18. def mock_open(a, b):
  19. file_like = io.BytesIO(b'lockfileVersion: 0')
  20. return io.BufferedReader(file_like)
  21. monkeypatch.setattr(io, "open", mock_open)
  22. @pytest.fixture()
  23. def patch_open_no_version(monkeypatch):
  24. def mock_open(a, b):
  25. file_like = io.BytesIO(b'some text')
  26. return io.BufferedReader(file_like)
  27. monkeypatch.setattr(io, "open", mock_open)
  28. def test_lockfile_read_yaml_ok(patch_open_correct_version):
  29. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  30. lf.read()
  31. assert lf.data == {"lockfileVersion": 5.4}
  32. def test_lockfile_read_v6(patch_open_v6):
  33. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  34. lf.read()
  35. assert lf.data == {"lockfileVersion": '6.0'}
  36. def test_lockfile_read_yaml_error_incorrect_lockfile_version(patch_open_incorrect_version):
  37. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  38. with pytest.raises(Exception) as e:
  39. lf.read()
  40. assert str(e.value) == (
  41. 'Error of project configuration: /pnpm-lock.yaml has lockfileVersion: 0. '
  42. + 'This version is not supported. Please, delete pnpm-lock.yaml and regenerate it using "ya tool nots --clean update-lockfile"'
  43. )
  44. def test_lockfile_read_yaml_error_no_lockfile_version(patch_open_no_version):
  45. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  46. with pytest.raises(Exception) as e:
  47. lf.read()
  48. assert str(e.value) == (
  49. 'Error of project configuration: /pnpm-lock.yaml has lockfileVersion: <no-version>. '
  50. + 'This version is not supported. Please, delete pnpm-lock.yaml and regenerate it using "ya tool nots --clean update-lockfile"'
  51. )
  52. def test_lockfile_get_packages_meta_ok():
  53. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  54. lf.data = {
  55. "packages": {
  56. "/@babel/cli/7.6.2_@babel+core@7.6.2": {
  57. "resolution": {
  58. "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==",
  59. "tarball": "@babel%2fcli/-/cli-7.6.2.tgz?rbtorrent=cb1849da3e4947e56a8f6bde6a1ec42703ddd187",
  60. },
  61. },
  62. },
  63. }
  64. packages = list(lf.get_packages_meta())
  65. pkg = packages[0]
  66. assert len(packages) == 1
  67. assert pkg.tarball_url == "@babel%2fcli/-/cli-7.6.2.tgz"
  68. assert pkg.sky_id == "rbtorrent:cb1849da3e4947e56a8f6bde6a1ec42703ddd187"
  69. assert pkg.integrity == "JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ=="
  70. assert pkg.integrity_algorithm == "sha512"
  71. def test_lockfile_get_packages_empty():
  72. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  73. lf.data = {}
  74. assert len(list(lf.get_packages_meta())) == 0
  75. def test_package_meta_invalid_key():
  76. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  77. lf.data = {
  78. "packages": {
  79. "in/valid": {},
  80. },
  81. }
  82. with pytest.raises(TypeError) as e:
  83. list(lf.get_packages_meta())
  84. assert str(e.value) == "Invalid package meta for key in/valid, missing 'resolution' key"
  85. def test_package_meta_missing_resolution():
  86. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  87. lf.data = {
  88. "packages": {
  89. "/valid/1.2.3": {},
  90. },
  91. }
  92. with pytest.raises(TypeError) as e:
  93. list(lf.get_packages_meta())
  94. assert str(e.value) == "Invalid package meta for key /valid/1.2.3, missing 'resolution' key"
  95. def test_package_meta_missing_tarball():
  96. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  97. lf.data = {
  98. "packages": {
  99. "/valid/1.2.3": {
  100. "resolution": {},
  101. },
  102. },
  103. }
  104. with pytest.raises(TypeError) as e:
  105. list(lf.get_packages_meta())
  106. assert str(e.value) == "Invalid package meta for key /valid/1.2.3, missing 'tarball' key"
  107. def test_package_meta_missing_rbtorrent():
  108. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  109. lf.data = {
  110. "packages": {
  111. "/valid/1.2.3": {
  112. "resolution": {
  113. "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==",
  114. "tarball": "valid-without-rbtorrent-1.2.3.tgz",
  115. },
  116. },
  117. },
  118. }
  119. packages = list(lf.get_packages_meta())
  120. pkg = packages[0]
  121. assert len(packages) == 1
  122. assert pkg.sky_id == ""
  123. def test_lockfile_meta_file_tarball_prohibits_file_protocol():
  124. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  125. lf.data = {
  126. "packages": {
  127. "/@babel/cli/7.6.2": {
  128. "resolution": {
  129. "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==",
  130. "tarball": "file:/some/abs/path.tgz",
  131. },
  132. },
  133. },
  134. }
  135. with pytest.raises(TypeError) as e:
  136. list(lf.get_packages_meta())
  137. assert (
  138. str(e.value)
  139. == "Invalid package meta for key /@babel/cli/7.6.2, parse error: tarball cannot point to a file, got file:/some/abs/path.tgz"
  140. )
  141. def test_lockfile_update_tarball_resolutions_ok():
  142. lf = PnpmLockfile(path="/pnpm-lock.yaml")
  143. lf.data = {
  144. "packages": {
  145. "/@babel/cli/7.6.2_@babel+core@7.6.2": {
  146. "resolution": {
  147. "integrity": "sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ==",
  148. "tarball": "@babel%2fcli/-/cli-7.6.2.tgz?rbtorrent=cb1849da3e4947e56a8f6bde6a1ec42703ddd187",
  149. },
  150. },
  151. },
  152. }
  153. lf.update_tarball_resolutions(lambda p: p.tarball_url)
  154. assert (
  155. lf.data["packages"]["/@babel/cli/7.6.2_@babel+core@7.6.2"]["resolution"]["tarball"]
  156. == "@babel%2fcli/-/cli-7.6.2.tgz"
  157. )
  158. def test_lockfile_merge():
  159. lf1 = PnpmLockfile(path="/foo/pnpm-lock.yaml")
  160. lf1.data = {
  161. "dependencies": {
  162. "a": "1.0.0",
  163. },
  164. "specifiers": {
  165. "a": "1.0.0",
  166. },
  167. "packages": {
  168. "/a/1.0.0": {},
  169. },
  170. }
  171. lf2 = PnpmLockfile(path="/bar/pnpm-lock.yaml")
  172. lf2.data = {
  173. "dependencies": {
  174. "b": "1.0.0",
  175. },
  176. "specifiers": {
  177. "b": "1.0.0",
  178. },
  179. "packages": {
  180. "/b/1.0.0": {},
  181. },
  182. }
  183. lf3 = PnpmLockfile(path="/another/baz/pnpm-lock.yaml")
  184. lf3.data = {
  185. "importers": {
  186. ".": {
  187. "dependencies": {
  188. "@a/qux": "link:../qux",
  189. "a": "1.0.0",
  190. },
  191. "specifiers": {
  192. "@a/qux": "workspace:../qux",
  193. "a": "1.0.0",
  194. },
  195. },
  196. "../qux": {
  197. "dependencies": {
  198. "b": "1.0.1",
  199. },
  200. "specifiers": {
  201. "b": "1.0.1",
  202. },
  203. },
  204. },
  205. "packages": {
  206. "/a/1.0.0": {},
  207. "/b/1.0.1": {},
  208. },
  209. }
  210. lf4 = PnpmLockfile(path="/another/quux/pnpm-lock.yaml")
  211. lf4.data = {
  212. "dependencies": {
  213. "@a/bar": "link:../../bar",
  214. },
  215. "specifiers": {
  216. "@a/bar": "workspace:../../bar",
  217. },
  218. }
  219. lf1.merge(lf2)
  220. lf1.merge(lf3)
  221. lf1.merge(lf4)
  222. assert lf1.data == {
  223. "importers": {
  224. ".": {
  225. "dependencies": {
  226. "a": "1.0.0",
  227. },
  228. "specifiers": {
  229. "a": "1.0.0",
  230. },
  231. },
  232. "../bar": {
  233. "dependencies": {
  234. "b": "1.0.0",
  235. },
  236. "specifiers": {
  237. "b": "1.0.0",
  238. },
  239. },
  240. "../another/baz": {
  241. "dependencies": {
  242. "@a/qux": "link:../qux",
  243. "a": "1.0.0",
  244. },
  245. "specifiers": {
  246. "@a/qux": "workspace:../qux",
  247. "a": "1.0.0",
  248. },
  249. },
  250. "../another/qux": {
  251. "dependencies": {
  252. "b": "1.0.1",
  253. },
  254. "specifiers": {
  255. "b": "1.0.1",
  256. },
  257. },
  258. "../another/quux": {
  259. "dependencies": {
  260. "@a/bar": "link:../../bar",
  261. },
  262. "specifiers": {
  263. "@a/bar": "workspace:../../bar",
  264. },
  265. },
  266. },
  267. "packages": {
  268. "/a/1.0.0": {},
  269. "/b/1.0.0": {},
  270. "/b/1.0.1": {},
  271. },
  272. }
  273. def test_lockfile_merge_dont_overrides_packages():
  274. lf1 = PnpmLockfile(path="/foo/pnpm-lock.yaml")
  275. lf1.data = {
  276. "dependencies": {
  277. "a": "1.0.0",
  278. },
  279. "specifiers": {
  280. "a": "1.0.0",
  281. },
  282. "packages": {
  283. "/a/1.0.0": {},
  284. },
  285. }
  286. lf2 = PnpmLockfile(path="/bar/pnpm-lock.yaml")
  287. lf2.data = {
  288. "dependencies": {
  289. "a": "1.0.0",
  290. "b": "1.0.0",
  291. },
  292. "specifiers": {
  293. "a": "1.0.0",
  294. "b": "1.0.0",
  295. },
  296. "packages": {
  297. "/a/1.0.0": {
  298. "overriden": True,
  299. },
  300. "/b/1.0.0": {},
  301. },
  302. }
  303. lf1.merge(lf2)
  304. assert lf1.data == {
  305. "importers": {
  306. ".": {
  307. "dependencies": {
  308. "a": "1.0.0",
  309. },
  310. "specifiers": {
  311. "a": "1.0.0",
  312. },
  313. },
  314. "../bar": {
  315. "dependencies": {
  316. "a": "1.0.0",
  317. "b": "1.0.0",
  318. },
  319. "specifiers": {
  320. "a": "1.0.0",
  321. "b": "1.0.0",
  322. },
  323. },
  324. },
  325. "packages": {
  326. "/a/1.0.0": {},
  327. "/b/1.0.0": {},
  328. },
  329. }