git_auth_test.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  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':
  153. ['', 'luci'],
  154. }
  155. self.assertEqual(self.global_state, want)
  156. def test_apply_global_new_auth_sso(self):
  157. git_auth.ConfigChanger(
  158. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  159. remote_url=
  160. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  161. ).apply_global('/some/fake/dir')
  162. want = {
  163. 'protocol.sso.allow': ['always'],
  164. 'url.sso://chromium/.insteadof':
  165. ['https://chromium.googlesource.com/'],
  166. }
  167. self.assertEqual(self.global_state, want)
  168. def test_apply_global_chain_sso_new(self):
  169. git_auth.ConfigChanger(
  170. mode=git_auth.ConfigMode.NEW_AUTH_SSO,
  171. remote_url=
  172. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  173. ).apply_global('/some/fake/dir')
  174. git_auth.ConfigChanger(
  175. mode=git_auth.ConfigMode.NEW_AUTH,
  176. remote_url=
  177. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git',
  178. ).apply_global('/some/fake/dir')
  179. want = {
  180. 'protocol.sso.allow': ['always'],
  181. 'credential.https://chromium.googlesource.com/.helper':
  182. ['', 'luci'],
  183. }
  184. self.assertEqual(self.global_state, want)
  185. class TestParseCookiefile(unittest.TestCase):
  186. def test_ignore_comments(self):
  187. f = io.StringIO('''\
  188. # chromium.googlesource.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  189. ''')
  190. got = git_auth._parse_cookiefile(f)
  191. want = git_auth._CookiefileInfo(
  192. contains_gerrit=False,
  193. contains_nongerrit=False,
  194. )
  195. self.assertEqual(got, want)
  196. def test_gerrit(self):
  197. f = io.StringIO('''\
  198. chromium.googlesource.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  199. ''')
  200. got = git_auth._parse_cookiefile(f)
  201. want = git_auth._CookiefileInfo(
  202. contains_gerrit=True,
  203. contains_nongerrit=False,
  204. )
  205. self.assertEqual(got, want)
  206. def test_nongerrit(self):
  207. f = io.StringIO('''\
  208. github.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  209. ''')
  210. got = git_auth._parse_cookiefile(f)
  211. want = git_auth._CookiefileInfo(
  212. contains_gerrit=False,
  213. contains_nongerrit=True,
  214. )
  215. self.assertEqual(got, want)
  216. def test_both(self):
  217. f = io.StringIO('''\
  218. chromium.googlesource.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  219. github.com,FALSE,/,TRUE,2147483647,o,git-ayatane.google.com=1//fake-credential
  220. ''')
  221. got = git_auth._parse_cookiefile(f)
  222. want = git_auth._CookiefileInfo(
  223. contains_gerrit=True,
  224. contains_nongerrit=True,
  225. )
  226. self.assertEqual(got, want)
  227. class TestConfigWizard(unittest.TestCase):
  228. maxDiff = None
  229. def setUp(self):
  230. super().setUp()
  231. self._global_state_view: Iterable[tuple[str,
  232. list[str]]] = scm_mock.GIT(self)
  233. self.ui = _FakeUI()
  234. self.wizard = git_auth.ConfigWizard(self.ui)
  235. @property
  236. def global_state(self):
  237. return dict(self._global_state_view)
  238. def test_configure_sso_global(self):
  239. parts = urllib.parse.urlsplit(
  240. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git')
  241. self.wizard._configure_sso(parts, scope='global')
  242. want = {
  243. 'url.sso://chromium/.insteadof':
  244. ['https://chromium.googlesource.com/'],
  245. }
  246. self.assertEqual(self.global_state, want)
  247. def test_configure_oauth_global(self):
  248. parts = urllib.parse.urlsplit(
  249. 'https://chromium.googlesource.com/chromium/tools/depot_tools.git')
  250. self.wizard._configure_oauth(parts, scope='global')
  251. want = {
  252. 'credential.https://chromium.googlesource.com.helper': ['', 'luci'],
  253. }
  254. self.assertEqual(self.global_state, want)
  255. def test_check_gitcookies_same(self):
  256. with tempfile.NamedTemporaryFile() as gitcookies:
  257. self.wizard._gitcookies = lambda: gitcookies.name
  258. scm.GIT.SetConfig(os.getcwd(),
  259. 'http.cookiefile',
  260. gitcookies.name,
  261. scope='global')
  262. got = self.wizard._check_gitcookies()
  263. want = git_auth._GitcookiesSituation(
  264. gitcookies_exists=True,
  265. cookiefile=gitcookies.name,
  266. cookiefile_exists=True,
  267. divergent_cookiefiles=False,
  268. )
  269. self.assertEqual(got, want)
  270. def test_check_gitcookies_different(self):
  271. with tempfile.NamedTemporaryFile(
  272. ) as gitcookies, tempfile.NamedTemporaryFile() as cookiefile:
  273. self.wizard._gitcookies = lambda: gitcookies.name
  274. scm.GIT.SetConfig(os.getcwd(),
  275. 'http.cookiefile',
  276. cookiefile.name,
  277. scope='global')
  278. got = self.wizard._check_gitcookies()
  279. want = git_auth._GitcookiesSituation(
  280. gitcookies_exists=True,
  281. cookiefile=cookiefile.name,
  282. cookiefile_exists=True,
  283. divergent_cookiefiles=True,
  284. )
  285. self.assertEqual(got, want)
  286. def test_check_gitcookies_missing_gitcookies(self):
  287. with tempfile.NamedTemporaryFile() as cookiefile:
  288. self.wizard._gitcookies = lambda: '/this-file-does-not-exist-yue'
  289. scm.GIT.SetConfig(os.getcwd(),
  290. 'http.cookiefile',
  291. cookiefile.name,
  292. scope='global')
  293. got = self.wizard._check_gitcookies()
  294. want = git_auth._GitcookiesSituation(
  295. gitcookies_exists=False,
  296. cookiefile=cookiefile.name,
  297. cookiefile_exists=True,
  298. divergent_cookiefiles=False,
  299. )
  300. self.assertEqual(got, want)
  301. def test_check_gitcookies_missing_cookiefile(self):
  302. with tempfile.NamedTemporaryFile() as gitcookies:
  303. self.wizard._gitcookies = lambda: gitcookies.name
  304. scm.GIT.SetConfig(os.getcwd(),
  305. 'http.cookiefile',
  306. '/this-file-does-not-exist-yue',
  307. scope='global')
  308. got = self.wizard._check_gitcookies()
  309. want = git_auth._GitcookiesSituation(
  310. gitcookies_exists=True,
  311. cookiefile='/this-file-does-not-exist-yue',
  312. cookiefile_exists=False,
  313. divergent_cookiefiles=False,
  314. )
  315. self.assertEqual(got, want)
  316. def test_check_gitcookies_unset(self):
  317. with tempfile.NamedTemporaryFile() as gitcookies:
  318. self.wizard._gitcookies = lambda: gitcookies.name
  319. got = self.wizard._check_gitcookies()
  320. want = git_auth._GitcookiesSituation(
  321. gitcookies_exists=True,
  322. cookiefile='',
  323. cookiefile_exists=False,
  324. divergent_cookiefiles=False,
  325. )
  326. self.assertEqual(got, want)
  327. class _FakeUI(object):
  328. """Implements UserInterface for testing."""
  329. def __init__(self, choices: Iterable[str] = ()):
  330. self.choices: list[str] = list(choices)
  331. def read_yn(self, prompt: str, *, default: bool | None = None) -> bool:
  332. choice = self.choices.pop(0)
  333. if choice == 'y':
  334. return True
  335. if choice == 'n':
  336. return False
  337. raise Exception(f'invalid choice for yn {choice!r}')
  338. def read_line(self, prompt: str, *, check=lambda *any: True) -> str:
  339. return self.choices.pop(0)
  340. def write(self, s: str) -> None:
  341. pass
  342. if __name__ == '__main__':
  343. logging.basicConfig(
  344. level=logging.DEBUG if '-v' in sys.argv else logging.ERROR)
  345. unittest.main()