def test_operator_validator(setup_database): dbsession = setup_database # Test passing SQLObserver with empty SQL result alert1 = create_alert(dbsession, "SELECT first FROM test_table WHERE first = -1") observe(alert1.id, dbsession) assert (operator_validator(alert1.sql_observer[0], '{"op": ">=", "threshold": 60}') is False) # Test passing SQLObserver with result that doesn't pass a greater than threshold alert2 = create_alert(dbsession, "SELECT 55") observe(alert2.id, dbsession) assert (operator_validator(alert2.sql_observer[0], '{"op": ">=", "threshold": 60}') is False) # Test passing SQLObserver with result that passes a greater than threshold assert (operator_validator(alert2.sql_observer[0], '{"op": ">=", "threshold": 40}') is True) # Test passing SQLObserver with result that doesn't pass a less than threshold assert (operator_validator(alert2.sql_observer[0], '{"op": "<=", "threshold": 40}') is False) # Test passing SQLObserver with result that passes threshold assert (operator_validator(alert2.sql_observer[0], '{"op": "<=", "threshold": 60}') is True) # Test passing SQLObserver with result that doesn't equal threshold assert (operator_validator(alert2.sql_observer[0], '{"op": "==", "threshold": 60}') is False) # Test passing SQLObserver with result that equals threshold assert (operator_validator(alert2.sql_observer[0], '{"op": "==", "threshold": 55}') is True)
def test_deliver_alert_screenshot(screenshot_mock, url_mock, email_mock, file_upload_mock, setup_database): dbsession = setup_database alert = create_alert(dbsession, "SELECT 55", "not null", "{}") observe(alert.id, dbsession) screenshot = read_fixture("sample.png") screenshot_mock.return_value = screenshot # TODO: fix AlertModelView.show url call from test url_mock.side_effect = [ f"http://0.0.0.0:8080/alert/show/{alert.id}", f"http://0.0.0.0:8080/superset/slice/{alert.slice_id}/", ] deliver_alert(alert.id, dbsession) assert email_mock.call_args[1]["images"]["screenshot"] == screenshot assert file_upload_mock.call_args[1] == { "channels": alert.slack_channel, "file": screenshot, "initial_comment": f"\n*Triggered Alert: {alert.label} :redalert:*\n" f"*Query*:```{alert.sql_observer[0].sql}```\n" f"*Result*: {alert.observations[-1].value}\n" f"*Reason*: {alert.observations[-1].value} {alert.validators[0].pretty_print()}\n" f"<http://0.0.0.0:8080/alert/show/{alert.id}" f"|View Alert Details>\n<http://0.0.0.0:8080/superset/slice/{alert.slice_id}/" "|*Explore in Superset*>", "title": f"[Alert] {alert.label}", }
def test_operator_validator(setup_database): dbsession = setup_database # Test passing with empty SQL result alert1 = create_alert(dbsession, "SELECT first FROM test_table WHERE first = -1") observe(alert1.id, dbsession) assert operator_validator(alert1, '{"op": ">=", "threshold": 60}') is False # ensure that 0 threshold works assert operator_validator(alert1, '{"op": ">=", "threshold": 0}') is False # Test passing with result that doesn't pass a greater than threshold alert2 = create_alert(dbsession, "SELECT 55") observe(alert2.id, dbsession) assert operator_validator(alert2, '{"op": ">=", "threshold": 60}') is False # Test passing with result that passes a greater than threshold assert operator_validator(alert2, '{"op": ">=", "threshold": 40}') is True # Test passing with result that doesn't pass a less than threshold assert operator_validator(alert2, '{"op": "<=", "threshold": 40}') is False # Test passing with result that passes threshold assert operator_validator(alert2, '{"op": "<=", "threshold": 60}') is True # Test passing with result that doesn't equal threshold assert operator_validator(alert2, '{"op": "==", "threshold": 60}') is False # Test passing with result that equals threshold assert operator_validator(alert2, '{"op": "==", "threshold": 55}') is True # Test passing with result that equals decimal threshold assert operator_validator(alert2, '{"op": ">", "threshold": 54.999}') is True
def test_alert_observer_error_msg(setup_database, description, query): logger.info(description) db_session = setup_database alert = create_alert(db_session, query) observe(alert.id, db_session) assert alert.observations[-1].value is None assert alert.observations[-1].error_msg is not None
def test_validate_observations_with_observe(setup_database, description, query, validator_type, config, expected): db_session = setup_database logger.info(description) alert = create_alert(db_session, query, validator_type, config) observe(alert.id, db_session) assert validate_observations(alert.id, alert.label, db_session) is expected
def test_validate_observations(setup_database): db_session = setup_database # Test False on alert that shouldnt be triggered alert3 = create_alert(db_session, "SELECT 0", "not null", "{}") observe(alert3.id, db_session) assert validate_observations(alert3.id, alert3.label, db_session) is False # Test True on alert that should be triggered alert4 = create_alert(db_session, "SELECT 55", "operator", '{"op": "<=", "threshold": 60}') observe(alert4.id, db_session) assert validate_observations(alert4.id, alert4.label, db_session) is True
def evaluate_alert( alert_id: int, label: str, session: Session, recipients: Optional[str] = None, slack_channel: Optional[str] = None, ) -> None: """Processes an alert to see if it should be triggered""" logger.info("Processing alert ID: %i", alert_id) state = None dttm_start = datetime.utcnow() try: logger.info("Querying observers for alert <%s:%s>", alert_id, label) error_msg = observe(alert_id, session) if error_msg: state = AlertState.ERROR logging.error(error_msg) except Exception as exc: # pylint: disable=broad-except state = AlertState.ERROR logging.exception(exc) logging.error("Failed at query observers for alert: %s (%s)", label, alert_id) dttm_end = datetime.utcnow() if state != AlertState.ERROR: # Don't validate alert on test runs since it may not be triggered if recipients or slack_channel: deliver_alert(alert_id, session, recipients, slack_channel) state = AlertState.TRIGGER # Validate during regular workflow and deliver only if triggered elif validate_observations(alert_id, label, session): deliver_alert(alert_id, session, recipients, slack_channel) state = AlertState.TRIGGER else: state = AlertState.PASS session.commit() alert = session.query(Alert).get(alert_id) if state != AlertState.ERROR: alert.last_eval_dttm = dttm_end alert.last_state = state alert.logs.append( AlertLog( scheduled_dttm=dttm_start, dttm_start=dttm_start, dttm_end=dttm_end, state=state, )) session.commit()
def test_not_null_validator(setup_database): dbsession = setup_database # Test passing SQLObserver with 'null' SQL result alert1 = create_alert(dbsession, "SELECT 0") observe(alert1.id, dbsession) assert not_null_validator(alert1.sql_observer[0], "{}") is False # Test passing SQLObserver with empty SQL result alert2 = create_alert(dbsession, "SELECT first FROM test_table WHERE first = -1") observe(alert2.id, dbsession) assert not_null_validator(alert2.sql_observer[0], "{}") is False # Test triggering alert with non-null SQL result alert3 = create_alert(dbsession, "SELECT 55") observe(alert3.id, dbsession) assert not_null_validator(alert3.sql_observer[0], "{}") is True
def test_alert_observer(setup_database): dbsession = setup_database # Test SQLObserver with int SQL return alert1 = create_alert(dbsession, "SELECT 55") observe(alert1.id, dbsession) assert alert1.sql_observer[0].observations[-1].value == 55.0 assert alert1.sql_observer[0].observations[-1].error_msg is None # Test SQLObserver with double SQL return alert2 = create_alert(dbsession, "SELECT 30.0 as wage") observe(alert2.id, dbsession) assert alert2.sql_observer[0].observations[-1].value == 30.0 assert alert2.sql_observer[0].observations[-1].error_msg is None # Test SQLObserver with NULL result alert3 = create_alert(dbsession, "SELECT null as null_result") observe(alert3.id, dbsession) assert alert3.sql_observer[0].observations[-1].value is None assert alert3.sql_observer[0].observations[-1].error_msg is None # Test SQLObserver with empty SQL return alert4 = create_alert(dbsession, "SELECT first FROM test_table WHERE first = -1") observe(alert4.id, dbsession) assert alert4.sql_observer[0].observations[-1].value is None assert alert4.sql_observer[0].observations[-1].error_msg is not None # Test SQLObserver with str result alert5 = create_alert(dbsession, "SELECT 'test_string' as string_value") observe(alert5.id, dbsession) assert alert5.sql_observer[0].observations[-1].value is None assert alert5.sql_observer[0].observations[-1].error_msg is not None # Test SQLObserver with two row result alert6 = create_alert(dbsession, "SELECT first FROM test_table") observe(alert6.id, dbsession) assert alert6.sql_observer[0].observations[-1].value is None assert alert6.sql_observer[0].observations[-1].error_msg is not None # Test SQLObserver with two column result alert7 = create_alert( dbsession, "SELECT first, second FROM test_table WHERE first = 1") observe(alert7.id, dbsession) assert alert7.sql_observer[0].observations[-1].value is None assert alert7.sql_observer[0].observations[-1].error_msg is not None
def test_not_null_validator(setup_database, description, query, value): logger.info(description) db_session = setup_database alert = create_alert(db_session, query) observe(alert.id, db_session) assert not_null_validator(alert, "{}") is value
def test_alert_observer(setup_database): db_session = setup_database # Test int SQL return alert1 = create_alert(db_session, "SELECT 55") observe(alert1.id, db_session) assert alert1.observations[-1].value == 55.0 assert alert1.observations[-1].error_msg is None # Test double SQL return alert2 = create_alert(db_session, "SELECT 30.0 as wage") observe(alert2.id, db_session) assert alert2.observations[-1].value == 30.0 assert alert2.observations[-1].error_msg is None # Test NULL result alert3 = create_alert(db_session, "SELECT null as null_result") observe(alert3.id, db_session) assert alert3.observations[-1].value is None assert alert3.observations[-1].error_msg is None # Test empty SQL return, expected alert4 = create_alert(db_session, "SELECT first FROM test_table WHERE first = -1") observe(alert4.id, db_session) assert alert4.observations[-1].value is None assert alert4.observations[-1].error_msg is None # Test str result alert5 = create_alert(db_session, "SELECT 'test_string' as string_value") observe(alert5.id, db_session) assert alert5.observations[-1].value is None assert alert5.observations[-1].error_msg is not None # Test two row result alert6 = create_alert(db_session, "SELECT first FROM test_table") observe(alert6.id, db_session) assert alert6.observations[-1].value is None assert alert6.observations[-1].error_msg is not None # Test two column result alert7 = create_alert( db_session, "SELECT first, second FROM test_table WHERE first = 1") observe(alert7.id, db_session) assert alert7.observations[-1].value is None assert alert7.observations[-1].error_msg is not None # Test multiline sql alert8 = create_alert( db_session, """ -- comment SELECT 1 -- comment FROM test_table WHERE first = 1 """, ) observe(alert8.id, db_session) assert alert8.observations[-1].value == 1.0 assert alert8.observations[-1].error_msg is None # Test jinja alert9 = create_alert(db_session, "SELECT {{ 2 }}") observe(alert9.id, db_session) assert alert9.observations[-1].value == 2.0 assert alert9.observations[-1].error_msg is None