-
Notifications
You must be signed in to change notification settings - Fork 181
Expand file tree
/
Copy pathassertion.py
More file actions
490 lines (415 loc) · 23.4 KB
/
assertion.py
File metadata and controls
490 lines (415 loc) · 23.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
# Copyright (c) 2010 Franz Allan Valencia See
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Any, Optional, Tuple
from assertionengine import AssertionOperator, verify_assertion
from robot.api import logger
from robot.libraries.BuiltIn import BuiltIn
from robot.utils import timestr_to_secs
from .params_decorator import renamed_args
class Assertion:
"""
Assertion handles all the assertions of Database Library.
"""
def check_if_exists_in_database(
self,
select_statement: str,
*,
no_transaction: bool = False,
msg: Optional[str] = None,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.
Check if any row would be returned by given the input ``select_statement``. If there are no results, then this will
throw an AssertionError.
Set optional input ``no_transaction`` to _True_ to run command without an explicit transaction
commit or rollback.
The default error message can be overridden with the ``msg`` argument.
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Examples:
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' |
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | msg=my error message |
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | alias=my_alias |
| Check If Exists In Database | SELECT id FROM person WHERE first_name = 'John' | no_transaction=True |
| @{parameters} | Create List | John |
| Check If Exists In Database | SELECT id FROM person WHERE first_name = %s | parameters=${parameters} |
"""
if not self.query(select_statement, no_transaction, alias=alias, parameters=parameters):
raise AssertionError(
msg or f"Expected to have have at least one row, but got 0 rows from: '{select_statement}'"
)
def check_if_not_exists_in_database(
self,
selectStatement: str,
sansTran: bool = False,
msg: Optional[str] = None,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.
This is the negation of `check_if_exists_in_database`.
Check if no rows would be returned by given the input ``selectStatement``. If there are any results, then this
will throw an AssertionError.
Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.
The default error message can be overridden with the ``msg`` argument.
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Examples:
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' |
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | msg=my error message |
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'Franz Allan' | alias=my_alias |
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = 'John' | sansTran=True |
| @{parameters} | Create List | John |
| Check If Not Exists In Database | SELECT id FROM person WHERE first_name = %s | parameters=${parameters} |
"""
query_results = self.query(selectStatement, sansTran, alias=alias, parameters=parameters)
if query_results:
raise AssertionError(
msg or f"Expected to have have no rows from '{selectStatement}', but got some rows: {query_results}"
)
def row_count_is_0(
self,
selectStatement: str,
sansTran: bool = False,
msg: Optional[str] = None,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.
Check if any rows are returned from the submitted ``selectStatement``. If there are, then this will throw an
AssertionError.
Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or
rollback.
The default error message can be overridden with the ``msg`` argument.
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Examples:
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' |
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'Franz Allan' | msg=my error message |
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | alias=my_alias |
| Row Count is 0 | SELECT id FROM person WHERE first_name = 'John' | sansTran=True |
| @{parameters} | Create List | John |
| Row Count is 0 | SELECT id FROM person WHERE first_name = %s | parameters=${parameters} |
"""
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
if num_rows > 0:
raise AssertionError(msg or f"Expected 0 rows, but {num_rows} were returned from: '{selectStatement}'")
def row_count_is_equal_to_x(
self,
selectStatement: str,
numRows: str,
sansTran: bool = False,
msg: Optional[str] = None,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.
Check if the number of rows returned from ``selectStatement`` is equal to the value submitted. If not, then this
will throw an AssertionError.
Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.
The default error message can be overridden with the ``msg`` argument.
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Examples:
| Row Count Is Equal To X | SELECT id FROM person | 1 |
| Row Count Is Equal To X | SELECT id FROM person | 3 | msg=my error message |
| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | alias=my_alias |
| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = 'John' | 0 | sansTran=True |
| @{parameters} | Create List | John |
| Row Count Is Equal To X | SELECT id FROM person WHERE first_name = %s | 0 | parameters=${parameters} |
"""
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
if num_rows != int(numRows.encode("ascii")):
raise AssertionError(
msg or f"Expected {numRows} rows, but {num_rows} were returned from: '{selectStatement}'"
)
def row_count_is_greater_than_x(
self,
selectStatement: str,
numRows: str,
sansTran: bool = False,
msg: Optional[str] = None,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.
Check if the number of rows returned from ``selectStatement`` is greater than the value submitted. If not, then
this will throw an AssertionError.
Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.
The default error message can be overridden with the ``msg`` argument.
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Examples:
| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 |
| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 | msg=my error message |
| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = 'John' | 0 | alias=my_alias |
| Row Count Is Greater Than X | SELECT id FROM person | 1 | sansTran=True |
| @{parameters} | Create List | John |
| Row Count Is Greater Than X | SELECT id FROM person WHERE first_name = %s | 0 | parameters=${parameters} |
"""
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
if num_rows <= int(numRows.encode("ascii")):
raise AssertionError(
msg or f"Expected more than {numRows} rows, but {num_rows} were returned from '{selectStatement}'"
)
def row_count_is_less_than_x(
self,
selectStatement: str,
numRows: str,
sansTran: bool = False,
msg: Optional[str] = None,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
):
"""
*DEPRECATED* Use new `Check Row Count` keyword with assertion engine instead.
The deprecated keyword will be removed in future versions.
Check if the number of rows returned from ``selectStatement`` is less than the value submitted. If not, then this
will throw an AssertionError.
Set optional input ``sansTran`` to _True_ to run command without an explicit transaction commit or rollback.
Using optional ``msg`` to override the default error message:
Use optional ``alias`` parameter to specify what connection should be used for the query if you have more
than one connection open.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Examples:
| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 1 |
| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 2 | msg=my error message |
| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 3 | alias=my_alias |
| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = 'John' | 4 | sansTran=True |
| @{parameters} | Create List | John |
| Row Count Is Less Than X | SELECT id FROM person WHERE first_name = %s | 5 | parameters=${parameters} |
"""
num_rows = self.row_count(selectStatement, sansTran, alias=alias, parameters=parameters)
if num_rows >= int(numRows.encode("ascii")):
raise AssertionError(
msg or f"Expected less than {numRows} rows, but {num_rows} were returned from '{selectStatement}'"
)
@renamed_args(mapping={"selectStatement": "select_statement", "sansTran": "no_transaction"})
def check_row_count(
self,
select_statement: str,
assertion_operator: AssertionOperator,
expected_value: int,
assertion_message: Optional[str] = None,
no_transaction: bool = False,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
retry_timeout="0 seconds",
retry_pause="0.5 seconds",
*,
replace_robot_variables=False,
selectStatement: Optional[str] = None,
sansTran: Optional[bool] = None,
):
"""
Check the number of rows returned from ``select_statement`` using ``assertion_operator``
and ``expected_value``. See `Inline assertions` for more details.
Use ``assertion_message`` to override the default error message.
Set ``no_transaction`` to _True_ to run command without explicit transaction rollback in case of error.
See `Commit behavior` for details.
Use ``alias`` to specify what connection should be used if `Handling multiple database connections`.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Use ``retry_timeout`` and ``retry_pause`` parameters to enable waiting for assertion to pass.
See `Retry mechanism` for more details.
Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.
=== Some parameters were renamed in version 2.0 ===
The old parameters ``selectStatement`` and ``sansTran`` are *deprecated*,
please use new parameters ``select_statement`` and ``no_transaction`` instead.
*The old parameters will be removed in future versions.*
=== Examples ===
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *==* | 1 |
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *>=* | 2 | assertion_message=my error message |
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *inequal* | 3 | alias=my_alias |
| Check Row Count | SELECT id FROM person WHERE first_name = 'John' | *less than* | 4 | no_transaction=True |
| @{parameters} | Create List | John |
| Check Row Count | SELECT id FROM person WHERE first_name = %s | *equals* | 5 | parameters=${parameters} |
"""
check_ok = False
time_counter = 0
while not check_ok:
try:
num_rows = self.row_count(
select_statement,
no_transaction=no_transaction,
alias=alias,
parameters=parameters,
replace_robot_variables=replace_robot_variables,
)
verify_assertion(num_rows, assertion_operator, expected_value, "Wrong row count:", assertion_message)
check_ok = True
except AssertionError as e:
if time_counter >= timestr_to_secs(retry_timeout):
logger.info(f"Timeout '{retry_timeout}' reached")
raise e
BuiltIn().sleep(retry_pause)
time_counter += timestr_to_secs(retry_pause)
@renamed_args(mapping={"selectStatement": "select_statement", "sansTran": "no_transaction"})
def check_query_result(
self,
select_statement: str,
assertion_operator: AssertionOperator,
expected_value: Any,
row=0,
col=0,
assertion_message: Optional[str] = None,
no_transaction: bool = False,
alias: Optional[str] = None,
parameters: Optional[Tuple] = None,
retry_timeout="0 seconds",
retry_pause="0.5 seconds",
*,
replace_robot_variables=False,
selectStatement: Optional[str] = None,
sansTran: Optional[bool] = None,
):
"""
Check value in query result returned from ``select_statement`` using ``assertion_operator`` and ``expected_value``.
The value position in results can be adjusted using ``row`` and ``col`` parameters (0-based).
See `Inline assertions` for more details.
*The assertion in this keyword is type sensitive!*
The ``expected_value`` is taken as a string, no argument conversion is performed.
Use RF syntax like ``${1}`` for numeric values.
Use optional ``assertion_message`` to override the default error message.
Set ``no_transaction`` to _True_ to run command without explicit transaction rollback in case of error.
See `Commit behavior` for details.
Use ``alias`` to specify what connection should be used if `Handling multiple database connections`.
Use ``parameters`` for query variable substitution (variable substitution syntax may be different
depending on the database client).
Use ``retry_timeout`` and ``retry_pause`` parameters to enable waiting for assertion to pass.
See `Retry mechanism` for more details.
Set ``replace_robot_variables`` to resolve RF variables (like ${MY_VAR}) before executing the SQL.
=== Some parameters were renamed in version 2.0 ===
The old parameters ``selectStatement`` and ``sansTran`` are *deprecated*,
please use new parameters ``select_statement`` and ``no_transaction`` instead.
*The old parameters will be removed in future versions.*
=== Examples ===
| Check Query Result | SELECT first_name FROM person | *contains* | Allan |
| Check Query Result | SELECT first_name, last_name FROM person | *==* | Schneider | row=1 | col=1 |
| Check Query Result | SELECT id FROM person WHERE first_name = 'John' | *==* | 2 | # Fails, if query returns an integer value |
| Check Query Result | SELECT id FROM person WHERE first_name = 'John' | *==* | ${2} | # Works, if query returns an integer value |
| Check Query Result | SELECT first_name FROM person | *equal* | Franz Allan | assertion_message=my error message |
| Check Query Result | SELECT first_name FROM person | *inequal* | John | alias=my_alias |
| Check Query Result | SELECT first_name FROM person | *contains* | Allan | no_transaction=True |
| @{parameters} | Create List | John |
| Check Query Result | SELECT first_name FROM person | *contains* | Allan | parameters=${parameters} |
"""
check_ok = False
time_counter = 0
while not check_ok:
try:
query_results = self.query(
select_statement,
no_transaction=no_transaction,
alias=alias,
parameters=parameters,
replace_robot_variables=replace_robot_variables,
)
row_count = len(query_results)
assert (
row < row_count
), f"Checking row '{row}' is not possible, as query results contain {row_count} rows only!"
col_count = len(query_results[row])
assert (
col < col_count
), f"Checking column '{col}' is not possible, as query results contain {col_count} columns only!"
actual_value = query_results[row][col]
verify_assertion(
actual_value, assertion_operator, expected_value, "Wrong query result:", assertion_message
)
check_ok = True
except AssertionError as e:
if time_counter >= timestr_to_secs(retry_timeout):
logger.info(f"Timeout '{retry_timeout}' reached")
raise e
BuiltIn().sleep(retry_pause)
time_counter += timestr_to_secs(retry_pause)
@renamed_args(mapping={"tableName": "table_name", "sansTran": "no_transaction"})
def table_must_exist(
self,
table_name: str,
no_transaction: bool = False,
msg: Optional[str] = None,
alias: Optional[str] = None,
*,
tableName: Optional[str] = None,
sansTran: Optional[bool] = None,
):
"""
Check if the table with `table_name` exists in the database.
Use ``msg`` for custom error message.
Set ``no_transaction`` to _True_ to run command without explicit transaction rollback in case of error.
See `Commit behavior` for details.
Use ``alias`` to specify what connection should be used if `Handling multiple database connections`.
=== Some parameters were renamed in version 2.0 ===
The old parameters ``tableName`` and ``sansTran`` are *deprecated*,
please use new parameters ``table_name`` and ``no_transaction`` instead.
*The old parameters will be removed in future versions.*
=== Examples ===
| Table Must Exist | person |
| Table Must Exist | person | msg=my error message |
| Table Must Exist | person | alias=my_alias |
| Table Must Exist | person | no_transaction=True |
"""
db_connection = self.connection_store.get_connection(alias)
if db_connection.module_name in ["cx_Oracle", "oracledb"]:
query = (
"SELECT * FROM all_objects WHERE object_type IN ('TABLE','VIEW') AND "
f"owner = SYS_CONTEXT('USERENV', 'SESSION_USER') AND object_name = UPPER('{table_name}')"
)
table_exists = self.row_count(query, no_transaction=no_transaction, alias=alias) > 0
elif db_connection.module_name in ["sqlite3"]:
query = f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}' COLLATE NOCASE"
table_exists = self.row_count(query, no_transaction=no_transaction, alias=alias) > 0
elif db_connection.module_name in ["ibm_db", "ibm_db_dbi"]:
query = f"SELECT name FROM SYSIBM.SYSTABLES WHERE type='T' AND name=UPPER('{table_name}')"
table_exists = self.row_count(query, no_transaction=no_transaction, alias=alias) > 0
elif db_connection.module_name in ["teradata"]:
query = f"SELECT TableName FROM DBC.TablesV WHERE TableKind='T' AND TableName='{table_name}'"
table_exists = self.row_count(query, no_transaction=no_transaction, alias=alias) > 0
else:
try:
query = f"SELECT * FROM information_schema.tables WHERE table_name='{table_name}'"
table_exists = self.row_count(query, no_transaction=no_transaction, alias=alias) > 0
except:
logger.info("Database doesn't support information schema, try using a simple SQL request")
try:
query = f"SELECT 1 from {table_name} where 1=0"
self.row_count(query, no_transaction=no_transaction, alias=alias)
table_exists = True
except:
table_exists = False
assert table_exists, msg or f"Table '{table_name}' does not exist in the db"