Пример #1
0
    def test_perform_action(self):
        print("\nTesting perform_action from metagov\n")
        # 1) Create Policy that performs a metagov action
        policy = PlatformPolicy()
        policy.community = self.community
        policy.filter = "return True"
        policy.initialize = "debug('help!')"
        policy.check = """parameters = {"low": 4, "high": 5}
response = metagov.perform_action('randomness.random-int', parameters)
if response and response.get('value') == 4:
    return PASSED
return FAILED"""
        policy.notify = "pass"
        policy.success = "pass"
        policy.fail = "pass"
        policy.description = "test"
        policy.name = "test policy"
        policy.save()

        # 2) Save an action to trigger the policy execution
        action = SlackPinMessage()
        action.initiator = self.user
        action.community = self.community
        action.save()

        self.assertEqual(action.proposal.status, "passed")

        # Check that evaluation debug log was generated
        from django_db_logger.models import EvaluationLog

        self.assertEqual(
            EvaluationLog.objects.filter(community=policy.community,
                                         msg__contains="help!").count(), 1)
Пример #2
0
    def test_non_community_origin_actions(self):
        """Actions that didnt originate on the community platform should executed on 'pass'"""
        first_policy = Policy.objects.create(
            **all_actions_pass_policy,
            kind=Policy.PLATFORM,
            community=self.slack_community,
            name="policy that passes",
        )

        # engine should call "execute" for non-community actions on pass
        action = SlackPinMessage(initiator=self.user, community=self.slack_community, community_origin=False)
        # mock the execute function
        action.execute = lambda: action.data.set("was_executed", True)
        action.save()
        self.assertEqual(action.proposal.status, "passed")
        self.assertEqual(action.data.get("was_executed"), True)

        # engine should not call "execute" for community actions on pass
        action = SlackPinMessage(initiator=self.user, community=self.slack_community, community_origin=True)
        # mock the execute function
        action.execute = lambda: action.data.set("was_executed", True)
        action.save()
        self.assertEqual(action.proposal.status, "passed")
        self.assertEqual(action.data.get("was_executed"), None)
Пример #3
0
    def test_close_process(self):
        """Integration-test metagov process with randomness plugin"""
        # 1) Create Policy and PlatformAction
        policy_code = {
            **all_actions_pass_policy,
            "initialize":
            """
metagov.start_process("randomness.delayed-stochastic-vote", {"options": ["one", "two", "three"], "delay": 1})
""",
            "check":
            """
result = metagov.close_process()
action.data.set('status', result.status)
action.data.set('outcome', result.outcome)

if result is None:
    return #still processing
if result.errors:
    return FAILED
if result.outcome:
    return PASSED if result.outcome.get('winner') else FAILED
return FAILED
""",
        }
        policy = Policy(
            **policy_code,
            kind=Policy.PLATFORM,
            community=self.slack_community,
            description="test",
            name="test policy",
        )
        policy.save()

        action = SlackPinMessage()
        action.initiator = self.user
        action.community = self.slack_community
        action.community_origin = True

        # 2) Save action to trigger policy execution
        action.save()

        process = MetagovProcess.objects.filter(action=action,
                                                policy=policy).first()
        self.assertIsNotNone(process)

        self.assertEqual(action.proposal.status, "passed")
        self.assertEqual(action.data.get("status"), "completed")
        self.assertIsNotNone(action.data.get("outcome").get("winner"))
Пример #4
0
    def test_close_process(self):
        """Integration-test metagov process with randomness plugin. Process is closed from within the policy."""
        # 1) Create Policy and GovernableAction
        policy_code = {
            **TestUtils.ALL_ACTIONS_PASS,
            "initialize":
            """
metagov.start_process("randomness.delayed-stochastic-vote", options=["one", "two", "three"], delay=1)
""",
            "check":
            """
result = metagov.close_process()
proposal.data.set('reached_close', True)

if result is None:
    return #still processing
if result.errors:
    return FAILED
if result.outcome:
    return PASSED if result.outcome.get('winner') else FAILED
return FAILED
""",
        }
        policy = Policy(
            **policy_code,
            kind=Policy.PLATFORM,
            community=self.community,
        )
        policy.save()

        action = SlackPinMessage()
        action.initiator = self.user
        action.community = self.slack_community
        action.community_origin = True

        # 2) Save action to trigger policy execution
        action.save()

        proposal = Proposal.objects.get(action=action, policy=policy)
        self.assertEqual(proposal.status, Proposal.PASSED)
        self.assertTrue(proposal.is_vote_closed)
        self.assertEqual(proposal.data.get("reached_close"), True)

        # assert that the process outcome was saved
        metagov = Metagov(proposal)
        process_data = metagov.get_process()
        self.assertEqual(process_data.status, "completed")
        self.assertIsNotNone(process_data.outcome.get("winner"))
Пример #5
0
    def test_perform_action(self):
        """Integration-test metagov.perform_action function with randomness plugin"""
        # 1) Create Policy that performs a metagov action
        policy = Policy(kind=Policy.PLATFORM)
        policy.community = self.community
        policy.filter = "return True"
        policy.initialize = "logger.debug('help!')"
        policy.check = """
response = metagov.perform_action('randomness.random-int', low=4, high=5)
if response and response.get('value') == 4:
    return PASSED
return FAILED"""
        policy.notify = "pass"
        policy.success = "pass"
        policy.fail = "pass"
        policy.description = "test"
        policy.name = "test policy"
        policy.save()

        # 2) Save an action to trigger the policy execution
        action = SlackPinMessage()
        action.initiator = self.user
        action.community = self.slack_community
        action.community_origin = True
        action.revert = lambda: None
        action.execute = lambda: None
        action.save()

        proposal = Proposal.objects.get(action=action, policy=policy)
        self.assertEqual(proposal.status, Proposal.PASSED)

        # Check that proposal debug log was generated
        from django_db_logger.models import EvaluationLog

        self.assertEqual(
            EvaluationLog.objects.filter(proposal=proposal,
                                         msg__contains="help!").count(), 1)
Пример #6
0
    def test_loomio_vote(self):
        print("\nTesting external process\n")
        # 1) Create Policy and PlatformAction
        policy = PlatformPolicy()
        policy.community = self.community
        policy.filter = "return True"
        policy.initialize = """
result = metagov.start_process("loomio.poll", {"title": "[test] policykit poll", "options": ["one", "two", "three"], "closing_at": "2021-05-11"})
poll_url = result.outcome.get('poll_url')
action.data.set('poll_url', poll_url)
"""
        policy.notify = "pass"
        policy.check = """
result = metagov.get_process()
if result.status != "completed":
    return None #still processing
if result.errors:
    return FAILED
if result.outcome:
    return PASSED if result.outcome.get('value') == 27 else FAILED
return FAILED
"""
        policy.success = "pass"
        policy.fail = "pass"
        policy.description = "test"
        policy.name = "test policy"
        policy.save()

        action = SlackPinMessage()
        action.initiator = self.user
        action.community = self.community

        # 2) Save action to trigger execution of check() and notify()
        action.save()

        process = MetagovProcess.objects.filter(action=action,
                                                policy=policy).first()
        self.assertIsNotNone(process)
        self.assertEqual(process.data.status, "pending")
        self.assertTrue("https://www.loomio.org/p/" in
                        process.data.outcome.get("poll_url"))
        self.assertEqual(action.proposal.status, "proposed")
        self.assertTrue(
            "https://www.loomio.org/p/" in action.data.get("poll_url"))
Пример #7
0
class ExecCodeTests(TestCase):
    def setUp(self):
        self.slack_community, self.user = TestUtils.create_slack_community_and_user()

        self.policy = Policy.objects.create(
            **TestUtils.ALL_ACTIONS_PROPOSED,
            kind=Policy.PLATFORM,
            community=self.slack_community.community,
        )
        self.action = SlackPinMessage(initiator=self.user, community=self.slack_community, community_origin=True)
        self.action.revert = lambda: None
        self.action.execute = lambda: None
        self.action.save()
        self.proposal = Proposal.objects.create(action=self.action, policy=self.policy)

    def test_return_status(self):
        """Test the ability to return status"""

        ctx = EvaluationContext(self.proposal)
        self.assertEqual(exec_code_block("return PASSED", ctx), "passed")
        self.assertEqual(exec_code_block("return FAILED", ctx), "failed")
        self.assertEqual(exec_code_block("return PROPOSED", ctx), "proposed")

    def test_scope(self):
        """Test that all the EvaluationContext attributes are in scope"""

        ctx = EvaluationContext(self.proposal)

        self.assertRaises(PolicyCodeError, exec_code_block, "return variable_doesnt_exist", ctx)
        self.assertRaises(PolicyCodeError, exec_code_block, "return discord.team_id", ctx)
        self.assertRaises(PolicyCodeError, exec_code_block, "import os", ctx)

        self.assertEqual(exec_code_block("return slack.team_id", ctx), "ABC")

        # these shouldn't raise any exceptions
        exec_code_block("return action.id", ctx)
        exec_code_block("return policy.id", ctx)
        exec_code_block("return proposal.proposal_time", ctx)
        exec_code_block("return proposal.vote_url", ctx)
        exec_code_block("return datetime.datetime.now()", ctx)
        self.assertEqual(exec_code_block("return math.ceil(0.9)", ctx), 1)

    def test_syntax_errors(self):
        """syntax errors and other errors show correct line numbers"""
        ctx = EvaluationContext(self.proposal)
        with self.assertRaises(PolicyCodeError) as cm:
            exec_code_block("return variable_doesnt_exist", ctx)
        self.assertTrue("NameError at line 1" in str(cm.exception))

        with self.assertRaises(PolicyCodeError) as cm:
            exec_code_block("foo = 10\nimport something\nreturn True", ctx)
        self.assertTrue("SyntaxError at line 2" in str(cm.exception))

    def test_logger(self):
        """Test evaluation logger"""

        ctx = EvaluationContext(self.proposal)
        exec_code_block("logger.debug('hello')", ctx)
        self.assertEqual(EvaluationLog.objects.filter(proposal=self.proposal, msg__contains="hello").count(), 1)
        exec_code_block("logger.error('world')", ctx)
        self.assertEqual(EvaluationLog.objects.filter(proposal=self.proposal, msg__contains="world").count(), 1)
Пример #8
0
def action(request):
    json_data = json.loads(request.body)
    logger.info('RECEIVED ACTION')
    logger.info(json_data)
    action_type = json_data.get('type')

    if action_type == "url_verification":
        challenge = json_data.get('challenge')
        return HttpResponse(challenge)

    elif action_type == "event_callback":
        event = json_data.get('event')
        team_id = json_data.get('team_id')
        community = SlackCommunity.objects.get(team_id=team_id)
        admin_user = SlackUser.objects.filter(is_community_admin=True)[0]

        new_api_action = None
        policy_kit_action = False

        if event.get('type') == "channel_rename":
            if not is_policykit_action(community, event['channel']['name'], 'name', SlackRenameConversation.ACTION):
                new_api_action = SlackRenameConversation()
                new_api_action.community = community
                new_api_action.name = event['channel']['name']
                new_api_action.channel = event['channel']['id']

                u,_ = SlackUser.objects.get_or_create(username=event['user'],
                                                    community=community)
                new_api_action.initiator = u
                prev_names = new_api_action.get_channel_info()
                new_api_action.prev_name = prev_names[0]

        elif event.get('type') == 'message' and event.get('subtype') == None:
            if not is_policykit_action(community, event['text'], 'text', SlackPostMessage.ACTION):
                new_api_action = SlackPostMessage()
                new_api_action.community = community
                new_api_action.text = event['text']
                new_api_action.channel = event['channel']
                new_api_action.time_stamp = event['ts']

                u,_ = SlackUser.objects.get_or_create(username=event['user'], community=community)

                new_api_action.initiator = u

        elif event.get('type') == "member_joined_channel":
            if not is_policykit_action(community, event['channel'], 'channel', SlackJoinConversation.ACTION):
                new_api_action = SlackJoinConversation()
                new_api_action.community = community
                if event.get('inviter'):
                    u,_ = SlackUser.objects.get_or_create(username=event['inviter'],
                                                          community=community)
                    new_api_action.initiator = u
                else:
                    u,_ = SlackUser.objects.get_or_create(username=event['user'],
                                                          community=community)
                    new_api_action.initiator = u
                new_api_action.users = event.get('user')
                new_api_action.channel = event['channel']


        elif event.get('type') == 'pin_added':
            if not is_policykit_action(community, event['channel_id'], 'channel', SlackPinMessage.ACTION):
                new_api_action = SlackPinMessage()
                new_api_action.community = community

                u,_ = SlackUser.objects.get_or_create(username=event['user'],
                                                      community=community)
                new_api_action.initiator = u
                new_api_action.channel = event['channel_id']
                new_api_action.timestamp = event['item']['message']['ts']

        if new_api_action.initiator.has_perm('slackintegration.add_' + new_api_action.action_codename):
            if new_api_action and not policy_kit_action:
                #if they have execute permission, skip all policies
                if new_api_action.initiator.has_perm('slackintegration.can_execute_' + new_api_action.action_codename):
                    new_api_action.execute()
                else:
                    for policy in PlatformPolicy.objects.filter(community=new_api_action.community):
                      if filter_policy(policy, new_api_action):
                          if not new_api_action.pk:
                              new_api_action.community_origin = True
                              new_api_action.is_bundled = False
                              new_api_action.save()
                          initialize_policy(policy, new_api_action)
                          cond_result = check_policy(policy, new_api_action)
                          if cond_result == Proposal.PROPOSED or cond_result == Proposal.FAILED:
                              new_api_action.revert()
        else:
            p = Proposal.objects.create(status=Proposal.FAILED,
                                        author=new_api_action.initiator)
            new_api_action.proposal = p

        if event.get('type') == 'reaction_added':
            ts = event['item']['ts']
            action = None
            action_res = PlatformAction.objects.filter(community_post=ts)
            if action_res.exists():
                action = action_res[0]

                if event['reaction'] == '+1' or event['reaction'] == '-1':
                    if event['reaction'] == '+1':
                        value = True
                    elif event['reaction'] == '-1':
                        value = False

                    user,_ = SlackUser.objects.get_or_create(username=event['user'],
                                                            community=action.community)
                    uv = BooleanVote.objects.filter(proposal=action.proposal,
                                                                 user=user)
                    if uv.exists():
                        uv = uv[0]
                        uv.boolean_value = value
                        uv.save()
                    else:
                        uv = BooleanVote.objects.create(proposal=action.proposal, user=user, boolean_value=value)

            if action == None:
                action_res = PlatformActionBundle.objects.filter(community_post=ts)
                if action_res.exists():
                    action = action_res[0]

                    bundled_actions = list(action.bundled_actions.all())

                    if event['reaction'] in NUMBERS_TEXT.keys():
                        num = NUMBERS_TEXT[event['reaction']]
                        voted_action = bundled_actions[num]

                        user,_ = SlackUser.objects.get_or_create(username=event['user'],
                                                               community=voted_action.community)
                        uv = NumberVote.objects.filter(proposal=voted_action.proposal,
                                                                 user=user)
                        if uv.exists():
                            uv = uv[0]
                            uv.number_value = 1
                            uv.save()
                        else:
                            uv = NumberVote.objects.create(proposal=voted_action.proposal, user=user, number_value=1)

    return HttpResponse("")
Пример #9
0
def slack_event_to_platform_action(community, event_type, data, initiator):
    new_api_action = None
    initiator = initiator.get("user_id")
    if not initiator:
        # logger.debug(f"{event_type} event does not have an initiating user ID, skipping")
        return
    from integrations.slack.models import (
        SlackJoinConversation,
        SlackPinMessage,
        SlackPostMessage,
        SlackRenameConversation,
        SlackUser,
    )

    event = data
    if event_type == "message" and event.get("subtype") == "channel_name":
        if not is_policykit_action(community, event["name"], "name",
                                   SlackRenameConversation.ACTION):
            new_api_action = SlackRenameConversation(
                community=community,
                name=event["name"],
                channel=event["channel"],
                previous_name=event["old_name"])
            u, _ = SlackUser.objects.get_or_create(username=initiator,
                                                   community=community)
            new_api_action.initiator = u
    elif event_type == "message" and event.get("subtype") == None:
        if not is_policykit_action(community, event["text"], "text",
                                   SlackPostMessage.ACTION):
            new_api_action = SlackPostMessage()
            new_api_action.community = community
            new_api_action.text = event["text"]
            new_api_action.channel = event["channel"]
            new_api_action.timestamp = event["ts"]

            u, _ = SlackUser.objects.get_or_create(username=initiator,
                                                   community=community)

            new_api_action.initiator = u

    elif event_type == "member_joined_channel":
        if not is_policykit_action(community, event["channel"], "channel",
                                   SlackJoinConversation.ACTION):
            new_api_action = SlackJoinConversation()
            new_api_action.community = community
            if event.get("inviter"):
                u, _ = SlackUser.objects.get_or_create(
                    username=event["inviter"], community=community)
                new_api_action.initiator = u
            else:
                u, _ = SlackUser.objects.get_or_create(username=initiator,
                                                       community=community)
                new_api_action.initiator = u
            new_api_action.users = initiator
            new_api_action.channel = event["channel"]

    elif event_type == "pin_added":
        if not is_policykit_action(community, event["channel_id"], "channel",
                                   SlackPinMessage.ACTION):
            new_api_action = SlackPinMessage()
            new_api_action.community = community

            u, _ = SlackUser.objects.get_or_create(username=initiator,
                                                   community=community)
            new_api_action.initiator = u
            new_api_action.channel = event["channel_id"]
            new_api_action.timestamp = event["item"]["message"]["ts"]

    return new_api_action
Пример #10
0
 def new_slackpinmessage(self, initiator=None, community_origin=False):
     """helper for creating a new platform action"""
     return SlackPinMessage(initiator=initiator or self.user,
                            community=self.slack_community,
                            community_origin=community_origin)
Пример #11
0
    def test_outcome_endpoint(self):
        """Integration-test metagov process is updated via the outcome receiver endpoint in PolicyKit"""
        # 1) Create Policy and GovernableAction
        policy_code = {
            **TestUtils.ALL_ACTIONS_PASS,
            "initialize":
            """
metagov.start_process("randomness.delayed-stochastic-vote", options=["one", "two", "three"], delay=1)
""",
            "check":
            """
result = metagov.get_process()
proposal.data.set('process_status', result.status)
proposal.data.set('is_vote_closed', proposal.is_vote_closed)

if result is None or result.status == "pending":
    return None #still processing
if result.errors:
    return FAILED
if result.outcome:
    return PASSED if result.outcome.get('winner') else FAILED
return FAILED
""",
        }
        policy = Policy(
            **policy_code,
            kind=Policy.PLATFORM,
            community=self.community,
        )
        policy.save()

        action = SlackPinMessage()
        action.initiator = self.user
        action.community = self.slack_community
        action.community_origin = True
        action.revert = lambda: None
        action.execute = lambda: None

        # 2) Save action to trigger policy execution
        action.save()

        proposal = Proposal.objects.get(action=action, policy=policy)
        self.assertEqual(proposal.status, Proposal.PROPOSED)
        self.assertEqual(proposal.data.get("process_status"), "pending")
        self.assertFalse(proposal.data.get("is_vote_closed"))
        self.assertFalse(proposal.is_vote_closed)

        # 2) Close the process, which should emit a signal for PolicyKit to handle
        proposal.governance_process.update()
        proposal.governance_process.proxy.close()

        # re-run proposal using celery task function
        consider_proposed_actions()
        proposal.refresh_from_db()

        self.assertEqual(proposal.status, Proposal.PASSED)

        metagov = Metagov(proposal)
        process_data = metagov.get_process()
        self.assertEqual(process_data.status, "completed")
        self.assertIsNotNone(process_data.outcome.get("winner"))