git_auth_test.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. #!/usr/bin/env vpython3
  2. # coding=utf-8
  3. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
  4. # Use of this source code is governed by a BSD-style license that can be
  5. # found in the LICENSE file.
  6. """Unit tests for git_cl.py."""
  7. from __future__ import annotations
  8. from collections.abc import Iterable
  9. import io
  10. import logging
  11. import os
  12. import sys
  13. import tempfile
  14. from typing import Iterable
  15. import unittest
  16. import urllib.parse
  17. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  18. import git_auth
  19. import scm
  20. import scm_mock
  21. class TestConfigChanger(unittest.TestCase):
  22. maxDiff = None
  23. def setUp(self):
  24. self._global_state_view: Iterable[tuple[str,
  25. list[str]]] = scm_mock.GIT(self)
  26. @property
  27. def global_state(self):
  28. return dict(self._global_state_view)
  29. def test_apply_new_auth(self):
  30. git_auth.ConfigChanger(
  31. mode=git_auth.ConfigMode.NEW_AUTH,
  32. remote_url=
  33. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  34. ).apply('/some/fake/dir')
  35. want = {
  36. '/some/fake/dir': {
  37. 'credential.https://chromium.googlesource.com.helper':
  38. ['', 'luci'],
  39. 'http.cookiefile': [''],
  40. 'url.https://chromium.googlesource.com/chromium/tools/depot_tools.git.insteadof':
  41. [
  42. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
  43. ],
  44. },
  45. }
  46. self.assertEqual(scm.GIT._dump_config_state(), want)
  47. def test_apply_new_auth_sso(self):
  48. git_auth.ConfigChanger(
  49. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  50. remote_url=
  51. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  52. ).apply('/some/fake/dir')
  53. want = {
  54. '/some/fake/dir': {
  55. 'protocol.sso.allow': ['always'],
  56. 'url.sso://chromium/.insteadof':
  57. ['https://chromium.googlesource.com/'],
  58. 'http.cookiefile': [''],
  59. },
  60. }
  61. self.assertEqual(scm.GIT._dump_config_state(), want)
  62. def test_apply_no_auth(self):
  63. git_auth.ConfigChanger(
  64. mode=git_auth.ConfigMode.NO_AUTH,
  65. remote_url=
  66. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  67. ).apply('/some/fake/dir')
  68. want = {
  69. '/some/fake/dir': {},
  70. }
  71. self.assertEqual(scm.GIT._dump_config_state(), want)
  72. def test_apply_chain_sso_new(self):
  73. git_auth.ConfigChanger(
  74. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  75. remote_url=
  76. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  77. ).apply('/some/fake/dir')
  78. git_auth.ConfigChanger(
  79. mode=git_auth.ConfigMode.NEW_AUTH,
  80. remote_url=
  81. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  82. ).apply('/some/fake/dir')
  83. want = {
  84. '/some/fake/dir': {
  85. 'credential.https://chromium.googlesource.com.helper':
  86. ['', 'luci'],
  87. 'http.cookiefile': [''],
  88. 'url.https://chromium.googlesource.com/chromium/tools/depot_tools.git.insteadof':
  89. [
  90. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'
  91. ],
  92. },
  93. }
  94. self.assertEqual(scm.GIT._dump_config_state(), want)
  95. def test_apply_chain_new_sso(self):
  96. git_auth.ConfigChanger(
  97. mode=git_auth.ConfigMode.NEW_AUTH,
  98. remote_url=
  99. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  100. ).apply('/some/fake/dir')
  101. git_auth.ConfigChanger(
  102. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  103. remote_url=
  104. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  105. ).apply('/some/fake/dir')
  106. want = {
  107. '/some/fake/dir': {
  108. 'protocol.sso.allow': ['always'],
  109. 'url.sso://chromium/.insteadof':
  110. ['https://chromium.googlesource.com/'],
  111. 'http.cookiefile': [''],
  112. },
  113. }
  114. self.assertEqual(scm.GIT._dump_config_state(), want)
  115. def test_apply_chain_new_no(self):
  116. git_auth.ConfigChanger(
  117. mode=git_auth.ConfigMode.NEW_AUTH,
  118. remote_url=
  119. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  120. ).apply('/some/fake/dir')
  121. git_auth.ConfigChanger(
  122. mode=git_auth.ConfigMode.NO_AUTH,
  123. remote_url=
  124. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  125. ).apply('/some/fake/dir')
  126. want = {
  127. '/some/fake/dir': {},
  128. }
  129. self.assertEqual(scm.GIT._dump_config_state(), want)
  130. def test_apply_chain_sso_no(self):
  131. git_auth.ConfigChanger(
  132. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  133. remote_url=
  134. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  135. ).apply('/some/fake/dir')
  136. git_auth.ConfigChanger(
  137. mode=git_auth.ConfigMode.NO_AUTH,
  138. remote_url=
  139. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  140. ).apply('/some/fake/dir')
  141. want = {
  142. '/some/fake/dir': {},
  143. }
  144. self.assertEqual(scm.GIT._dump_config_state(), want)
  145. def test_apply_global_new_auth(self):
  146. git_auth.ConfigChanger(
  147. mode=git_auth.ConfigMode.NEW_AUTH,
  148. remote_url=
  149. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  150. ).apply_global('/some/fake/dir')
  151. want = {
  152. 'credential.https://chromium.googlesource.com.helper': ['', 'luci'],
  153. }
  154. self.assertEqual(self.global_state, want)
  155. def test_apply_global_new_auth_sso(self):
  156. git_auth.ConfigChanger(
  157. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  158. remote_url=
  159. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  160. ).apply_global('/some/fake/dir')
  161. want = {
  162. 'protocol.sso.allow': ['always'],
  163. 'url.sso://chromium/.insteadof':
  164. ['https://chromium.googlesource.com/'],
  165. }
  166. self.assertEqual(self.global_state, want)
  167. def test_apply_global_chain_sso_new(self):
  168. git_auth.ConfigChanger(
  169. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  170. remote_url=
  171. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  172. ).apply_global('/some/fake/dir')
  173. git_auth.ConfigChanger(
  174. mode=git_auth.ConfigMode.NEW_AUTH,
  175. remote_url=
  176. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  177. ).apply_global('/some/fake/dir')
  178. want = {
  179. 'protocol.sso.allow': ['always'],
  180. 'credential.https://chromium.googlesource.com.helper': ['', 'luci'],
  181. }
  182. self.assertEqual(self.global_state, want)
  183. class TestParseCookiefile(unittest.TestCase):
  184. def test_ignore_comments(self):
  185. f = io.StringIO('''\
  186. # chromium.googlesource.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  187. ''')
  188. got = git_auth._parse_cookiefile(f)
  189. want = git_auth._CookiefileInfo(
  190. contains_gerrit=False,
  191. contains_nongerrit=False,
  192. )
  193. self.assertEqual(got, want)
  194. def test_gerrit(self):
  195. f = io.StringIO('''\
  196. chromium.googlesource.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  197. ''')
  198. got = git_auth._parse_cookiefile(f)
  199. want = git_auth._CookiefileInfo(
  200. contains_gerrit=True,
  201. contains_nongerrit=False,
  202. )
  203. self.assertEqual(got, want)
  204. def test_nongerrit(self):
  205. f = io.StringIO('''\
  206. github.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  207. ''')
  208. got = git_auth._parse_cookiefile(f)
  209. want = git_auth._CookiefileInfo(
  210. contains_gerrit=False,
  211. contains_nongerrit=True,
  212. )
  213. self.assertEqual(got, want)
  214. def test_both(self):
  215. f = io.StringIO('''\
  216. chromium.googlesource.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  217. github.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  218. ''')
  219. got = git_auth._parse_cookiefile(f)
  220. want = git_auth._CookiefileInfo(
  221. contains_gerrit=True,
  222. contains_nongerrit=True,
  223. )
  224. self.assertEqual(got, want)
  225. class TestConfigWizard(unittest.TestCase):
  226. maxDiff = None
  227. def setUp(self):
  228. super().setUp()
  229. self._global_state_view: Iterable[tuple[str,
  230. list[str]]] = scm_mock.GIT(self)
  231. self.ui = _FakeUI()
  232. self.wizard = git_auth.ConfigWizard(
  233. ui=self.ui, remote_url_func=lambda: 'remote.example.com')
  234. @property
  235. def global_state(self):
  236. return dict(self._global_state_view)
  237. def test_configure_sso_global(self):
  238. parts = urllib.parse.urlsplit(
  239. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git')
  240. self.wizard._configure_sso(parts, scope='global')
  241. want = {
  242. 'url.sso://chromium/.insteadof':
  243. ['https://chromium.googlesource.com/'],
  244. }
  245. self.assertEqual(self.global_state, want)
  246. def test_configure_oauth_global(self):
  247. parts = urllib.parse.urlsplit(
  248. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git')
  249. self.wizard._configure_oauth(parts, scope='global')
  250. want = {
  251. 'credential.https://chromium.googlesource.com.helper': ['', 'luci'],
  252. }
  253. self.assertEqual(self.global_state, want)
  254. def test_check_gitcookies_same(self):
  255. with tempfile.NamedTemporaryFile() as gitcookies:
  256. self.wizard._gitcookies = lambda: gitcookies.name
  257. scm.GIT.SetConfig(os.getcwd(),
  258. 'http.cookiefile',
  259. gitcookies.name,
  260. scope='global')
  261. got = self.wizard._check_gitcookies()
  262. want = git_auth._GitcookiesSituation(
  263. gitcookies_exists=True,
  264. cookiefile=gitcookies.name,
  265. cookiefile_exists=True,
  266. divergent_cookiefiles=False,
  267. )
  268. self.assertEqual(got, want)
  269. def test_check_gitcookies_different(self):
  270. with tempfile.NamedTemporaryFile(
  271. ) as gitcookies, tempfile.NamedTemporaryFile() as cookiefile:
  272. self.wizard._gitcookies = lambda: gitcookies.name
  273. scm.GIT.SetConfig(os.getcwd(),
  274. 'http.cookiefile',
  275. cookiefile.name,
  276. scope='global')
  277. got = self.wizard._check_gitcookies()
  278. want = git_auth._GitcookiesSituation(
  279. gitcookies_exists=True,
  280. cookiefile=cookiefile.name,
  281. cookiefile_exists=True,
  282. divergent_cookiefiles=True,
  283. )
  284. self.assertEqual(got, want)
  285. def test_check_gitcookies_missing_gitcookies(self):
  286. with tempfile.NamedTemporaryFile() as cookiefile:
  287. self.wizard._gitcookies = lambda: '/this-file-does-not-exist-yue'
  288. scm.GIT.SetConfig(os.getcwd(),
  289. 'http.cookiefile',
  290. cookiefile.name,
  291. scope='global')
  292. got = self.wizard._check_gitcookies()
  293. want = git_auth._GitcookiesSituation(
  294. gitcookies_exists=False,
  295. cookiefile=cookiefile.name,
  296. cookiefile_exists=True,
  297. divergent_cookiefiles=False,
  298. )
  299. self.assertEqual(got, want)
  300. def test_check_gitcookies_missing_cookiefile(self):
  301. with tempfile.NamedTemporaryFile() as gitcookies:
  302. self.wizard._gitcookies = lambda: gitcookies.name
  303. scm.GIT.SetConfig(os.getcwd(),
  304. 'http.cookiefile',
  305. '/this-file-does-not-exist-yue',
  306. scope='global')
  307. got = self.wizard._check_gitcookies()
  308. want = git_auth._GitcookiesSituation(
  309. gitcookies_exists=True,
  310. cookiefile='/this-file-does-not-exist-yue',
  311. cookiefile_exists=False,
  312. divergent_cookiefiles=False,
  313. )
  314. self.assertEqual(got, want)
  315. def test_check_gitcookies_unset(self):
  316. with tempfile.NamedTemporaryFile() as gitcookies:
  317. self.wizard._gitcookies = lambda: gitcookies.name
  318. got = self.wizard._check_gitcookies()
  319. want = git_auth._GitcookiesSituation(
  320. gitcookies_exists=True,
  321. cookiefile='',
  322. cookiefile_exists=False,
  323. divergent_cookiefiles=False,
  324. )
  325. self.assertEqual(got, want)
  326. def test_move_file(self):
  327. with tempfile.TemporaryDirectory() as d:
  328. path = os.path.join(d, 'foo')
  329. open(path, 'w').close()
  330. self.wizard._move_file(path)
  331. self.assertEqual(os.listdir(d), ['foo.bak'])
  332. def test_move_file_backup_exists(self):
  333. with tempfile.TemporaryDirectory() as d:
  334. path = os.path.join(d, 'foo')
  335. open(path, 'w').close()
  336. open(os.path.join(d, 'foo.bak'), 'w').close()
  337. self.wizard._move_file(path)
  338. self.assertEqual(sorted(os.listdir(d)), ['foo.bak', 'foo.bak2'])
  339. class _FakeUI(object):
  340. """Implements UserInterface for testing."""
  341. def __init__(self, choices: Iterable[str] = ()):
  342. self.choices: list[str] = list(choices)
  343. def read_yn(self, prompt: str, *, default: bool | None = None) -> bool:
  344. choice = self.choices.pop(0)
  345. if choice == 'y':
  346. return True
  347. if choice == 'n':
  348. return False
  349. raise Exception(f'invalid choice for yn {choice!r}')
  350. def read_line(self, prompt: str, *, check=lambda *any: True) -> str:
  351. return self.choices.pop(0)
  352. def write(self, s: str) -> None:
  353. pass
  354. if __name__ == '__main__':
  355. logging.basicConfig(
  356. level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
  357. unittest.main()