db_test.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. from base_test import DakTestCase, fixture
  2. from daklib.config import Config
  3. from daklib.dbconn import *
  4. from sqlalchemy import create_engine, func, __version__
  5. from sqlalchemy.exc import SADeprecationWarning
  6. from sqlalchemy.schema import DDL
  7. import pickle
  8. import warnings
  9. all_tables = ['architecture', 'archive', 'bin_associations', 'bin_contents',
  10. 'binaries', 'binary_acl', 'binary_acl_map', 'build_queue', 'build_queue_files',
  11. 'changes', 'changes_pending_binaries', 'changes_pending_files',
  12. 'changes_pending_files_map', 'changes_pending_source',
  13. 'changes_pending_source_files', 'changes_pool_files', 'component', 'config',
  14. 'dsc_files', 'files', 'fingerprint', 'keyring_acl_map', 'keyrings', 'location',
  15. 'maintainer', 'new_comments', 'override', 'override_type', 'policy_queue',
  16. 'priority', 'section', 'source', 'source_acl', 'src_associations',
  17. 'src_format', 'src_uploaders', 'suite', 'suite_architectures',
  18. 'suite_build_queue_copy', 'suite_src_formats', 'uid', 'upload_blocks']
  19. drop_plpgsql = "DROP LANGUAGE IF EXISTS plpgsql CASCADE"
  20. create_plpgsql = "CREATE LANGUAGE plpgsql"
  21. create_function = """CREATE OR REPLACE FUNCTION tfunc_set_modified() RETURNS trigger AS $$
  22. BEGIN NEW.modified = now(); return NEW; END;
  23. $$ LANGUAGE 'plpgsql'"""
  24. create_trigger = """CREATE TRIGGER modified_%s BEFORE UPDATE ON %s
  25. FOR EACH ROW EXECUTE PROCEDURE tfunc_set_modified()"""
  26. class DBDakTestCase(DakTestCase):
  27. def execute(self, statement):
  28. DDL(statement).execute(self.metadata.bind)
  29. def create_all_triggers(self):
  30. for statement in (drop_plpgsql, create_plpgsql, create_function):
  31. self.execute(statement)
  32. for table in all_tables:
  33. self.execute(create_trigger % (table, table))
  34. metadata = None
  35. def initialize(self):
  36. cnf = Config()
  37. if cnf["DB::Name"] in ('backports', 'obscurity', 'projectb'):
  38. self.fail("You have configured an invalid database name: '%s'." % \
  39. cnf["DB::Name"])
  40. if cnf["DB::Host"]:
  41. # TCP/IP
  42. connstr = "postgres://%s" % cnf["DB::Host"]
  43. if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
  44. connstr += ":%s" % cnf["DB::Port"]
  45. connstr += "/%s" % cnf["DB::Name"]
  46. else:
  47. # Unix Socket
  48. connstr = "postgres:///%s" % cnf["DB::Name"]
  49. if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
  50. connstr += "?port=%s" % cnf["DB::Port"]
  51. pickle_filename = 'db-metadata-%s.pkl' % __version__
  52. pickle_file = open(fixture(pickle_filename), 'r')
  53. DBDakTestCase.metadata = pickle.load(pickle_file)
  54. self.metadata.ddl_listeners = pickle.load(pickle_file)
  55. pickle_file.close()
  56. self.metadata.bind = create_engine(connstr)
  57. self.metadata.create_all()
  58. self.create_all_triggers()
  59. def setup_suites(self):
  60. "setup a hash of Suite objects in self.suite"
  61. if 'suite' in self.__dict__:
  62. return
  63. self.suite = {}
  64. for suite_name in ('lenny', 'squeeze', 'sid'):
  65. self.suite[suite_name] = Suite(suite_name = suite_name, version = '-')
  66. self.session.add_all(self.suite.values())
  67. def setup_architectures(self):
  68. "setup Architecture objects in self.arch and connect to suites"
  69. if 'arch' in self.__dict__:
  70. return
  71. self.setup_suites()
  72. self.arch = {}
  73. for arch_string in ('source', 'all', 'i386', 'amd64', 'kfreebsd-i386'):
  74. self.arch[arch_string] = Architecture(arch_string)
  75. if arch_string != 'kfreebsd-i386':
  76. self.arch[arch_string].suites = self.suite.values()
  77. else:
  78. self.arch[arch_string].suites = [self.suite['squeeze'], self.suite['sid']]
  79. # hard code ids for source and all
  80. self.arch['source'].arch_id = 1
  81. self.arch['all'].arch_id = 2
  82. self.session.add_all(self.arch.values())
  83. def setup_components(self):
  84. 'create some Component objects'
  85. if 'comp' in self.__dict__:
  86. return
  87. self.comp = {}
  88. for name in ('main', 'contrib', 'non-free'):
  89. self.comp[name] = Component(component_name = name)
  90. self.session.add_all(self.comp.values())
  91. def setup_locations(self):
  92. 'create some Location objects'
  93. if 'loc' in self.__dict__:
  94. return
  95. self.setup_components()
  96. self.loc = {}
  97. self.loc['main'] = Location( \
  98. path = fixture('ftp/pool/'), component = self.comp['main'])
  99. self.loc['contrib'] = Location( \
  100. path = fixture('ftp/pool/'), component = self.comp['contrib'])
  101. self.session.add_all(self.loc.values())
  102. def setup_poolfiles(self):
  103. 'create some PoolFile objects'
  104. if 'file' in self.__dict__:
  105. return
  106. self.setup_locations()
  107. self.file = {}
  108. self.file['hello_2.2-3.dsc'] = PoolFile(filename = 'main/h/hello/hello_2.2-3.dsc', \
  109. location = self.loc['main'], filesize = 0, md5sum = '')
  110. self.file['hello_2.2-2.dsc'] = PoolFile(filename = 'main/h/hello/hello_2.2-2.dsc', \
  111. location = self.loc['main'], filesize = 0, md5sum = '')
  112. self.file['hello_2.2-1.dsc'] = PoolFile(filename = 'main/h/hello/hello_2.2-1.dsc', \
  113. location = self.loc['main'], filesize = 0, md5sum = '')
  114. self.file['gnome-hello_3.0-1.dsc'] = PoolFile( \
  115. filename = 'main/g/gnome-hello/gnome-hello_3.0-1.dsc', \
  116. location = self.loc['contrib'], filesize = 0, md5sum = '')
  117. self.file['hello_2.2-1_i386.deb'] = PoolFile( \
  118. filename = 'main/h/hello/hello_2.2-1_i386.deb', \
  119. location = self.loc['main'], filesize = 0, md5sum = '')
  120. self.file['gnome-hello_2.2-1_i386.deb'] = PoolFile( \
  121. filename = 'main/h/hello/gnome-hello_2.2-1_i386.deb', \
  122. location = self.loc['main'], filesize = 0, md5sum = '')
  123. self.file['python-hello_2.2-1_all.deb'] = PoolFile( \
  124. filename = 'main/h/hello/python-hello_2.2-1_all.deb', \
  125. location = self.loc['main'], filesize = 0, md5sum = '')
  126. self.file['gnome-hello_3.0-1_i386.deb'] = PoolFile( \
  127. filename = 'main/g/gnome-hello/gnome-hello_3.0-1_i386.deb', \
  128. location = self.loc['contrib'], filesize = 0, md5sum = '')
  129. self.file['sl_3.03-16.dsc'] = PoolFile(filename = 'main/s/sl/sl_3.03-16.dsc', \
  130. location = self.loc['main'], filesize = 0, md5sum = '')
  131. self.file['python2.6_2.6.6-8.dsc'] = PoolFile( \
  132. filename = 'main/p/python2.6/python2.6_2.6.6-8.dsc', \
  133. location = self.loc['main'], filesize = 0, md5sum = '')
  134. self.session.add_all(self.file.values())
  135. def setup_maintainers(self):
  136. 'create some Maintainer objects'
  137. if 'maintainer' in self.__dict__:
  138. return
  139. self.maintainer = {}
  140. self.maintainer['maintainer'] = Maintainer(name = 'Mr. Maintainer')
  141. self.maintainer['uploader'] = Maintainer(name = 'Mrs. Uploader')
  142. self.maintainer['lazyguy'] = Maintainer(name = 'Lazy Guy')
  143. self.session.add_all(self.maintainer.values())
  144. def setup_sources(self):
  145. 'create DBSource objects'
  146. if 'source' in self.__dict__:
  147. return
  148. install_date = self.now()
  149. self.setup_maintainers()
  150. self.setup_suites()
  151. self.setup_poolfiles()
  152. self.source = {}
  153. self.source['hello_2.2-2'] = DBSource(source = 'hello', version = '2.2-2', \
  154. maintainer = self.maintainer['maintainer'], \
  155. changedby = self.maintainer['uploader'], \
  156. poolfile = self.file['hello_2.2-2.dsc'], install_date = install_date)
  157. self.source['hello_2.2-2'].suites.append(self.suite['sid'])
  158. self.source['hello_2.2-1'] = DBSource(source = 'hello', version = '2.2-1', \
  159. maintainer = self.maintainer['maintainer'], \
  160. changedby = self.maintainer['uploader'], \
  161. poolfile = self.file['hello_2.2-1.dsc'], install_date = install_date)
  162. self.source['hello_2.2-1'].suites.append(self.suite['sid'])
  163. self.source['gnome-hello_3.0-1'] = DBSource(source = 'gnome-hello', \
  164. version = '3.0-1', maintainer = self.maintainer['maintainer'], \
  165. changedby = self.maintainer['uploader'], \
  166. poolfile = self.file['gnome-hello_3.0-1.dsc'], install_date = install_date)
  167. self.source['gnome-hello_3.0-1'].suites.append(self.suite['sid'])
  168. self.source['sl_3.03-16'] = DBSource(source = 'sl', version = '3.03-16', \
  169. maintainer = self.maintainer['maintainer'], \
  170. changedby = self.maintainer['uploader'], \
  171. poolfile = self.file['sl_3.03-16.dsc'], install_date = install_date)
  172. self.source['sl_3.03-16'].suites.append(self.suite['squeeze'])
  173. self.source['sl_3.03-16'].suites.append(self.suite['sid'])
  174. self.session.add_all(self.source.values())
  175. def setup_binaries(self):
  176. 'create DBBinary objects'
  177. if 'binary' in self.__dict__:
  178. return
  179. self.setup_sources()
  180. self.setup_architectures()
  181. self.binary = {}
  182. self.binary['hello_2.2-1_i386'] = DBBinary(package = 'hello', \
  183. source = self.source['hello_2.2-1'], version = '2.2-1', \
  184. maintainer = self.maintainer['maintainer'], \
  185. architecture = self.arch['i386'], \
  186. poolfile = self.file['hello_2.2-1_i386.deb'])
  187. self.binary['hello_2.2-1_i386'].suites.append(self.suite['squeeze'])
  188. self.binary['hello_2.2-1_i386'].suites.append(self.suite['sid'])
  189. self.binary['gnome-hello_2.2-1_i386'] = DBBinary(package = 'gnome-hello', \
  190. source = self.source['hello_2.2-1'], version = '2.2-1', \
  191. maintainer = self.maintainer['maintainer'], \
  192. architecture = self.arch['i386'], \
  193. poolfile = self.file['gnome-hello_2.2-1_i386.deb'])
  194. self.binary['gnome-hello_2.2-1_i386'].suites.append(self.suite['squeeze'])
  195. self.binary['gnome-hello_2.2-1_i386'].suites.append(self.suite['sid'])
  196. self.binary['gnome-hello_3.0-1_i386'] = DBBinary(package = 'gnome-hello', \
  197. source = self.source['gnome-hello_3.0-1'], version = '3.0-1', \
  198. maintainer = self.maintainer['maintainer'], \
  199. architecture = self.arch['i386'], \
  200. poolfile = self.file['gnome-hello_3.0-1_i386.deb'])
  201. self.binary['gnome-hello_3.0-1_i386'].suites.append(self.suite['sid'])
  202. self.binary['python-hello_2.2-1_i386'] = DBBinary(package = 'python-hello', \
  203. source = self.source['hello_2.2-1'], version = '2.2-1', \
  204. maintainer = self.maintainer['maintainer'], \
  205. architecture = self.arch['all'], \
  206. poolfile = self.file['python-hello_2.2-1_all.deb'])
  207. self.binary['python-hello_2.2-1_i386'].suites.append(self.suite['squeeze'])
  208. self.session.add_all(self.binary.values())
  209. def setup_overridetypes(self):
  210. '''
  211. Setup self.otype of class OverrideType.
  212. '''
  213. if 'otype' in self.__dict__:
  214. return
  215. self.otype = {}
  216. for type_ in ('deb', 'udeb'):
  217. self.otype[type_] = OverrideType(overridetype = type_)
  218. self.session.add_all(self.otype.values())
  219. self.session.flush()
  220. def setup_sections(self):
  221. '''
  222. Setup self.section of class Section.
  223. '''
  224. if 'section' in self.__dict__:
  225. return
  226. self.section = {}
  227. self.section['python'] = Section(section = 'python')
  228. self.session.add_all(self.section.values())
  229. self.session.flush()
  230. def setup_priorities(self):
  231. '''
  232. Setup self.prio of class Priority.
  233. '''
  234. if 'prio' in self.__dict__:
  235. return
  236. self.prio = {}
  237. self.prio['standard'] = Priority(priority = 'standard', level = 7)
  238. self.session.add_all(self.prio.values())
  239. self.session.flush()
  240. def setup_overrides(self):
  241. '''
  242. Setup self.override of class Override.
  243. '''
  244. if 'override' in self.__dict__:
  245. return
  246. self.setup_suites()
  247. self.setup_components()
  248. self.setup_overridetypes()
  249. self.setup_sections()
  250. self.setup_priorities()
  251. self.override = {}
  252. self.override['hello_sid_main_udeb'] = Override(package = 'hello', \
  253. suite = self.suite['sid'], component = self.comp['main'], \
  254. overridetype = self.otype['udeb'], \
  255. section = self.section['python'], priority = self.prio['standard'])
  256. self.override['hello_squeeze_main_deb'] = Override(package = 'hello', \
  257. suite = self.suite['squeeze'], component = self.comp['main'], \
  258. overridetype = self.otype['deb'], \
  259. section = self.section['python'], priority = self.prio['standard'])
  260. self.override['hello_lenny_contrib_deb'] = Override(package = 'hello', \
  261. suite = self.suite['lenny'], component = self.comp['contrib'], \
  262. overridetype = self.otype['deb'], \
  263. section = self.section['python'], priority = self.prio['standard'])
  264. self.session.add_all(self.override.values())
  265. self.session.flush()
  266. def setUp(self):
  267. if self.metadata is None:
  268. self.initialize()
  269. self.session = DBConn().session()
  270. def now(self):
  271. """
  272. Returns the current time at the db server. Please note the function
  273. returns the same value as long as it is in the same transaction. You
  274. should self.session.rollback() (or commit) if you rely on getting a
  275. fresh timestamp.
  276. """
  277. return self.session.query(func.now()).scalar()
  278. def classes_to_clean(self):
  279. """
  280. The function classes_to_clean() returns a list of classes. All objects
  281. of each class will be deleted from the database in tearDown(). This
  282. function should be overridden in derived test cases as needed.
  283. """
  284. return ()
  285. def tearDown(self):
  286. self.session.rollback()
  287. for class_ in self.classes_to_clean():
  288. for object_ in self.session.query(class_):
  289. self.session.delete(object_)
  290. self.session.commit()
  291. # usually there is no need to drop all tables here
  292. #self.metadata.drop_all()