Coverage for app/models.py : 96%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from flask_login import UserMixin
2#from flask import current_app
3from werkzeug.security import generate_password_hash, check_password_hash
4from datetime import datetime
5from app import app, db, login_manager
6import uuid
7import re
8import json
9from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION
11#
12# Handling sclalchemy dialects
13#
14dialect = re.search(r'(?<=\()\w+', str(db.get_engine()).lower())
15# binary fields
16if dialect.group() == 'mysql':
17 from sqlalchemy.dialects.mysql import BINARY
18else:
19 BINARY = db.LargeBinary
20# double precision
21if dialect and dialect.group() == 'postgresql':
22 from sqlalchemy.dialects.postgresql import DOUBLE_PRECISION
23else:
24 DOUBLE_PRECISION = db.Float
27#
28# UUID as bytes
29#
30def uuidAsBytes():
31 return uuid.uuid4().bytes
33#
34# user Model
35# TODO: extend user Model
36#
37class User(UserMixin, db.Model):
38 __tablename__ = 'users'
39 id = db.Column(db.Integer, primary_key=True)
40 username = db.Column(db.String(64), unique=True, nullable=False)
41 password = db.Column(db.String(128), nullable=False)
42 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
43 lastlogin = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
45 def __str__(self):
46 return self.username
48 def set_password(self, password):
49 self.password = generate_password_hash(password)
51 def verify_password(self, password):
52 return check_password_hash(self.password, password)
54@login_manager.user_loader
55def load_user(id):
56 return User.query.get(int(id))
58#
59# relation tables
60#
62testrun_casesequence = db.Table(
63 'testrun_casesequence',
64 db.Column('testrun_id', BINARY(16), db.ForeignKey('testruns.id'), primary_key=True),
65 db.Column('testcase_sequence_id', BINARY(16), db.ForeignKey('testcase_sequences.id'), primary_key=True)
66)
68testcase_sequence_datafile = db.Table(
69 'testcase_sequence_datafile',
70 db.Column('testcase_sequence_id', BINARY(16), db.ForeignKey('testcase_sequences.id'), primary_key=True),
71 db.Column('datafile_id', BINARY(16), db.ForeignKey('datafiles.id'), primary_key=True)
72)
74testcase_sequence_case = db.Table(
75 'testcase_sequence_case',
76 db.Column('testcase_sequence_id', BINARY(16), db.ForeignKey('testcase_sequences.id'), primary_key=True),
77 db.Column('testcase_id', BINARY(16), db.ForeignKey('testcases.id'), primary_key=True)
78)
80testcase_stepsequence = db.Table(
81 'testcase_stepsequence',
82 db.Column('testcase_id', BINARY(16), db.ForeignKey('testcases.id'), primary_key=True),
83 db.Column('teststep_sequence_id', BINARY(16), db.ForeignKey('teststep_sequences.id'), primary_key=True)
84)
87#
88# main entities
89#
90class Testrun(db.Model):
91 #
92 # Testrun object
93 #
95 __tablename__ = 'testruns'
97 # fields
98 id = db.Column(BINARY(16), primary_key=True, default=uuidAsBytes)
99 name = db.Column(db.String(64), nullable=False)
100 description = db.Column(db.String(512), nullable=False)
101 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
102 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
103 edited = db.Column(db.DateTime, nullable=True)
104 editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
106 # globals
107 global_settings = db.Column(db.String(2048), nullable=True)
109 # realshinships
110 creator = db.relationship(
111 'User',
112 backref=db.backref('created_testruns', order_by='desc(Testrun.created)'),
113 lazy='select',
114 foreign_keys=[creator_id],
115 )
116 editor = db.relationship('User', backref='edited_testruns', lazy='select', foreign_keys=[editor_id])
118 @property
119 def uuid(self):
120 return str(uuid.UUID(bytes=self.id))
122 def __str__(self):
123 return self.name
125 def __repr__(self):
126 return f'<Testrun: {self.uuid}>'
128 def finished_calls(self, stage=None):
129 if stage == 'None':
130 return [call for call in self.calls if (call.is_finished and call.stage == None)]
131 if stage:
132 return [call for call in self.calls if (call.is_finished and call.stage == stage)]
134 return [call for call in self.calls if call.is_finished]
137 def to_json(self):
138 #
139 # returns item as JSON
140 #
142 return {
143 'name': self.name,
144 'testrun': {
145 'GC.KWARGS_TESTRUNATTRIBUTES': {
146 'GC.EXPORT_FORMAT': {
147 'GC.EXPORT_FORMAT': None,
148 'GC.EXP_FIELDLIST': [],
149 },
150 'GC.STRUCTURE_TESTCASESEQUENCE': {
151 i+1: self.testcase_sequences[i].to_json(i+1) for i in range(len(self.testcase_sequences))
152 },
153 }
154 },
155 'globals' : json.loads(self.global_settings) if self.global_settings else None,
156 }
158 def get_status(self):
159 #
160 # check for item completeness
161 # returns tuple: (status, message)
162 #
164 # check for TestCaseSequences
165 if len(self.testcase_sequences) == 0:
166 return False, f'Testrun {self} does not contain TestCaseSequences'
168 # iterate TestCaseSequences
169 for items in self.testcase_sequences:
170 status, message = items.get_status()
171 if not status:
172 return status, message
174 return True, 'OK'
177class TestCaseSequence(db.Model):
178 #
179 # TestCse Sequence object
180 #
182 __tablename__ = 'testcase_sequences'
184 # fields
185 id = db.Column(BINARY(16), primary_key=True, default=uuidAsBytes)
186 name = db.Column(db.String(64), nullable=False)
187 description = db.Column(db.String(512), nullable=False)
188 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
189 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
190 edited = db.Column(db.DateTime, nullable=True)
191 editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
192 classname_id = db.Column(db.Integer, db.ForeignKey('classnames.id'), nullable=False)
194 # relashionships
195 creator = db.relationship('User', backref='created_testcase_sequences', lazy='select', foreign_keys=[creator_id])
196 editor = db.relationship('User', backref='edited_testcase_sequences', lazy='select', foreign_keys=[editor_id])
197 classname = db.relationship('ClassName', backref='testcase_sequences', lazy='select', foreign_keys=[classname_id])
198 testrun = db.relationship(
199 'Testrun',
200 secondary=testrun_casesequence,
201 lazy='select',
202 backref=db.backref('testcase_sequences', order_by='TestCaseSequence.created', lazy=True),
203 )
205 @property
206 def uuid(self):
207 return str(uuid.UUID(bytes=self.id))
209 def __str__(self):
210 return self.name
212 def __repr__(self):
213 return f'<TestCaseSequence: {self.uuid}>'
215 def to_json(self, TestCaseSequenceNumber=1):
216 #
217 # returns item as JSON
218 #
220 # get name of DataFile
221 if len(self.datafiles) > 0:
222 TestDataFileName = self.datafiles[0].uuid
223 TestDataSheetName = self.datafiles[0].sheet
224 else:
225 TestDataFileName = TestDataSheetName = None
226 return [
227 self.classname.name,
228 {
229 'SequenceClass': self.classname.name,
230 'GC.DATABASE_FILENAME': TestDataFileName,
231 'GC.DATABASE_SHEETNAME': TestDataSheetName,
232 'GC.EXECUTION_PARALLEL': 1,
233 'GC.DATABASE_LINES': '0-999999',
234 'GC.STRUCTURE_TESTCASE': {
235 i+1: self.testcases[i].to_json(
236 TestCaseSequenceNumber,
237 i+1,
238 ) for i in range(len(self.testcases))
239 },
240 }
241 ]
243 def get_status(self):
244 #
245 # check for item completeness
246 # returns tuple: (status, message)
247 #
249 # check for DataFiles
250 if len(self.datafiles) == 0:
251 return False, f'TestCaseSequence {self} does not contain DataFiles'
253 # check for TestCases
254 if len(self.testcases) == 0:
255 return False, f'TestCaseSequence {self} does not contain TestCases'
257 # iterate TestCases
258 for items in self.testcases:
259 status, message = items.get_status()
260 if not status:
261 return status, message
263 return True, 'OK'
266class DataFile(db.Model):
267 #
268 # DataFile object
269 #
271 __tablename__ = 'datafiles'
273 # fields
274 id = db.Column(BINARY(16), primary_key=True, default=uuidAsBytes)
275 filename = db.Column(db.String(64), nullable=False)
276 sheet = db.Column(db.String(32), nullable=False, default='data')
277 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
278 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
280 # relationships
281 creator = db.relationship('User', backref='created_datafiles', lazy='select', foreign_keys=[creator_id])
282 testcase_sequence = db.relationship(
283 'TestCaseSequence',
284 secondary=testcase_sequence_datafile,
285 lazy='select',
286 backref=db.backref('datafiles', lazy=True),
287 )
289 @property
290 def uuid(self):
291 return str(uuid.UUID(bytes=self.id))
293 @property
294 def url(self):
295 return '/'.join((
296 'http:/',
297 app.config.get('BAANGT_DATAFILE_HOST'),
298 app.config.get('BAANGT_DATAFILE_GET'),
299 self.uuid,
300 ))
303 def __str__(self):
304 return self.filename
306 def __repr__(self):
307 return f'<DataFile: {self.uuid}>'
310class TestCase(db.Model):
311 #
312 # testCase object
313 #
315 __tablename__ = 'testcases'
317 # fields
318 id = db.Column(BINARY(16), primary_key=True, default=uuidAsBytes)
319 name = db.Column(db.String(64), nullable=False)
320 description = db.Column(db.String(512), nullable=False)
321 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
322 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
323 edited = db.Column(db.DateTime, nullable=True)
324 editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
325 classname_id = db.Column(db.Integer, db.ForeignKey('classnames.id'), nullable=False)
326 browser_type_id = db.Column(db.Integer, db.ForeignKey('browser_types.id'), nullable=False)
327 testcase_type_id = db.Column(db.Integer, db.ForeignKey('testcase_types.id'), nullable=False)
329 # realshionshipd
330 creator = db.relationship('User', backref='created_testcases', lazy='select', foreign_keys=[creator_id])
331 editor = db.relationship('User', backref='edited_testcases', lazy='select', foreign_keys=[editor_id])
332 classname = db.relationship('ClassName', backref='testcases', lazy='select', foreign_keys=[classname_id])
333 browser_type = db.relationship('BrowserType', backref='testcases', lazy='select', foreign_keys=[browser_type_id])
334 testcase_type = db.relationship('TestCaseType', backref='testcases', lazy='select', foreign_keys=[testcase_type_id])
335 testcase_sequence = db.relationship(
336 'TestCaseSequence',
337 secondary=testcase_sequence_case,
338 lazy='select',
339 backref=db.backref('testcases', order_by='TestCase.created', lazy=True),
340 )
342 @property
343 def uuid(self):
344 return str(uuid.UUID(bytes=self.id))
346 def __str__(self):
347 return self.name
349 def __repr__(self):
350 return f'<TestCase: {self.uuid}>'
352 def to_json(self, TestCaseSequenceNumber=1, TestCaseNumber=1):
353 #
354 # returns item as JSON
355 #
357 return [
358 self.classname.name,
359 {
360 'GC.KWARGS_TESTCASETYPE': self.testcase_type.name,
361 'GC.KWARGS_BROWSER': self.browser_type.name,
362 'GC.BROWSER_ATTRIBUTES': '',
363 },
364 {
365 'GC.STRUCTURE_TESTSTEP': {
366 i+1: self.teststep_sequences[i].to_json(
367 TestCaseSequenceNumber,
368 TestCaseNumber,
369 i+1,
370 ) for i in range(len(self.teststep_sequences))
371 },
372 }
373 ]
375 def get_status(self):
376 #
377 # check for item completeness
378 # returns tuple: (status, message)
379 #
381 # check for TestStepSequences
382 if len(self.teststep_sequences) == 0:
383 return False, f'TestCase {self} does not contain TestStepSequences'
385 # iterate TestStepSequence
386 for items in self.teststep_sequences:
387 status, message = items.get_status()
388 if not status:
389 return status, message
391 return True, 'OK'
394class TestStepSequence(db.Model):
395 #
396 # TestStep Sequence object
397 #
399 __tablename__ = 'teststep_sequences'
401 # fields
402 id = db.Column(BINARY(16), primary_key=True, default=uuidAsBytes)
403 name = db.Column(db.String(64), nullable=False)
404 description = db.Column(db.String(512), nullable=False)
405 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
406 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
407 edited = db.Column(db.DateTime, nullable=True)
408 editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
409 classname_id = db.Column(db.Integer, db.ForeignKey('classnames.id'), nullable=False)
411 # relashionship
412 creator = db.relationship('User', backref='created_teststep_sequences', lazy='select', foreign_keys=[creator_id])
413 editor = db.relationship('User', backref='edited_teststep_sequences', lazy='select', foreign_keys=[editor_id])
414 classname = db.relationship('ClassName', backref='teststep_sequences', lazy='select', foreign_keys=[classname_id])
415 testcase = db.relationship(
416 'TestCase',
417 secondary=testcase_stepsequence,
418 lazy='select',
419 backref=db.backref('teststep_sequences', order_by='TestStepSequence.created', lazy=True),
420 )
422 @property
423 def uuid(self):
424 return str(uuid.UUID(bytes=self.id))
426 def __str__(self):
427 return self.name
429 def __repr__(self):
430 return f'<TestStepSequence: {self.uuid}>'
432 def to_json(self, TestCaseSequenceNumber=1, TestCaseNumber=1, TestStepNumber=1):
433 #
434 # returns item as JSON
435 #
437 return [
438 {
439 'TestStepClass': self.classname.name,
440 },
441 {
442 'GC.STRUCTURE_TESTSTEPEXECUTION': {
443 i+1: self.teststeps[i].to_json(
444 TestCaseSequenceNumber,
445 TestCaseNumber,
446 TestStepNumber,
447 i+1,
448 ) for i in range(len(self.teststeps))
449 }
450 },
451 ]
453 def get_status(self):
454 #
455 # check for item completeness
456 # returns tuple: (status, message)
457 #
459 # check for TestSteps
460 if len(self.teststeps) == 0:
461 return False, f'TestStepSequence {self} does not contain TestSteps'
463 return True, 'OK'
466class TestStepExecution(db.Model):
467 #
468 # TestStep object
469 #
470 __tablename__ = 'teststep_executions'
472 # fields
473 id = db.Column(BINARY(16), primary_key=True, default=uuidAsBytes)
474 name = db.Column(db.String(64), nullable=False)
475 description = db.Column(db.String(512), nullable=False)
476 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
477 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
478 edited = db.Column(db.DateTime, nullable=True)
479 locator = db.Column(db.String(512), nullable=True)
480 optional = db.Column(db.Boolean, nullable=False, default=False)
481 # sa dialect
482 timeout = db.Column(DOUBLE_PRECISION(precision='5,2'), nullable=True)
483 release = db.Column(db.String(64), nullable=True)
484 value = db.Column(db.String(1024), nullable=True)
485 value2 = db.Column(db.String(1024), nullable=True)
486 comparison = db.Column(db.String(16), nullable=True)
487 editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
488 activity_type_id = db.Column(db.Integer, db.ForeignKey('activity_types.id'), nullable=False)
489 locator_type_id = db.Column(db.Integer, db.ForeignKey('locator_types.id'), nullable=True)
490 teststep_sequence_id = db.Column(BINARY(16), db.ForeignKey('teststep_sequences.id'), nullable=True)
492 # relashionships
493 creator = db.relationship('User', backref='created_teststeps', lazy='select', foreign_keys=[creator_id])
494 editor = db.relationship('User', backref='edited_teststeps', lazy='select', foreign_keys=[editor_id])
495 activity_type = db.relationship('ActivityType', backref='teststeps', lazy='select', foreign_keys=[activity_type_id])
496 locator_type = db.relationship('LocatorType', backref='teststeps', lazy='select', foreign_keys=[locator_type_id])
497 teststep_sequence = db.relationship(
498 'TestStepSequence',
499 backref=db.backref('teststeps', order_by='TestStepExecution.created'),
500 lazy='select',
501 foreign_keys=[teststep_sequence_id],
502 )
504 @property
505 def uuid(self):
506 return str(uuid.UUID(bytes=self.id))
508 def __str__(self):
509 return self.name
511 def __repr__(self):
512 return f'<TestStepExecution: {self.uuid}>'
514 def to_json(self, TestCaseSequenceNumber=1, TestCaseNumber=1, TestStepNumber=1, TestStepExecutionNumber=1):
515 #
516 # returns item as JSON
517 #
519 # set LocatorType name
520 #locator_type = ''
521 #if self.locator_type:
522 # locator_type = self.locator_type.name
524 return {
525 'Activity': self.activity_type.name,
526 'LocatorType': self.locator_type.name if self.locator_type else '',
527 'Locator': self.locator or '',
528 'Value': self.value or '',
529 'Comparison': self.comparison or '',
530 'Value2': self.value2 or '',
531 'Timeout': self.timeout or '',
532 'Optional': self.optional or '',
533 'Release': self.release or '',
534 }
536#
537# supporting entities
538#
539class GlobalTestStepExecution(db.Model):
540 __tablename__ = 'global_teststep_executions'
541 id = db.Column(BINARY(16), primary_key=True, default=uuidAsBytes)
542 name = db.Column(db.String(64), nullable=False)
543 description = db.Column(db.String(512), nullable=False)
544 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
545 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
546 edited = db.Column(db.DateTime, nullable=True)
547 editor_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)
548 activity_type_id = db.Column(db.Integer, db.ForeignKey('activity_types.id'), nullable=False)
549 locator_type_id = db.Column(db.Integer, db.ForeignKey('locator_types.id'), nullable=False)
550 teststep_sequence_id = db.Column(BINARY(16), db.ForeignKey('teststep_sequences.id'), nullable=True)
552 # relationships
553 creator = db.relationship('User', backref='created_global_teststeps', lazy='select', foreign_keys=[creator_id])
554 editor = db.relationship('User', backref='edited_global_teststeps', lazy='select', foreign_keys=[editor_id])
555 activity_type = db.relationship('ActivityType', backref='global_teststeps', lazy='select', foreign_keys=[activity_type_id])
556 locator_type = db.relationship('LocatorType', backref='global_teststeps', lazy='select', foreign_keys=[locator_type_id])
557 teststep_sequence = db.relationship('TestStepSequence', backref='global_teststeps', lazy='select', foreign_keys=[teststep_sequence_id])
559 @property
560 def uuid(self):
561 return str(uuid.UUID(bytes=self.id))
563 def __str__(self):
564 return self.name
567class ClassName(db.Model):
568 __tablename__ = 'classnames'
569 id = db.Column(db.Integer, primary_key=True)
570 name = db.Column(db.String(64), nullable=False)
571 description = db.Column(db.String(512), nullable=False)
573 def __str__(self):
574 return self.name
576class BrowserType(db.Model):
577 __tablename__ = 'browser_types'
578 id = db.Column(db.Integer, primary_key=True)
579 name = db.Column(db.String(64), nullable=False)
580 description = db.Column(db.String(512), nullable=False)
582 def __str__(self):
583 return self.name
585class TestCaseType(db.Model):
586 __tablename__ = 'testcase_types'
587 id = db.Column(db.Integer, primary_key=True)
588 name = db.Column(db.String(64), nullable=False)
589 description = db.Column(db.String(512), nullable=False)
591 def __str__(self):
592 return self.name
594class ActivityType(db.Model):
595 __tablename__ = 'activity_types'
596 id = db.Column(db.Integer, primary_key=True)
597 name = db.Column(db.String(64), nullable=False)
598 description = db.Column(db.String(512), nullable=False)
600 def __str__(self):
601 return self.name
603class LocatorType(db.Model):
604 __tablename__ = 'locator_types'
605 id = db.Column(db.Integer, primary_key=True)
606 name = db.Column(db.String(64), nullable=False)
607 description = db.Column(db.String(512), nullable=False)
609 def __str__(self):
610 return self.name
613#
614# Testrun results
615#
617class TestRunCall(db.Model):
618 __tablename__ = 'testrun_calls'
619 id = db.Column(BINARY(16), primary_key=True)
620 created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
621 creator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
622 testrun_id = db.Column(BINARY(16), db.ForeignKey('testruns.id'), nullable=False)
624 # relationships
625 creator = db.relationship('User', backref='testruns_called', lazy='select', foreign_keys=[creator_id])
626 testrun = db.relationship(
627 'Testrun',
628 backref=db.backref('calls', order_by='TestRunCall.created'),
629 lazy='select',
630 foreign_keys=[testrun_id],
631 )
633 # Testrun call faild
634 is_failed = db.Column(db.Boolean, nullable=False, default=False)
635 error_message = db.Column(db.String(512), nullable=True)
637 # summary
638 is_finished = db.Column(db.Boolean, nullable=False, default=False)
639 testrecords = db.Column(db.Integer, nullable=True)
640 successful = db.Column(db.Integer, nullable=True)
641 error = db.Column(db.Integer, nullable=True)
642 paused = db.Column(db.Integer, nullable=True)
643 duration = db.Column(db.Integer, nullable=True)
644 stage = db.Column(db.String(32), nullable=True)
646 def __str__(self):
647 return str(uuid.UUID(bytes=self.id))