class SideloaderDB(object):
    def __init__(self):
    def _fetchOneTxn(self, txn, *a, **kw):
    def fetchOne(self, *a, **kw):
    def runInsert(self, table, keys):
    def select(self, table, fields, **kw):
    def getProject(self, id):
    def updateBuildLog(self, id, log):
    def getProjectNotificationSettings(self, id):
    def getBuild(self, id):
    def getBuildNumber(self, repo):
    def setBuildNumber(self, repo, num, create=False):
    def setBuildState(self, id, state):
    def setBuildFile(self, id, f):
    def createRelease(self, release):
    def checkReleaseSchedule(self, release):
    def releaseSignoffCount(self, release_id):
    def signoff_remaining(self, release_id, flow):
    def checkReleaseSignoff(self, release_id, flow):
    def countReleases(self, id, waiting=False, lock=False):
    def getReleases(self, flowid=None, waiting=None, lock=None):
    def getRelease(self, id):
    def getReleaseStream(self, id):
    def updateReleaseLocks(self, id, lock):
    def updateReleaseState(self, id, lock=False, waiting=False):
    def getFlow(self, id):
    def getFlowSignoffList(self, flow):
    def getFlowNotifyList(self, flow):
    def getAutoFlows(self, project):
    def getNextFlowRelease(self, flow_id):
    def getLastFlowRelease(self, flow_id):
    def getFlowTargets(self, flow_id):
    def getServer(self, id):
    def updateTargetState(self, id, state):
    def updateTargetLog(self, id, log):
    def updateTargetBuild(self, id, build):
    def updateServerStatus(self, id, status):
    def getWebhooks(self, flow_id):
    def setWebhookResponse(self, id, response): def async(f):
    def wrapper(self, *args, **kw):
    def __init__(self, clock):
    def _set_id(self, table, row):
    def runInsert(self, table, keys):
    def getProject(self, id):
    def updateBuildLog(self, id, data):
    def getProjectNotificationSettings(self, id):
    def getBuild(self, id):
    def getBuildNumber(self, repo):
    def setBuildNumber(self, repo, num, create=False):
    def setBuildState(self, id, state):
    def setBuildFile(self, id, file):
    def createRelease(self, release):
    def checkReleaseSchedule(self, release):
    def releaseSignoffCount(self, release_id):
    def signoff_remaining(self, release_id, flow):
    def checkReleaseSignoff(self, release_id, flow):
    def countReleases(self, id, waiting=False, lock=False):
    def getReleases(self, flowid=None, waiting=None, lock=None):
    def getRelease(self, id):
    def getReleaseStream(self, id):
    def updateReleaseLocks(self, id, lock):
    def updateReleaseState(self, id, lock=False, waiting=False):
    def getFlow(self, id):
    def getFlowSignoffList(self, flow):
    def getFlowNotifyList(self, flow):
    def getAutoFlows(self, id):
    def getNextFlowRelease(self, flow_id):
    def getLastFlowRelease(self, flow_id):
    def getFlowTargets(self, flow_id):
    def getServer(self, id):
    def updateTargetState(self, id, state):
    def updateTargetLog(self, id, log):
    def updateTargetBuild(self, id, build):
    def updateServerStatus(self, id, status):
    def getWebhooks(self, flow_id):
    def setWebhookResponse(self, id, response):
class FakeDB(object): RELEASESTREAM_QA = {
    'id': 1,
    'name': 'QA',
    'push_command': "echo 'Pushing to QA: %s'",
} RELEASESTREAM_PROD = {
    'id': 2,
    'name': 'PROD',
    'push_command': "echo 'Pushing to PROD: %s'",
} PROJECT_SIDELOADER = {
    'id': 1,
    'name': 'Test project',
    'github_url': 'https://github.com/praekelt/sideloader.git',
    'branch': 'develop',
    'deploy_file': '.deploy.yaml',
    'idhash': '6d8adfebec3011e59599b88d121fe884',
    'notifications': False,
    'slack_channel': '#mychan',
    'created_by_user_id': 1,
    'release_stream_id': 1,
    'build_script': 'scripts/test_build.sh',
    'package_name': 'test_package',
    'postinstall_script': 'scripts/test_post.sh',
    'package_manager': 'deb',
    'deploy_type': 'virtualenv',
} BUILD_1 = {
    'id': 1,
    'build_time': datetime_utc(2016, 4, 1, 0, 0, 0),
    'task_id': '1',
    'log': '',
    'project_id': 1,
    'state': 0,
    'build_file': '',
} RELEASEFLOW_QA = {
    'id': 1,
    'name': 'QA Flow',
    'project_id': 1,
    'stream_mode': 0,
    'stream_id': 1,
    'require_signoff': False,
    'signoff_list': '',
    'quorum': 0,
    'notify': False,
    'notify_list': '',
    'service_restart': True,
    'service_pre_stop': False,
    'puppet_run': True,
    'auto_release': True,
} RELEASEFLOW_PROD = {
    'id': 2,
    'name': 'PROD Flow',
    'project_id': 1,
    'stream_mode': 0,
    'stream_id': 2,
    'require_signoff': False,
    'signoff_list': '',
    'quorum': 0,
    'notify': False,
    'notify_list': '',
    'service_restart': True,
    'service_pre_stop': False,
    'puppet_run': True,
    'auto_release': False,
} RELEASE_1 = {
    'id': 1,
    'flow_id': 1,
    'build_id': 1,
    'waiting': True,
    'scheduled': None,
    'release_date': datetime_utc(2016, 4, 1, 1, 0, 0),
    'lock': False,
} WEBHOOK_QA_1 = {
    'id': 1,
    'flow_id': RELEASEFLOW_QA['id'],
    'description': 'QA webhook',
    'url': 'http://example.com/hook1/token',
    'method': 'GET',
    'content_type': 'application/json',
    'payload': '',
    'after_id': None,
    'last_response': '',
} WEBHOOK_QA_2 = {
    'id': 2,
    'flow_id': RELEASEFLOW_QA['id'],
    'description': 'Another QA webhook',
    'url': 'http://example.com/hook2/token',
    'method': 'GET',
    'content_type': 'application/json',
    'payload': '',
    'after_id': None,
    'last_response': '',
} def dictmerge(dct, **kw):
    """
    Because there's no non-mutating equivalent of dict.update().
    """
    dct = dct.copy()
    dct.update(kw)
    return dct def now_utc():
    return datetime.now(utc)
from twisted.internet import defer, reactor
from twisted.python.failure import Failure
from twisted.trial import unittest
from sideloader import task_db
from sideloader.tests import fake_db
from sideloader.tests.fake_data import (
    RELEASESTREAM_QA, RELEASESTREAM_PROD, PROJECT_SIDELOADER, BUILD_1,
    RELEASEFLOW_QA, RELEASEFLOW_PROD, RELEASE_1, WEBHOOK_QA_1, WEBHOOK_QA_2)
from sideloader.tests.utils import dictmerge, now_utc
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_QA)
        yield self.db.runInsert('sideloader_build', BUILD_1)
        yield self.db.runInsert('sideloader_release', RELEASE_1)

        assert RELEASE_1['lock'] is False
        yield self.db.updateReleaseLocks(1, True)
        release = yield self.db.getRelease(1)
        assert release['lock'] is True
        yield self.db.updateReleaseLocks(1, False)
        release = yield self.db.getRelease(1)
        assert release['lock'] is False

    @defer.inlineCallbacks
    def test_updateReleaseState_no_args(self):
        """
        We can update the release state with default values.
        """
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_QA)
        yield self.db.runInsert('sideloader_project', PROJECT_SIDELOADER)
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_QA)
        yield self.db.runInsert('sideloader_build', BUILD_1)
        yield self.db.runInsert('sideloader_release', RELEASE_1)

        yield self.db.updateReleaseLocks(1, True)
        release = yield self.db.getRelease(1)
        assert release['lock'] is True
        assert release['waiting'] is True

        yield self.db.updateReleaseState(1)
        release = yield self.db.getRelease(1)
        assert release['lock'] is False
        assert release['waiting'] is False

    @defer.inlineCallbacks
    def test_getFlow(self):
        """
        We can get information about a release flow.
        """
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_QA)
        yield self.db.runInsert('sideloader_project', PROJECT_SIDELOADER)
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_QA)
        flow = yield self.db.getFlow(1)
        assert flow == RELEASEFLOW_QA

    def test_getFlowNotifyList_empty(self):
        """
        We get an empty notify list when a flow has nobody to notify.
        """
        notify_list = self.db.getFlowNotifyList(RELEASEFLOW_QA)
        assert notify_list == []

    @defer.inlineCallbacks
    def test_getReleaseFlow_missing(self):
        """
        We can't get information about a release flow that doesn't exist.
        """
        yield self.assertFailure(self.db.getFlow(42), Exception)

    @defer.inlineCallbacks
    def test_getAutoFlows(self):
        """
        We can get all the autorelease flows for a project.
        """
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_QA)
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_PROD)
        yield self.db.runInsert('sideloader_project', PROJECT_SIDELOADER)
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_QA)
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_PROD)
        flows = yield self.db.getAutoFlows(1)
        assert flows == [RELEASEFLOW_QA]

    @defer.inlineCallbacks
    def test_getAutoFlows_no_flows(self):
        """
        If a project has no autorelease flows, we get an empty list of them.
        """
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_QA)
        yield self.db.runInsert('sideloader_project', PROJECT_SIDELOADER)
        flows = yield self.db.getAutoFlows(1)
        assert flows == []

    @defer.inlineCallbacks
    def test_getWebhooks(self):
        """
        We can get all webhooks for a release flow.
        """
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_QA)
        yield self.db.runInsert('sideloader_project', PROJECT_SIDELOADER)
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_QA)
        yield self.db.runInsert('sideloader_webhook', WEBHOOK_QA_1)
        yield self.db.runInsert('sideloader_webhook', WEBHOOK_QA_2)

        webhooks = yield self.db.getWebhooks(RELEASEFLOW_QA['id'])
        assert id_sorted(webhooks) == [WEBHOOK_QA_1, WEBHOOK_QA_2]

    @defer.inlineCallbacks
    def test_getWebhooks_no_webhooks(self):
        """
        If a release flow has no webhooks, we get an empty list of them.
        """
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_QA)
        yield self.db.runInsert('sideloader_project', PROJECT_SIDELOADER)
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_QA)

        webhooks = yield self.db.getWebhooks(RELEASEFLOW_QA['id'])
        assert webhooks == []

    @defer.inlineCallbacks
    def test_setWebhookResponse(self):
        """
        We can update a webhook's last response.
        """
        yield self.db.runInsert('sideloader_releasestream', RELEASESTREAM_QA)
        yield self.db.runInsert('sideloader_project', PROJECT_SIDELOADER)
        yield self.db.runInsert('sideloader_releaseflow', RELEASEFLOW_QA)
        yield self.db.runInsert('sideloader_webhook', WEBHOOK_QA_1)

        [webhook] = yield self.db.getWebhooks(RELEASEFLOW_QA['id'])
        assert webhook['last_response'] == ""
        yield self.db.setWebhookResponse(WEBHOOK_QA_1['id'], "Hello.")

        [webhook] = yield self.db.getWebhooks(RELEASEFLOW_QA['id'])