Beispiel #1
0
    def test_cannot_approve_already_approved(self):
        u = UserFactory()
        req = ApprovalRequestFactory()
        req.approve(u, "r+")

        with pytest.raises(req.NotActionable):
            req.approve(u, "r+")
Beispiel #2
0
    def test_cannot_reject_already_rejected(self):
        u = UserFactory()
        req = ApprovalRequestFactory()
        req.reject(u, "r-")

        with pytest.raises(req.NotActionable):
            req.reject(u, "r-")
Beispiel #3
0
    def test_it_can_edit_approval_requests(self, api_client):
        approval_request = ApprovalRequestFactory(active=True)

        res = api_client.patch('/api/v1/approval_request/%s/' % approval_request.id,
                               {'active': False})
        assert res.status_code == 200

        approval_request.refresh_from_db()
        assert not approval_request.active
Beispiel #4
0
    def test_it_can_enable_recipes(self, api_client):
        recipe = RecipeFactory(enabled=False)
        approval_request = ApprovalRequestFactory(recipe=recipe)
        approval_request.approve(UserFactory())

        res = api_client.post('/api/v1/recipe/%s/enable/' % recipe.id)
        assert res.status_code == 204

        recipe = Recipe.objects.all()[0]
        assert recipe.enabled
Beispiel #5
0
    def test_reject(self):
        u = UserFactory()
        req = ApprovalRequestFactory()
        req.reject(u, 'r-')
        assert not req.approved
        assert req.approver == u
        assert req.comment == 'r-'

        recipe = req.revision.recipe
        assert not recipe.is_approved
Beispiel #6
0
    def test_reject_not_actionable(self, api_client):
        r = RecipeFactory()
        a = ApprovalRequestFactory(revision=r.latest_revision)
        a.approve(UserFactory(), 'r+')

        res = api_client.post(
            '/api/v1/approval_request/{}/reject/'.format(a.id),
            {'comment': '-r'})
        assert res.status_code == 400
        assert res.data[
            'error'] == 'This approval request has already been approved or rejected.'
Beispiel #7
0
    def test_recipe_is_approved(self):
        recipe = RecipeFactory(name="old")
        assert not recipe.is_approved

        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        approval.approve(UserFactory(), "r+")
        assert recipe.is_approved
        assert recipe.approved_revision == recipe.latest_revision

        recipe.revise(name="new")
        assert recipe.is_approved
        assert recipe.approved_revision != recipe.latest_revision
Beispiel #8
0
    def test_recipe_is_approved(self):
        recipe = RecipeFactory(name="old")
        assert not recipe.is_approved

        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        approval.approve(UserFactory(), "r+")
        assert recipe.is_approved
        assert recipe.approved_revision == recipe.latest_revision

        recipe.revise(name="new")
        assert recipe.is_approved
        assert recipe.approved_revision != recipe.latest_revision
Beispiel #9
0
    def test_reject(self, mocker):
        u = UserFactory()
        req = ApprovalRequestFactory()
        mocker.patch.object(req, "verify_approver")

        req.reject(u, "r-")
        assert not req.approved
        assert req.approver == u
        assert req.comment == "r-"
        req.verify_approver.assert_called_with(u)

        recipe = req.revision.recipe
        assert not recipe.is_approved
Beispiel #10
0
    def test_verify_approver_enforced(self, settings, mocker):
        settings.PEER_APPROVAL_ENFORCED = True

        creator = UserFactory()
        user = UserFactory()
        req = ApprovalRequestFactory(creator=creator)

        # Do not raise when creator and approver are different
        req.verify_approver(user)

        # Raise when creator and approver are the same
        with pytest.raises(req.CannotActOnOwnRequest):
            req.verify_approver(creator)
Beispiel #11
0
    def test_reject(self, mocker):
        u = UserFactory()
        req = ApprovalRequestFactory()
        mocker.patch.object(req, "verify_approver")

        req.reject(u, "r-")
        assert not req.approved
        assert req.approver == u
        assert req.comment == "r-"
        req.verify_approver.assert_called_with(u)

        recipe = req.revision.recipe
        assert not recipe.is_approved
Beispiel #12
0
    def test_approve(self, mocker):
        u = UserFactory()
        req = ApprovalRequestFactory()
        mocker.patch.object(req, 'verify_approver')

        req.approve(u, 'r+')
        assert req.approved
        assert req.approver == u
        assert req.comment == 'r+'
        req.verify_approver.assert_called_with(u)

        recipe = req.revision.recipe
        assert recipe.is_approved
Beispiel #13
0
 def test_cannot_open_second_approval_request(self, api_client):
     recipe = RecipeFactory()
     ApprovalRequestFactory(revision=recipe.latest_revision)
     res = api_client.post(
         '/api/v1/recipe_revision/{}/request_approval/'.format(
             recipe.latest_revision.id))
     assert res.status_code == 400
Beispiel #14
0
    def test_it_works(self, rf):
        recipe = RecipeFactory(arguments={"foo": "bar"})
        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        action = recipe.action
        serializer = RecipeSerializer(recipe, context={"request": rf.get("/")})

        assert serializer.data == {
            "name": recipe.name,
            "id": recipe.id,
            "last_updated": Whatever(),
            "enabled": recipe.enabled,
            "extra_filter_expression": recipe.extra_filter_expression,
            "filter_expression": recipe.filter_expression,
            "revision_id": recipe.revision_id,
            "action": action.name,
            "arguments": {
                "foo": "bar"
            },
            "is_approved": False,
            "latest_revision_id": recipe.latest_revision.id,
            "approved_revision_id": None,
            "approval_request": {
                "id": approval.id,
                "created": Whatever(),
                "creator": Whatever(),
                "approved": None,
                "approver": None,
                "comment": None,
            },
            "identicon_seed": Whatever.startswith("v1:"),
            "capabilities": sorted(recipe.capabilities),
        }
Beispiel #15
0
    def test_delete_pending_approval_request_on_revise(self):
        recipe = RecipeFactory(name="old")
        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        recipe.revise(name="new")

        with pytest.raises(ApprovalRequest.DoesNotExist):
            ApprovalRequest.objects.get(pk=approval.pk)
Beispiel #16
0
 def test_reject_no_comment(self, api_client):
     r = RecipeFactory()
     a = ApprovalRequestFactory(revision=r.latest_revision)
     res = api_client.post('/api/v1/approval_request/{}/reject/'.format(
         a.id))
     assert res.status_code == 400
     assert res.data['comment'] == 'This field is required.'
Beispiel #17
0
    def test_current_revision_property(self):
        """Ensure current revision properties work as expected."""
        recipe = RecipeFactory(name="first")
        assert recipe.name == "first"

        recipe.revise(name="second")
        assert recipe.name == "second"

        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        approval.approve(UserFactory(), "r+")
        assert recipe.name == "second"

        # When `revise` is called on a recipe with at least one approved revision, the new revision
        # is treated as a draft and as such the `name` property of the recipe should return the
        # `name` from the `approved_revision` not the `latest_revision`.
        recipe.revise(name="third")
        assert recipe.latest_revision.name == "third"  # The latest revision ("draft") is updated
        assert recipe.name == "second"  # The current revision is unchanged
Beispiel #18
0
    def test_close(self, api_client):
        r = RecipeFactory()
        a = ApprovalRequestFactory(revision=r.latest_revision)
        res = api_client.post('/api/v1/approval_request/{}/close/'.format(
            a.id))
        assert res.status_code == 204

        with pytest.raises(ApprovalRequest.DoesNotExist):
            ApprovalRequest.objects.get(pk=a.pk)
Beispiel #19
0
    def test_approval_status(self):
        recipe = RecipeFactory()
        revision = recipe.latest_revision
        assert revision.approval_status is None

        approval = ApprovalRequestFactory(revision=revision)
        revision = RecipeRevision.objects.get(pk=revision.pk)
        assert revision.approval_status == revision.PENDING

        approval.approve(UserFactory(), "r+")
        revision = RecipeRevision.objects.get(pk=revision.pk)
        assert revision.approval_status == revision.APPROVED

        approval.delete()
        approval = ApprovalRequestFactory(revision=revision)
        approval.reject(UserFactory(), "r-")
        revision = RecipeRevision.objects.get(pk=revision.pk)
        assert revision.approval_status == revision.REJECTED
Beispiel #20
0
    def test_current_revision_property(self):
        """Ensure current revision properties work as expected."""
        recipe = RecipeFactory(name="first")
        assert recipe.name == "first"

        recipe.revise(name="second")
        assert recipe.name == "second"

        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        approval.approve(UserFactory(), "r+")
        assert recipe.name == "second"

        # When `revise` is called on a recipe with at least one approved revision, the new revision
        # is treated as a draft and as such the `name` property of the recipe should return the
        # `name` from the `approved_revision` not the `latest_revision`.
        recipe.revise(name="third")
        assert recipe.latest_revision.name == "third"  # The latest revision ("draft") is updated
        assert recipe.name == "second"  # The current revision is unchanged
Beispiel #21
0
    def test_approve(self, api_client):
        r = RecipeFactory()
        a = ApprovalRequestFactory(revision=r.latest_revision)
        res = api_client.post(
            '/api/v1/approval_request/{}/approve/'.format(a.id),
            {'comment': 'r+'})
        assert res.status_code == 200

        r.refresh_from_db()
        assert r.is_approved
        assert r.approved_revision.approval_request.comment == 'r+'
Beispiel #22
0
    def test_reject(self, api_client):
        r = RecipeFactory()
        a = ApprovalRequestFactory(revision=r.latest_revision)
        res = api_client.post(
            '/api/v1/approval_request/{}/reject/'.format(a.id),
            {'comment': 'r-'})
        assert res.status_code == 200

        r.latest_revision.approval_request.refresh_from_db()
        assert r.latest_revision.approval_status == r.latest_revision.REJECTED
        assert r.latest_revision.approval_request.comment == 'r-'
Beispiel #23
0
    def test_cannot_reject_already_rejected(self):
        u = UserFactory()
        req = ApprovalRequestFactory()
        req.reject(u, "r-")

        with pytest.raises(req.NotActionable):
            req.reject(u, "r-")
Beispiel #24
0
    def test_cannot_approve_already_approved(self):
        u = UserFactory()
        req = ApprovalRequestFactory()
        req.approve(u, "r+")

        with pytest.raises(req.NotActionable):
            req.approve(u, "r+")
Beispiel #25
0
    def test_approval_request_property(self):
        # Make sure it works when there is no approval request
        recipe = RecipeFactory(name="old")
        assert recipe.approval_request is None

        # Make sure it returns an approval request if it exists
        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        assert recipe.approval_request == approval

        # Check the edge case where there is no latest_revision
        recipe.latest_revision.delete()
        recipe.refresh_from_db()
        assert recipe.approval_request is None
Beispiel #26
0
    def test_verify_approver_unenforced(self, settings, mocker):
        logger = mocker.patch("normandy.recipes.models.logger")
        settings.PEER_APPROVAL_ENFORCED = False

        creator = UserFactory()
        user = UserFactory()
        req = ApprovalRequestFactory(creator=creator)

        # Do not raise when creator and approver are different
        req.verify_approver(user)

        # Do not raise when creator and approver are the same since enforcement
        # is disabled.
        req.verify_approver(creator)
        logger.warning.assert_called_with(
            Whatever(),
            extra={
                "code": WARNING_BYPASSING_PEER_APPROVAL,
                "approval_id": req.id,
                "approver": creator,
            },
        )
Beispiel #27
0
 def test_resolve_approval_request_by_id(self, gql_client):
     a = ApprovalRequestFactory()
     res = gql_client.execute(GQ().query.approvalRequest(id=a.id).fields(
         GQ().revision.fields("id")))
     assert res == {
         "data": {
             "approvalRequest": {
                 "revision": {
                     "id": str(a.revision.id)
                 }
             }
         }
     }
Beispiel #28
0
    def test_approval_status(self):
        recipe = RecipeFactory()
        revision = recipe.latest_revision
        assert revision.approval_status is None

        approval = ApprovalRequestFactory(revision=revision)
        revision = RecipeRevision.objects.get(pk=revision.pk)
        assert revision.approval_status == revision.PENDING

        approval.approve(UserFactory(), "r+")
        revision = RecipeRevision.objects.get(pk=revision.pk)
        assert revision.approval_status == revision.APPROVED

        approval.delete()
        approval = ApprovalRequestFactory(revision=revision)
        approval.reject(UserFactory(), "r-")
        revision = RecipeRevision.objects.get(pk=revision.pk)
        assert revision.approval_status == revision.REJECTED
Beispiel #29
0
    def test_verify_approver_enforced(self, settings, mocker):
        settings.PEER_APPROVAL_ENFORCED = True

        creator = UserFactory()
        user = UserFactory()
        req = ApprovalRequestFactory(creator=creator)

        # Do not raise when creator and approver are different
        req.verify_approver(user)

        # Raise when creator and approver are the same
        with pytest.raises(req.CannotActOnOwnRequest):
            req.verify_approver(creator)
Beispiel #30
0
    def test_it_works(self, rf):
        channel = ChannelFactory()
        country = CountryFactory()
        locale = LocaleFactory()
        recipe = RecipeFactory(arguments={'foo': 'bar'},
                               channels=[channel],
                               countries=[country],
                               locales=[locale])
        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        action = recipe.action
        serializer = RecipeSerializer(recipe, context={'request': rf.get('/')})

        assert serializer.data == {
            'name': recipe.name,
            'id': recipe.id,
            'last_updated': Whatever(),
            'enabled': recipe.enabled,
            'extra_filter_expression': recipe.extra_filter_expression,
            'filter_expression': recipe.filter_expression,
            'action': {
                'arguments_schema': {},
                'id': action.id,
                'implementation_url': Whatever(),
                'name': action.name,
            },
            'arguments': {
                'foo': 'bar',
            },
            'channels': [channel.slug],
            'countries': [country.code],
            'locales': [locale.code],
            'is_approved': False,
            'latest_revision':
            RecipeRevisionSerializer(recipe.latest_revision).data,
            'approved_revision': None,
            'approval_request': {
                'id': approval.id,
                'created': Whatever(),
                'creator': Whatever(),
                'approved': None,
                'approver': None,
                'comment': None,
            },
        }
Beispiel #31
0
    def test_it_works(self, rf):
        recipe = RecipeFactory(arguments={"foo": "bar"}, bug_number=1436113)
        ApprovalRequestFactory(revision=recipe.latest_revision)
        request = rf.get("/")
        serializer = RecipeSerializer(recipe, context={"request": rf.get("/")})

        assert serializer.data == {
            "id":
            recipe.id,
            "latest_revision":
            RecipeRevisionSerializer(recipe.latest_revision,
                                     context={
                                         "request": request
                                     }).data,
            "approved_revision":
            None,
            "signature":
            None,
        }
Beispiel #32
0
    def test_it_works(self, rf):
        recipe = RecipeFactory(arguments={"foo": "bar"},
                               experimenter_slug="some-experimenter-slug")
        ApprovalRequestFactory(revision=recipe.latest_revision)
        request = rf.get("/")
        serializer = RecipeSerializer(recipe, context={"request": rf.get("/")})

        assert serializer.data == {
            "id":
            recipe.id,
            "latest_revision":
            RecipeRevisionSerializer(recipe.latest_revision,
                                     context={
                                         "request": request
                                     }).data,
            "approved_revision":
            None,
            "signature":
            None,
            "uses_only_baseline_capabilities":
            False,
        }
Beispiel #33
0
    def test_it_works(self, rf):
        recipe = RecipeFactory(arguments={"foo": "bar"},
                               filter_object_json=None)
        approval = ApprovalRequestFactory(revision=recipe.latest_revision)
        action = recipe.action
        serializer = RecipeSerializer(recipe, context={"request": rf.get("/")})

        assert serializer.data == {
            "name": recipe.name,
            "id": recipe.id,
            "last_updated": Whatever(),
            "enabled": recipe.enabled,
            "extra_filter_expression": recipe.extra_filter_expression,
            "filter_expression": recipe.filter_expression,
            "filter_object": [],
            "action": {
                "arguments_schema": {},
                "id": action.id,
                "implementation_url": Whatever(),
                "name": action.name,
            },
            "arguments": {
                "foo": "bar"
            },
            "is_approved": False,
            "latest_revision":
            RecipeRevisionSerializer(recipe.latest_revision).data,
            "approved_revision": None,
            "approval_request": {
                "id": approval.id,
                "created": Whatever(),
                "creator": Whatever(),
                "approved": None,
                "approver": None,
                "comment": None,
            },
            "identicon_seed": Whatever.startswith("v1:"),
        }
Beispiel #34
0
    def test_verify_approver_unenforced(self, settings, mocker):
        logger = mocker.patch('normandy.recipes.models.logger')
        settings.PEER_APPROVAL_ENFORCED = False

        creator = UserFactory()
        user = UserFactory()
        req = ApprovalRequestFactory(creator=creator)

        # Do not raise when creator and approver are different
        req.verify_approver(user)

        # Do not raise when creator and approver are the same since enforcement
        # is disabled.
        req.verify_approver(creator)
        logger.warning.assert_called_with(Whatever(),
                                          extra={
                                              'code':
                                              WARNING_BYPASSING_PEER_APPROVAL,
                                              'approval_id': req.id,
                                              'approver': creator,
                                          })
Beispiel #35
0
 def test_resolve_all_approval_requests(self, gql_client):
     a = ApprovalRequestFactory()
     res = gql_client.execute(GQ().query.allApprovalRequests.fields("id"))
     assert res == {"data": {"allApprovalRequests": [{"id": str(a.id)}]}}
Beispiel #36
0
    def test_can_save_open_request(self):
        request = ApprovalRequestFactory(active=True)

        # Should be able to call save without an integrity error
        request.save()