1
0

__init__.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2015 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. __requires__ = ['SQLAlchemy >= 0.7']
  8. import pkg_resources
  9. import unittest
  10. import shutil
  11. import sys
  12. import tempfile
  13. import os
  14. from datetime import date
  15. from datetime import datetime
  16. from datetime import timedelta
  17. from functools import wraps
  18. import pygit2
  19. from contextlib import contextmanager
  20. from sqlalchemy import create_engine
  21. from sqlalchemy.orm import sessionmaker
  22. from sqlalchemy.orm import scoped_session
  23. sys.path.insert(0, os.path.join(os.path.dirname(
  24. os.path.abspath(__file__)), '..'))
  25. import pagure
  26. import pagure.lib
  27. import pagure.lib.model
  28. from pagure.lib.repo import PagureRepo
  29. DB_PATH = 'sqlite:///:memory:'
  30. FAITOUT_URL = 'http://faitout.cloud.fedoraproject.org/faitout/'
  31. HERE = os.path.join(os.path.dirname(os.path.abspath(__file__)))
  32. if os.environ.get('BUILD_ID'):
  33. try:
  34. import requests
  35. req = requests.get('%s/new' % FAITOUT_URL)
  36. if req.status_code == 200:
  37. DB_PATH = req.text
  38. print 'Using faitout at: %s' % DB_PATH
  39. except:
  40. pass
  41. # Remove the log handlers for the tests
  42. pagure.LOG.handlers = []
  43. @contextmanager
  44. def user_set(APP, user):
  45. """ Set the provided user as fas_user in the provided application."""
  46. # Hack used to remove the before_request function set by
  47. # flask.ext.fas_openid.FAS which otherwise kills our effort to set a
  48. # flask.g.fas_user.
  49. from flask import appcontext_pushed, g
  50. APP.before_request_funcs[None] = []
  51. def handler(sender, **kwargs):
  52. g.fas_user = user
  53. g.fas_session_id = b'123'
  54. with appcontext_pushed.connected_to(handler, APP):
  55. yield
  56. class Modeltests(unittest.TestCase):
  57. """ Model tests. """
  58. def __init__(self, method_name='runTest'):
  59. """ Constructor. """
  60. unittest.TestCase.__init__(self, method_name)
  61. self.session = None
  62. self.path = tempfile.mkdtemp(prefix='pagure-tests')
  63. self.gitrepo = None
  64. self.gitrepos = None
  65. # pylint: disable=C0103
  66. def setUp(self):
  67. """ Set up the environnment, ran before every tests. """
  68. # Clean up eventual git repo left in the present folder.
  69. for filename in os.listdir(HERE):
  70. filename = os.path.join(HERE, filename)
  71. if filename.endswith('.git') and os.path.isdir(filename):
  72. shutil.rmtree(filename)
  73. for folder in ['tickets', 'repos', 'forks', 'docs',
  74. 'requests' ,'releases']:
  75. folder = os.path.join(HERE, folder)
  76. if os.path.exists(folder):
  77. shutil.rmtree(folder)
  78. os.mkdir(folder)
  79. self.session = pagure.lib.model.create_tables(
  80. DB_PATH, acls=pagure.APP.config.get('ACLS', {}))
  81. # Create a couple of users
  82. item = pagure.lib.model.User(
  83. user='pingou',
  84. fullname='PY C',
  85. password='foo',
  86. default_email='bar@pingou.com',
  87. )
  88. self.session.add(item)
  89. item = pagure.lib.model.UserEmail(
  90. user_id=1,
  91. email='bar@pingou.com')
  92. self.session.add(item)
  93. item = pagure.lib.model.UserEmail(
  94. user_id=1,
  95. email='foo@pingou.com')
  96. self.session.add(item)
  97. item = pagure.lib.model.User(
  98. user='foo',
  99. fullname='foo bar',
  100. password='foo',
  101. default_email='foo@bar.com',
  102. )
  103. self.session.add(item)
  104. item = pagure.lib.model.UserEmail(
  105. user_id=2,
  106. email='foo@bar.com')
  107. self.session.add(item)
  108. self.session.commit()
  109. # Prevent unit-tests to send email, globally
  110. pagure.APP.config['EMAIL_SEND'] = False
  111. # pylint: disable=C0103
  112. def tearDown(self):
  113. """ Remove the test.db database if there is one. """
  114. self.session.close()
  115. # Clear temp directory
  116. shutil.rmtree(self.path)
  117. # Clear DB
  118. if os.path.exists(DB_PATH):
  119. os.unlink(DB_PATH)
  120. if DB_PATH.startswith('postgres'):
  121. if 'localhost' in DB_PATH:
  122. pagure.lib.model.drop_tables(DB_PATH, self.session.bind)
  123. else:
  124. db_name = DB_PATH.rsplit('/', 1)[1]
  125. requests.get('%s/clean/%s' % (FAITOUT_URL, db_name))
  126. class FakeGroup(object):
  127. """ Fake object used to make the FakeUser object closer to the
  128. expectations.
  129. """
  130. def __init__(self, name):
  131. """ Constructor.
  132. :arg name: the name given to the name attribute of this object.
  133. """
  134. self.name = name
  135. self.group_type = 'cla'
  136. # pylint: disable=R0903
  137. class FakeUser(object):
  138. """ Fake user used to test the fedocallib library. """
  139. def __init__(self, groups=[], username='username', cla_done=True):
  140. """ Constructor.
  141. :arg groups: list of the groups in which this fake user is
  142. supposed to be.
  143. """
  144. if isinstance(groups, basestring):
  145. groups = [groups]
  146. self.groups = groups
  147. self.username = username
  148. self.name = username
  149. self.email = 'foo@bar.com'
  150. self.approved_memberships = [
  151. FakeGroup('packager'),
  152. FakeGroup('design-team')
  153. ]
  154. self.dic = {}
  155. self.dic['timezone'] = 'Europe/Paris'
  156. self.login_time = datetime.utcnow()
  157. self.cla_done = cla_done
  158. def __getitem__(self, key):
  159. return self.dic[key]
  160. def create_projects(session):
  161. """ Create some projects in the database. """
  162. item = pagure.lib.model.Project(
  163. user_id=1, # pingou
  164. name='test',
  165. description='test project #1',
  166. hook_token='aaabbbccc',
  167. )
  168. session.add(item)
  169. item = pagure.lib.model.Project(
  170. user_id=1, # pingou
  171. name='test2',
  172. description='test project #2',
  173. hook_token='aaabbbddd',
  174. )
  175. session.add(item)
  176. session.commit()
  177. def create_projects_git(folder, bare=False):
  178. """ Create some projects in the database. """
  179. repos = []
  180. for project in ['test.git', 'test2.git']:
  181. repo_path = os.path.join(folder, project)
  182. repos.append(repo_path)
  183. if not os.path.exists(repo_path):
  184. os.makedirs(repo_path)
  185. pygit2.init_repository(repo_path, bare=bare)
  186. return repos
  187. def create_tokens(session, user_id=1):
  188. """ Create some tokens for the project in the database. """
  189. item = pagure.lib.model.Token(
  190. id='aaabbbcccddd',
  191. user_id=user_id,
  192. project_id=1,
  193. expiration=datetime.utcnow() + timedelta(days=30)
  194. )
  195. session.add(item)
  196. item = pagure.lib.model.Token(
  197. id='foo_token',
  198. user_id=user_id,
  199. project_id=1,
  200. expiration=datetime.utcnow() + timedelta(days=30)
  201. )
  202. session.add(item)
  203. item = pagure.lib.model.Token(
  204. id='expired_token',
  205. user_id=user_id,
  206. project_id=1,
  207. expiration=datetime.utcnow() - timedelta(days=1)
  208. )
  209. session.add(item)
  210. session.commit()
  211. def create_tokens_acl(session, token_id='aaabbbcccddd'):
  212. """ Create some acls for the tokens. """
  213. for aclid in range(7):
  214. item = pagure.lib.model.TokenAcl(
  215. token_id=token_id,
  216. acl_id=aclid + 1,
  217. )
  218. session.add(item)
  219. session.commit()
  220. def add_content_git_repo(folder):
  221. """ Create some content for the specified git repo. """
  222. if not os.path.exists(folder):
  223. os.makedirs(folder)
  224. brepo = pygit2.init_repository(folder, bare=True)
  225. newfolder = tempfile.mkdtemp(prefix='pagure-tests')
  226. repo = pygit2.clone_repository(folder, newfolder)
  227. # Create a file in that git repo
  228. with open(os.path.join(newfolder, 'sources'), 'w') as stream:
  229. stream.write('foo\n bar')
  230. repo.index.add('sources')
  231. repo.index.write()
  232. parents = []
  233. commit = None
  234. try:
  235. commit = repo.revparse_single('HEAD')
  236. except KeyError:
  237. pass
  238. if commit:
  239. parents = [commit.oid.hex]
  240. # Commits the files added
  241. tree = repo.index.write_tree()
  242. author = pygit2.Signature(
  243. 'Alice Author', 'alice@authors.tld')
  244. committer = pygit2.Signature(
  245. 'Cecil Committer', 'cecil@committers.tld')
  246. repo.create_commit(
  247. 'refs/heads/master', # the name of the reference to update
  248. author,
  249. committer,
  250. 'Add sources file for testing',
  251. # binary string representing the tree object ID
  252. tree,
  253. # list of binary strings representing parents of the new commit
  254. parents,
  255. )
  256. parents = []
  257. commit = None
  258. try:
  259. commit = repo.revparse_single('HEAD')
  260. except KeyError:
  261. pass
  262. if commit:
  263. parents = [commit.oid.hex]
  264. subfolder = os.path.join('folder1', 'folder2')
  265. if not os.path.exists(os.path.join(newfolder, subfolder)):
  266. os.makedirs(os.path.join(newfolder, subfolder))
  267. # Create a file in that git repo
  268. with open(os.path.join(newfolder, subfolder, 'file'), 'w') as stream:
  269. stream.write('foo\n bar\nbaz')
  270. repo.index.add(os.path.join(subfolder, 'file'))
  271. repo.index.write()
  272. # Commits the files added
  273. tree = repo.index.write_tree()
  274. author = pygit2.Signature(
  275. 'Alice Author', 'alice@authors.tld')
  276. committer = pygit2.Signature(
  277. 'Cecil Committer', 'cecil@committers.tld')
  278. repo.create_commit(
  279. 'refs/heads/master', # the name of the reference to update
  280. author,
  281. committer,
  282. 'Add some directory and a file for more testing',
  283. # binary string representing the tree object ID
  284. tree,
  285. # list of binary strings representing parents of the new commit
  286. parents
  287. )
  288. # Push to origin
  289. ori_remote = repo.remotes[0]
  290. master_ref = repo.lookup_reference('HEAD').resolve()
  291. refname = '%s:%s' % (master_ref.name, master_ref.name)
  292. PagureRepo.push(ori_remote, refname)
  293. shutil.rmtree(newfolder)
  294. def add_readme_git_repo(folder):
  295. """ Create a README file for the specified git repo. """
  296. if not os.path.exists(folder):
  297. os.makedirs(folder)
  298. brepo = pygit2.init_repository(folder, bare=True)
  299. newfolder = tempfile.mkdtemp(prefix='pagure-tests')
  300. repo = pygit2.clone_repository(folder, newfolder)
  301. content = """Pagure
  302. ======
  303. :Author: Pierre-Yves Chibon <pingou@pingoured.fr>
  304. Pagure is a light-weight git-centered forge based on pygit2.
  305. Currently, Pagure offers a web-interface for git repositories, a ticket
  306. system and possibilities to create new projects, fork existing ones and
  307. create/merge pull-requests across or within projects.
  308. Homepage: https://github.com/pypingou/pagure
  309. Dev instance: http://209.132.184.222/ (/!\\ May change unexpectedly, it's a dev instance ;-))
  310. """
  311. parents = []
  312. commit = None
  313. try:
  314. commit = repo.revparse_single('HEAD')
  315. except KeyError:
  316. pass
  317. if commit:
  318. parents = [commit.oid.hex]
  319. # Create a file in that git repo
  320. with open(os.path.join(newfolder, 'README.rst'), 'w') as stream:
  321. stream.write(content)
  322. repo.index.add('README.rst')
  323. repo.index.write()
  324. # Commits the files added
  325. tree = repo.index.write_tree()
  326. author = pygit2.Signature(
  327. 'Alice Author', 'alice@authors.tld')
  328. committer = pygit2.Signature(
  329. 'Cecil Committer', 'cecil@committers.tld')
  330. repo.create_commit(
  331. 'refs/heads/master', # the name of the reference to update
  332. author,
  333. committer,
  334. 'Add a README file',
  335. # binary string representing the tree object ID
  336. tree,
  337. # list of binary strings representing parents of the new commit
  338. parents
  339. )
  340. # Push to origin
  341. ori_remote = repo.remotes[0]
  342. master_ref = repo.lookup_reference('HEAD').resolve()
  343. refname = '%s:%s' % (master_ref.name, master_ref.name)
  344. PagureRepo.push(ori_remote, refname)
  345. shutil.rmtree(newfolder)
  346. def add_commit_git_repo(folder, ncommits=10):
  347. """ Create some more commits for the specified git repo. """
  348. if not os.path.exists(folder):
  349. os.makedirs(folder)
  350. brepo = pygit2.init_repository(folder, bare=True)
  351. newfolder = tempfile.mkdtemp(prefix='pagure-tests')
  352. repo = pygit2.clone_repository(folder, newfolder)
  353. for index in range(ncommits):
  354. # Create a file in that git repo
  355. with open(os.path.join(newfolder, 'sources'), 'a') as stream:
  356. stream.write('Row %s\n' % index)
  357. repo.index.add('sources')
  358. repo.index.write()
  359. parents = []
  360. commit = None
  361. try:
  362. commit = repo.revparse_single('HEAD')
  363. except KeyError:
  364. pass
  365. if commit:
  366. parents = [commit.oid.hex]
  367. # Commits the files added
  368. tree = repo.index.write_tree()
  369. author = pygit2.Signature(
  370. 'Alice Author', 'alice@authors.tld')
  371. committer = pygit2.Signature(
  372. 'Cecil Committer', 'cecil@committers.tld')
  373. repo.create_commit(
  374. 'refs/heads/master', # the name of the reference to update
  375. author,
  376. committer,
  377. 'Add row %s to sources file' % index,
  378. # binary string representing the tree object ID
  379. tree,
  380. # list of binary strings representing parents of the new commit
  381. parents,
  382. )
  383. # Push to origin
  384. ori_remote = repo.remotes[0]
  385. master_ref = repo.lookup_reference('HEAD').resolve()
  386. refname = '%s:%s' % (master_ref.name, master_ref.name)
  387. PagureRepo.push(ori_remote, refname)
  388. shutil.rmtree(newfolder)
  389. def add_binary_git_repo(folder, filename):
  390. """ Create a fake image file for the specified git repo. """
  391. if not os.path.exists(folder):
  392. os.makedirs(folder)
  393. brepo = pygit2.init_repository(folder, bare=True)
  394. newfolder = tempfile.mkdtemp(prefix='pagure-tests')
  395. repo = pygit2.clone_repository(folder, newfolder)
  396. content = b"""\x00\x00\x01\x00\x01\x00\x18\x18\x00\x00\x01\x00 \x00\x88
  397. \t\x00\x00\x16\x00\x00\x00(\x00\x00\x00\x18\x00x00\x00\x01\x00 \x00\x00\x00
  398. \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
  399. 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa7lM\x01\xa6kM\t\xa6kM\x01
  400. \xa4fF\x04\xa2dE\x95\xa2cD8\xa1a
  401. """
  402. parents = []
  403. commit = None
  404. try:
  405. commit = repo.revparse_single('HEAD')
  406. except KeyError:
  407. pass
  408. if commit:
  409. parents = [commit.oid.hex]
  410. # Create a file in that git repo
  411. with open(os.path.join(newfolder, filename), 'wb') as stream:
  412. stream.write(content)
  413. repo.index.add(filename)
  414. repo.index.write()
  415. # Commits the files added
  416. tree = repo.index.write_tree()
  417. author = pygit2.Signature(
  418. 'Alice Author', 'alice@authors.tld')
  419. committer = pygit2.Signature(
  420. 'Cecil Committer', 'cecil@committers.tld')
  421. repo.create_commit(
  422. 'refs/heads/master', # the name of the reference to update
  423. author,
  424. committer,
  425. 'Add a fake image file',
  426. # binary string representing the tree object ID
  427. tree,
  428. # list of binary strings representing parents of the new commit
  429. parents
  430. )
  431. # Push to origin
  432. ori_remote = repo.remotes[0]
  433. master_ref = repo.lookup_reference('HEAD').resolve()
  434. refname = '%s:%s' % (master_ref.name, master_ref.name)
  435. PagureRepo.push(ori_remote, refname)
  436. shutil.rmtree(newfolder)
  437. if __name__ == '__main__':
  438. SUITE = unittest.TestLoader().loadTestsFromTestCase(Modeltests)
  439. unittest.TextTestRunner(verbosity=2).run(SUITE)