def help_send_alerts(self, to_list):
        try:
            self.setup(to_list)

            ########################################################################
            # TEST
            ########################################################################
            send_alerts(
                settings=struct.wrap({"param": {"debug": True}}),
                db=self.db
            )

            ########################################################################
            # VERIFY
            ########################################################################
            emails = self.get_new_emails() # id, to, body

            if len(to_list) == 0:
                assert len(emails) == 0
                return

            #VERIFY ONE MAIL SENT
            assert len(emails) == 1
            #VERIFY to MATCHES WHAT WAS PASSED TO THIS FUNCTION
            assert set(emails[0].to) == set(to_list), "mail.delivery not matching what's send"

            #VERIFY last_sent IS WRITTEN
            alert_state = self.db.query("""
                SELECT
                    id
                FROM
                    alerts
                WHERE
                    reason={{reason}} AND
                    last_sent>={{send_time}}
            """, {
                "reason": self.reason,
                "send_time": self.now
            })
            expected_marked = set([d.id for d in self.test_data if CNV.JSON2object(d.details).expect == 'pass'])
            actual_marked = set(Q.select(alert_state, "id"))
            assert expected_marked == actual_marked, expand_template(
                "Expecting only id in {{expected}}, but instead got {{actual}}", {
                    "expected": str(expected_marked),
                    "actual": str(actual_marked)
                })

            #VERIFY BODY HAS THE CORRECT ALERTS
            expecting_alerts = set([d.id for d in map(lambda d: CNV.JSON2object(d.details), self.test_data) if d.expect == 'pass'])
            actual_alerts_sent = set([
                CNV.value2int(between(b, ">>>>", "<<<<"))
                for b in emails[0].body.split(dzAlerts.daemons.alert.SEPARATOR)
                if CNV.value2int(between(b, ">>>>", "<<<<")) != None
            ])
            assert expecting_alerts == actual_alerts_sent
        except Exception, e:
            Log.error("Test failure", e)
def main():
    settings = startup.read_settings()
    Log.start(settings.debug)
    try:
        for repo in settings.param.repos:
            with DB(settings.database) as db:
                try:
                    pull_repo(repo)

                    #GET LATEST DATE
                    existing_range = db.query("""
                        SELECT
                            max(`date`) `max`,
                            min(`date`) `min`,
                            min(revision) min_rev,
                            max(revision) max_rev
                        FROM
                            changesets
                        WHERE
                            repo={{repo}}
                    """, {"repo": repo.name})[0]

                    ranges = struct.wrap([
                        {"min": nvl(existing_range.max, CNV.milli2datetime(0)) + timedelta(0, 1)},
                        {"max": existing_range.min}
                    ])

                    for r in ranges:
                        for g, docs in Q.groupby(get_changesets(date_range=r, repo=repo), size=100):
                            for doc in docs:
                                doc.file_changes = None
                                doc.file_adds = None
                                doc.file_dels = None
                                doc.description = doc.description[0:16000]

                            db.insert_list("changesets", docs)
                            db.flush()

                    missing_revisions = sql.find_holes(db, "changesets", "revision", {"term":{"repo":repo.name}}, {"min": 0, "max": existing_range.max_rev + 1})
                    for _range in missing_revisions:
                        for g, docs in Q.groupby(get_changesets(revision_range=_range, repo=repo), size=100):
                            for doc in docs:
                                doc.file_changes = None
                                doc.file_adds = None
                                doc.file_dels = None
                                doc.description = doc.description[0:16000]

                            db.insert_list("changesets", docs)
                            db.flush()



                except Exception, e:
                    Log.warning("Failure to pull from {{repo.name}}", {"repo":repo}, e)
    finally:
        Log.stop()
def test_1(settings):
    test_data1 = struct.wrap({
        "header": ("date", "count", "mean-std", "mean", "mean+std", "reject"),
        "rows": [
            ("2013-Apr-05 13:55:00", "23", "655.048136994614", "668.5652173913044", "682.0822977879948"),
            ("2013-Apr-05 13:59:00", "23", "657.8717192954238", "673.3478260869565", "688.8239328784892"),
            ("2013-Apr-05 14:05:00", "23", "658.3247270429598", "673", "687.6752729570402"),
            ("2013-Apr-05 14:08:00", "23", "658.5476631609771", "673.6521739130435", "688.7566846651099"),
            ("2013-Apr-05 14:16:00", "23", "653.2311994952266", "666.1739130434783", "679.1166265917299"),
            ("2013-Apr-05 14:26:00", "23", "659.5613845589426", "671.8260869565217", "684.0907893541009"),
            ("2013-Apr-05 14:42:00", "23", "662.3517791831357", "677.1739130434783", "691.9960469038208"),
            ("2013-Apr-05 15:26:00", "23", "659.8270045518033", "672", "684.1729954481967"),
            ("2013-Apr-05 15:30:00", "23", "659.4023663187861", "674", "688.5976336812139"),
            ("2013-Apr-05 15:32:00", "23", "652.8643631817508", "666.9565217391304", "681.0486802965099"),
            ("2013-Apr-05 15:35:00", "23", "661.6037178485499", "675.1739130434783", "688.7441082384066"),
            ("2013-Apr-05 15:39:00", "23", "658.0124378440726", "670.1304347826087", "682.2484317211449"),
            ("2013-Apr-05 16:20:00", "46", "655.9645219644624", "667.4782608695652", "678.9919997746681"),
            ("2013-Apr-05 16:30:00", "23", "660.2572506418051", "671.8695652173913", "683.4818797929775"),
            ("2013-Apr-05 16:31:00", "23", "661.011102554583", "673.4347826086956", "685.8584626628083"),
            ("2013-Apr-05 16:55:00", "23", "655.9407699325201", "671.304347826087", "686.6679257196539"),
            ("2013-Apr-05 17:07:00", "23", "657.6412277100247", "667.5217391304348", "677.4022505508448"),
            #        ("2013-Apr-05 17:12:00", "23", "598.3432138277318", "617.7391304347826", "637.1350470418334"),   # <--DIP IN DATA
            ("2013-Apr-05 17:23:00", "23", "801.0537973113723", "822.1739130434783", "843.2940287755843", 1)  # <--SPIKE IN DATA
        ]
    })
    test_data1 = [
        struct.wrap({
            "timestamp": CNV.datetime2unix(CNV.string2datetime(t.date, "%Y-%b-%d %H:%M:%S")),
            "datetime": CNV.string2datetime(t.date, "%Y-%b-%d %H:%M:%S"),
            "count": int(t.count),
            "mean": float(t.mean),
            "variance": pow(float(t["mean+std"]) - float(t.mean), 2),
            "reject": t.reject
        })
        for t in CNV.table2list(test_data1.header, test_data1.rows)
    ]

    with DB(settings.perftest) as db:
        tester = test_alert_exception(db)
        tester.test_alert_generated(settings, test_data1)
def not_test_2(settings):
    """
    THIS WAS TESTING FOR A DECREASE IN THE MEAN, BUT THE CURRENT CODE IGNORES THOSE
    """
    test_data2 = struct.wrap({
        "header": ("timestamp", "mean", "std", "h0_rejected", "count"),
        "rows": [
            (1366388389, 295.36, 32.89741631, 0, 25),
            (1366387915, 307.92, 32.86198412, 0, 25),
            (1366390777, 309, 41.22802445, 0, 25),
            (1366398771, 309.24, 34.18488945, 0, 25),
            (1366401499, 308.2, 30.36170834, 0, 25),
            (1366412504, 192.8, 46.27634385, 1, 25), # Should be an alert
            (1366421699, 298.04, 29.09249617, 0, 25),
            (1366433920, 324.52, 28.13378752, 0, 25),
            (1366445744, 302.2, 28.19131072, 0, 25),
            (1366455408, 369.96, 31.25363979, 0, 25),
            (1366474119, 313.12, 33.66541252, 0, 25),
            (1366483789, 369.96, 30.81460693, 0, 25),
            (1366498412, 311.76, 36.02462121, 0, 25),
            (1366507773, 291.08, 27.86562996, 0, 25)
        ]
    })
    test_data2 = [
        struct.wrap({
            "timestamp": t.timestamp,
            "datetime": CNV.unix2datetime(t.timestamp),
            "count": t.count,
            "mean": t.mean,
            "variance": pow(t.std, 2),
            "reject": t.h0_rejected
        })
        for t in CNV.table2list(test_data2.header, test_data2.rows)
    ]

    with DB(settings.perftest) as db:
        tester = test_alert_exception(db)
        tester.test_alert_generated(test_data2)
def test_1(settings):
    test_data = struct.wrap({
        "header": ("date", "count", "mean-std", "mean", "mean+std"),
        "rows": [
            ("2013-Apr-05 13:53:00", "23", "458.4859477694967", "473.30434782608694", "488.1227478826772"),
            ("2013-Apr-05 13:55:00", "23", "655.048136994614", "668.5652173913044", "682.0822977879948"),
            ("2013-Apr-05 13:56:00", "23", "452.89061649510194", "466.9130434782609", "480.9354704614198"),
            ("2013-Apr-05 13:59:00", "23", "657.8717192954238", "673.3478260869565", "688.8239328784892"),
            ("2013-Apr-05 14:03:00", "23", "447.32039354456913", "458.4347826086956", "469.5491716728221"),
            ("2013-Apr-05 14:05:00", "23", "658.3247270429598", "673", "687.6752729570402"),
            ("2013-Apr-05 14:08:00", "23", "658.5476631609771", "673.6521739130435", "688.7566846651099"),
            ("2013-Apr-05 14:10:00", "46", "492.8191446281407", "581.7608695652174", "670.702594502294"),
            ("2013-Apr-05 14:16:00", "23", "653.2311994952266", "666.1739130434783", "679.1166265917299"),
            ("2013-Apr-05 14:20:00", "23", "467.2878043841933", "480.4782608695652", "493.6687173549371"),
            ("2013-Apr-05 14:26:00", "23", "659.5613845589426", "671.8260869565217", "684.0907893541009"),
            ("2013-Apr-05 14:42:00", "23", "662.3517791831357", "677.1739130434783", "691.9960469038208"),
            ("2013-Apr-05 15:22:00", "46", "473.9206889491661", "574.0869565217391", "674.2532240943121"),
            ("2013-Apr-05 15:26:00", "23", "659.8270045518033", "672", "684.1729954481967"),
            ("2013-Apr-05 15:29:00", "23", "448.23962722602005", "460.1304347826087", "472.02124233919733"),
            ("2013-Apr-05 15:30:00", "23", "659.4023663187861", "674", "688.5976336812139"),
            ("2013-Apr-05 15:32:00", "23", "652.8643631817508", "666.9565217391304", "681.0486802965099"),
            ("2013-Apr-05 15:34:00", "23", "444.689168566475", "456.7391304347826", "468.78909230309023"),
            ("2013-Apr-05 15:35:00", "23", "661.6037178485499", "675.1739130434783", "688.7441082384066"),
            ("2013-Apr-05 15:39:00", "23", "658.0124378440726", "670.1304347826087", "682.2484317211449"),
            ("2013-Apr-05 16:19:00", "23", "449.60814855486547", "465", "480.39185144513453"),
            ("2013-Apr-05 16:20:00", "46", "655.9645219644624", "667.4782608695652", "678.9919997746681"),
            ("2013-Apr-05 16:26:00", "23", "452.24027844816516", "466.2173913043478", "480.19450416053047"),
            ("2013-Apr-05 16:30:00", "23", "660.2572506418051", "671.8695652173913", "683.4818797929775"),
            ("2013-Apr-05 16:31:00", "23", "661.011102554583", "673.4347826086956", "685.8584626628083"),
            ("2013-Apr-05 16:53:00", "46", "457.7534312522435", "565.4347826086956", "673.1161339651477"),
            ("2013-Apr-05 16:55:00", "23", "655.9407699325201", "671.304347826087", "686.6679257196539"),
            ("2013-Apr-05 17:05:00", "46", "412.0344183976609", "561.0217391304348", "710.0090598632087"),
            ("2013-Apr-05 17:06:00", "46", "457.54528946430196", "567.5652173913044", "677.5851453183068"),
            ("2013-Apr-05 17:07:00", "23", "657.6412277100247", "667.5217391304348", "677.4022505508448"),
            ("2013-Apr-05 17:12:00", "23", "598.3432138277318", "617.7391304347826", "637.1350470418334"),
            ("2013-Apr-05 17:23:00", "23", "801.0537973113723", "822.1739130434783", "843.2940287755843")  # <--SPIKE IN DATA
        ]
    })

    with DB(settings.perftest) as db:
        tester = test_alert_threshold(db, test_data)
        tester.test_alert_generated()
        tester.test_alert_obsolete()
    def setup(self, to_list):
        self.uid = self.db.query("SELECT util.newid() uid FROM DUAL")[0].uid

        #CLEAR EMAILS
        self.db.execute("DELETE FROM mail.delivery")
        self.db.execute("DELETE FROM mail.attachment")
        self.db.execute("DELETE FROM mail.content")

        #TEST NUMBER OF LISTENERS IN listeners TABLE
        self.db.execute("DELETE FROM listeners")
        for l in to_list:
            self.db.insert("listeners", {"email": l})


        #MAKE A REASON FOR USE IN THIS TESTING
        self.db.execute("DELETE FROM alerts WHERE reason={{reason}}", {"reason": self.reason})
        self.db.execute("DELETE FROM reasons WHERE code={{reason}}", {"reason": self.reason})
        self.db.insert("reasons", {
            "code": self.reason,
            "description": ">>>>{{id}}<<<<", #SPECIAL PATTERN TO DISTINGUISH BETWEEN RESULTING MAILS
            "config": None,
            "last_run": self.now - timedelta(days=1)
        })


        #MAKE SOME TEST DATA (AND GET ID)
        all_dim = struct.wrap({
            "header":
                ("id", "test_run_id", "product_id", "operating_system_id", "test_id", "page_id", "date_received", "revision", "product", "branch", "branch_version", "operating_system_name",
                 "operating_system_version", "processor", "build_type", "machine_name", "pushlog_id", "push_date", "test_name", "page_url", "mean", "std", "h0_rejected", "p", "n_replicates", "fdr",
                 "trend_mean", "trend_std", "test_evaluation", "status"),
            "data": [
                (0, 117679, 65, 20, 64, 860, 1366261267, "d6b34be6fb4c", "Firefox", "Mozilla-Inbound", "23.0a1", "win", "6.2.9200", "x86_64", "opt", "t-w864-ix-022", "19801727", "1366245741", "tp5o",
                 "bbc.co.uk", 138.8, 40.5257120028, 0, 0.650194865224, 25, 0, 144.37333333365, 12.96130778322, 1, 1)
            ]})
        self.db.insert_list("test_data_all_dimensions", CNV.table2list(all_dim.header, all_dim.data))
        self.series = self.db.query("SELECT min(id) id FROM test_data_all_dimensions")[0].id


        # WE INJECT THE EXPECTED TEST RESULTS RIGHT INTO THE DETAILS, THAT WAY
        # WE CAN SEE THEM IN THE EMAIL DELIVERED
        test_data = struct.wrap({
            "header":
                ("id", "status", "create_time", "last_updated", "last_sent", "tdad_id", "reason", "details", "severity", "confidence", "solution"),
            "data": [
                #TEST last_sent IS NOT TOO YOUNG
                (self.uid + 0, "new", self.far_past, self.far_past, self.recent_past, self.series, self.reason, CNV.object2JSON({"id": 0, "expect": "fail"}), self.high_severity, self.high_confidence,
                 None),
                #TEST last_sent IS TOO OLD, SHOULD BE (RE)SENT
                (self.uid + 1, "new", self.far_past, self.now, None, self.series, self.reason, CNV.object2JSON({"id": 1, "expect": "pass"}), self.high_severity, self.high_confidence, None),
                (self.uid + 2, "new", self.far_past, self.now, self.far_past, self.series, self.reason, CNV.object2JSON({"id": 2, "expect": "pass"}), self.high_severity, self.high_confidence, None),
                (self.uid + 3, "new", self.now, self.now, self.recent_past, self.series, self.reason, CNV.object2JSON({"id": 3, "expect": "pass"}), self.high_severity, self.high_confidence, None),
                #TEST obsolete ARE NOT SENT
                (self.uid + 4, "obsolete", self.now, self.now, self.far_past, self.series, self.reason, CNV.object2JSON({"id": 4, "expect": "fail"}), self.high_severity, self.high_confidence, None),
                #TEST ONLY IMPORTANT ARE SENT
                (self.uid + 5, "new", self.now, self.now, None, self.series, self.reason, CNV.object2JSON({"id": 5, "expect": "pass"}), self.important, 0.5, None),
                (self.uid + 6, "new", self.now, self.now, None, self.series, self.reason, CNV.object2JSON({"id": 6, "expect": "fail"}), self.low_severity, self.high_confidence, None),
                (self.uid + 7, "new", self.now, self.now, None, self.series, self.reason, CNV.object2JSON({"id": 7, "expect": "fail"}), self.high_severity, self.low_confidence, None),
                #TEST ONES WITH SOLUTION ARE NOT SENT
                (self.uid + 8, "new", self.now, self.now, None, self.series, self.reason, CNV.object2JSON({"id": 8, "expect": "fail"}), self.high_severity, self.high_confidence, "a solution!")
            ]
        })

        self.test_data = CNV.table2list(test_data.header, test_data.data)
        self.db.insert_list("alerts", self.test_data)