Beispiel #1
0
    def test_batch_does_not_raise_exception_if_batch_4xx_errors_are_ignored(self):
        error = {
            "errno": 121,
            "message": "Forbidden",
            "code": 403,
            "error": "This user cannot access this resource."
        }
        self.session.request.side_effect = [
            ({"settings": {"batch_max_requests": 25}}, []),
            ({"responses": [
                {"status": 200, "path": "/url1", "body": {}, "headers": {}},
                {"status": 403, "path": "/url2", "body": error, "headers": {}}
            ]}, [])]

        client = Client(session=self.session, ignore_batch_4xx=True)
        with client.batch(bucket='moz', collection='test') as batch:  # Do not raise
            batch.create_record(id=1234, data={'foo': 'bar'})
            batch.create_record(id=5678, data={'tutu': 'toto'})
Beispiel #2
0
class FunctionalTest(unittest.TestCase):
    def setUp(self):
        super().setUp()
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, "config/kinto.ini"))
        self.client = Client(server_url=self.server_url, auth=self.auth)
        self.create_user(self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, "/__flush__")
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def create_user(self, credentials):
        account_url = urljoin(self.server_url,
                              "/accounts/{}".format(credentials[0]))
        r = requests.put(account_url,
                         json={"data": {
                             "password": credentials[1]
                         }},
                         auth=DEFAULT_AUTH)
        r.raise_for_status()
        return r.json()

    def get_user_id(self, credentials):
        r = self.create_user(credentials)
        return "account:{}".format(r["data"]["id"])

    def test_bucket_creation(self):
        bucket = self.client.create_bucket(id="mozilla")
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket["permissions"]["write"]

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket(id="mozilla")
        # Should not raise.
        self.client.create_bucket(id="mozilla", if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket(id="mozilla")
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket(id="mozilla")
        self.client.get_bucket(id="mozilla")
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket(id="mozilla", data={"version": 1})
        assert bucket["data"]["version"] == 1
        bucket = self.client.patch_bucket(id="mozilla", data={"author": "you"})
        assert bucket["data"]["version"] == 1
        assert bucket["data"]["author"] == "you"
        bucket = self.client.update_bucket(id="mozilla",
                                           data={"date": "today"})
        assert bucket["data"]["date"] == "today"
        assert "version" not in bucket["data"]

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound,
                          self.client.get_bucket,
                          id="non-existent")

    def test_bucket_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.delete_bucket(id="mozilla")
        self.assertRaises(BucketNotFound, self.client.get_bucket, id="mozilla")

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.delete_bucket(id="mozilla")
        self.client.delete_bucket(id="mozilla", if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket(id="mozilla")
        buckets = self.client.delete_buckets()
        assert buckets[0]["id"] == "mozilla"
        self.assertRaises(BucketNotFound, self.client.get_bucket, id="mozilla")

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket(id="mozilla",
                                  permissions={"write": ["account:alexis"]})
        bucket = self.client.get_bucket(id="mozilla")
        assert "account:alexis" in bucket["permissions"]["write"]

    def test_group_creation(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(
            id="payments",
            bucket="mozilla",
            data={"members": ["blah"]},
            permissions={"write": ["blah"]},
        )
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group(id="payments", bucket="mozilla")
        assert "blah" in group["permissions"]["write"]

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="payments",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.create_group(
            id="payments",
            bucket="mozilla",
            data={"members": ["blah"]},
            permissions={"write": ["blah"]},
            if_not_exists=True,
        )

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException) as e:
            self.client.create_group(id="payments",
                                     bucket="mozilla",
                                     data={"members": ["blah"]})
        assert str(e).endswith(
            "PUT /v1/buckets/mozilla/groups/payments - "
            "403 Unauthorized. Please check that the "
            "bucket exists and that you have the permission "
            "to create or write on this group.")

    def test_group_update(self):
        self.client.create_bucket(id="mozilla")
        group = self.client.create_group(id="payments",
                                         bucket="mozilla",
                                         data={"members": ["blah"]},
                                         if_not_exists=True)
        assert group["data"]["members"][0] == "blah"
        group = self.client.update_group(data={"members": ["blah", "foo"]},
                                         id="payments",
                                         bucket="mozilla")
        self.assertEqual(group["data"]["members"][1], "foo")

    def test_group_list(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="receipts",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.create_group(id="assets",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        # The returned groups should be strings.
        groups = self.client.get_groups(bucket="mozilla")
        self.assertEqual(2, len(groups))
        self.assertEqual(set([coll["id"] for coll in groups]),
                         set(["receipts", "assets"]))

    def test_group_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="payments",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.delete_group(id="payments", bucket="mozilla")
        assert len(self.client.get_groups(bucket="mozilla")) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="payments",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.delete_group(id="payments", bucket="mozilla")
        self.client.delete_group(id="payments",
                                 bucket="mozilla",
                                 if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               "request",
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group(id="payments",
                                         bucket="mozilla",
                                         if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_group(id="amo",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.create_group(id="blocklist",
                                 bucket="mozilla",
                                 data={"members": ["blah"]})
        self.client.delete_groups(bucket="mozilla")
        assert len(self.client.get_groups(bucket="mozilla")) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket(id="mozilla")
        deleted_groups = self.client.delete_groups(bucket="mozilla")
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(
            id="payments",
            bucket="mozilla",
            permissions={"write": ["account:alexis"]})

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection(id="payments",
                                                bucket="mozilla")
        assert "account:alexis" in collection["permissions"]["write"]

    def test_collection_not_found(self):
        self.client.create_bucket(id="mozilla")

        with pytest.raises(CollectionNotFound):
            self.client.get_collection(id="payments", bucket="mozilla")

    def test_collection_access_forbidden(self):
        with pytest.raises(KintoException):
            self.client.get_collection(id="payments", bucket="mozilla")

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="payments", bucket="mozilla")
        # Should not raise.
        self.client.create_collection(id="payments",
                                      bucket="mozilla",
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="receipts", bucket="mozilla")
        self.client.create_collection(id="assets", bucket="mozilla")

        # The returned collections should be strings.
        collections = self.client.get_collections(bucket="mozilla")
        self.assertEqual(len(collections), 2)

        self.assertEqual(set([coll["id"] for coll in collections]),
                         set(["receipts", "assets"]))

    def test_collection_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="payments", bucket="mozilla")
        self.client.delete_collection(id="payments", bucket="mozilla")
        assert len(self.client.get_collections(bucket="mozilla")) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="payments", bucket="mozilla")
        self.client.delete_collection(id="payments", bucket="mozilla")
        self.client.delete_collection(id="payments",
                                      bucket="mozilla",
                                      if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               "request",
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection(id="payments",
                                              bucket="mozilla",
                                              if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket(id="mozilla")
        self.client.create_collection(id="amo", bucket="mozilla")
        self.client.create_collection(id="blocklist", bucket="mozilla")
        self.client.delete_collections(bucket="mozilla")
        assert len(self.client.get_collections(bucket="mozilla")) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket(id="mozilla")
        deleted_collections = self.client.delete_collections(bucket="mozilla")
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})
        record = client.get_record(id=created["data"]["id"])
        assert "account:alexis" in record["permissions"]["read"]

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        client.create_record(data={"foo": "bar"},
                             permissions={"read": ["account:alexis"]})
        records = client.get_records()
        assert len(records) == 1

    def test_records_timestamp_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={"foo": "bar"},
                                      permissions={"read": ["account:alexis"]})
        etag = client.get_records_timestamp()
        assert str(etag) == str(record["data"]["last_modified"])

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={"foo": "bar"},
                                 permissions={"read": ["account:alexis"]})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_records_generator_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={"foo": "bar"},
                                 permissions={"read": ["account:alexis"]})

        pages = list(client.get_paginated_records())

        assert len(pages) == 2

    def test_single_record_save(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})
        created["data"]["bar"] = "baz"

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created["data"]["id"], data=created["data"])

        retrieved = client.get_record(id=created["data"]["id"])
        assert "account:alexis" in retrieved["permissions"]["read"]
        assert retrieved["data"]["foo"] == u"bar"
        assert retrieved["data"]["bar"] == u"baz"
        assert created["data"]["id"] == retrieved["data"]["id"]

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={
                "id": created["data"]["id"],
                "bar": "baz"
            })

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={"foo": "bar"})
        client.create_record(data={
            "id": created["data"]["id"],
            "bar": "baz"
        },
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        created = client.create_record(
            data={"foo": "bar"}, permissions={"read": ["account:alexis"]})

        client.create_record(data={
            "id": created["data"]["id"],
            "bar": "baz"
        },
                             safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={"foo": "bar"})
        deleted = client.delete_record(id=record["data"]["id"])
        assert deleted["deleted"] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={"foo": "bar"})
        deleted = client.delete_record(id=record["data"]["id"])
        deleted_if_exists = client.delete_record(id=record["data"]["id"],
                                                 if_exists=True)
        assert deleted["deleted"] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        client.create_record(data={"foo": "bar"})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ("alice", "p4ssw0rd")
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket(id="shared-bucket",
                                  permissions={"read": [alice_userid]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket(id="shared-bucket")

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla")
        client.create_bucket()
        client.create_group(id="payments", data={"members": []})
        client.patch_group(id="payments", data={"secret": "psssssst!"})
        group = client.get_group(id="payments")
        assert group["data"]["secret"] == "psssssst!"

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="mozilla",
                        collection="payments")
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={"secret": "psssssst!"})
        collection = client.get_collection()
        assert collection["data"]["secret"] == "psssssst!"

    def test_collection_sharing(self):
        alice_credentials = ("alice", "p4ssw0rd")
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket(id="bob-bucket")
        self.client.create_collection(id="shared",
                                      bucket="bob-bucket",
                                      permissions={"read": [alice_userid]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection(id="shared", bucket="bob-bucket")

    def test_record_sharing(self):
        alice_credentials = ("alice", "p4ssw0rd")
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket(id="bob-bucket")
        self.client.create_collection(id="bob-personal-collection",
                                      bucket="bob-bucket")
        record = self.client.create_record(
            data={"foo": "bar"},
            permissions={"read": [alice_userid]},
            bucket="bob-bucket",
            collection="bob-personal-collection",
        )

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(id=record["data"]["id"],
                                         bucket="bob-bucket",
                                         collection="bob-personal-collection")

        assert record["data"]["foo"] == "bar"

    def test_request_batching(self):
        with self.client.batch(bucket="mozilla", collection="fonts") as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={"foo": "bar"},
                                permissions={"read": ["natim"]})
            batch.create_record(data={"bar": "baz"},
                                permissions={"read": ["account:alexis"]})

        _, _, r1, r2 = batch.results()
        records = self.client.get_records(bucket="mozilla", collection="fonts")

        assert len(records) == 2
        assert records[0] == r2["data"]
        assert records[1] == r1["data"]

    def test_patch_record_jsonpatch(self):
        self.client.create_bucket(id="b1")
        self.client.create_collection(id="c1", bucket="b1")
        self.client.create_record(id="r1",
                                  collection="c1",
                                  bucket="b1",
                                  data={"hello": "world"})
        patch = JSONPatch([
            {
                "op": "add",
                "path": "/data/goodnight",
                "value": "moon"
            },
            {
                "op": "add",
                "path": "/permissions/read/alice"
            },
        ])
        self.client.patch_record(id="r1",
                                 collection="c1",
                                 bucket="b1",
                                 changes=patch)
        record = self.client.get_record(bucket="b1", collection="c1", id="r1")
        assert record["data"]["hello"] == "world"
        assert record["data"]["goodnight"] == "moon"
        assert record["permissions"]["read"] == ["alice"]

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket="origin", collection="coll") as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={"foo": "bar", "n": n})

        origin = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket="origin",
                        collection="coll")
        destination = Client(server_url=self.server_url,
                             auth=self.auth,
                             bucket="destination",
                             collection="coll")

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket="destination",
                                          collection="coll")
        assert len(records) == 10
Beispiel #3
0
class FunctionalTest(unittest.TestCase):

    def setUp(self):
        super().setUp()
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, 'config/kinto.ini'))
        self.client = Client(server_url=self.server_url, auth=self.auth)
        self.create_user(self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, '/__flush__')
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def create_user(self, credentials):
        account_url = urljoin(self.server_url, '/accounts/{}'.format(credentials[0]))
        r = requests.put(account_url, json={"data": {"password": credentials[1]}},
                         auth=DEFAULT_AUTH)
        r.raise_for_status()
        return r.json()

    def get_user_id(self, credentials):
        r = self.create_user(credentials)
        return 'account:{}'.format(r["data"]["id"])

    def test_bucket_creation(self):
        bucket = self.client.create_bucket(id='mozilla')
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket['permissions']['write']

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket(id='mozilla')
        # Should not raise.
        self.client.create_bucket(id='mozilla', if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket(id='mozilla')
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket(id='mozilla')
        self.client.get_bucket(id='mozilla')
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket(id='mozilla', data={'version': 1})
        assert bucket['data']['version'] == 1
        bucket = self.client.patch_bucket(id='mozilla', data={'author': 'you'})
        assert bucket['data']['version'] == 1
        assert bucket['data']['author'] == 'you'
        bucket = self.client.update_bucket(id='mozilla', data={'date': 'today'})
        assert bucket['data']['date'] == 'today'
        assert 'version' not in bucket['data']

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound, self.client.get_bucket, id='non-existent')

    def test_bucket_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.delete_bucket(id='mozilla')
        self.assertRaises(BucketNotFound, self.client.get_bucket, id='mozilla')

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.delete_bucket(id='mozilla')
        self.client.delete_bucket(id='mozilla', if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket(id='mozilla')
        buckets = self.client.delete_buckets()
        assert buckets[0]['id'] == 'mozilla'
        self.assertRaises(BucketNotFound, self.client.get_bucket, id='mozilla')

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket(id='mozilla', permissions={'write': ['account:alexis']})
        bucket = self.client.get_bucket(id='mozilla')
        assert 'account:alexis' in bucket['permissions']['write']

    def test_group_creation(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(
            id='payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]})
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group(id='payments', bucket='mozilla')
        assert 'blah' in group['permissions']['write']

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(
            id='payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]},
            if_not_exists=True)

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException) as e:
            self.client.create_group(
                id='payments', bucket='mozilla',
                data={'members': ['blah']})
        assert str(e).endswith('PUT /v1/buckets/mozilla/groups/payments - '
                               '403 Unauthorized. Please check that the '
                               'bucket exists and that you have the permission '
                               'to create or write on this group.')

    def test_group_update(self):
        self.client.create_bucket(id='mozilla')
        group = self.client.create_group(
                    id='payments', bucket='mozilla',
                    data={'members': ['blah', ]},
                    if_not_exists=True)
        assert group['data']['members'][0] == 'blah'
        group = self.client.update_group(
                    data={'members': ['blah', 'foo']},
                    id='payments', bucket='mozilla')
        self.assertEqual(group['data']['members'][1], 'foo')

    def test_group_list(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='receipts', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(id='assets', bucket='mozilla', data={'members': ['blah', ]})
        # The returned groups should be strings.
        groups = self.client.get_groups(bucket='mozilla')
        self.assertEqual(2, len(groups))
        self.assertEqual(set([coll['id'] for coll in groups]),
                         set(['receipts', 'assets']))

    def test_group_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group(id='payments', bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group(id='payments', bucket='mozilla')
        self.client.delete_group(id='payments', bucket='mozilla', if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group(id='payments', bucket='mozilla', if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_group(id='amo', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(id='blocklist', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_groups(bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket(id='mozilla')
        deleted_groups = self.client.delete_groups(bucket='mozilla')
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(
            id='payments', bucket='mozilla',
            permissions={'write': ['account:alexis', ]}
        )

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection(id='payments', bucket='mozilla')
        assert 'account:alexis' in collection['permissions']['write']

    def test_collection_not_found(self):
        self.client.create_bucket(id='mozilla')

        with pytest.raises(CollectionNotFound):
            self.client.get_collection(id='payments', bucket='mozilla')

    def test_collection_access_forbidden(self):
        with pytest.raises(KintoException):
            self.client.get_collection(id='payments', bucket='mozilla')

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='payments', bucket='mozilla')
        # Should not raise.
        self.client.create_collection(id='payments', bucket='mozilla',
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='receipts', bucket='mozilla')
        self.client.create_collection(id='assets', bucket='mozilla')

        # The returned collections should be strings.
        collections = self.client.get_collections(bucket='mozilla')
        self.assertEqual(len(collections), 2)

        self.assertEqual(set([coll['id'] for coll in collections]),
                         set(['receipts', 'assets']))

    def test_collection_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='payments', bucket='mozilla')
        self.client.delete_collection(id='payments', bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='payments', bucket='mozilla')
        self.client.delete_collection(id='payments', bucket='mozilla')
        self.client.delete_collection(id='payments', bucket='mozilla', if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection(id='payments', bucket='mozilla', if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket(id='mozilla')
        self.client.create_collection(id='amo', bucket='mozilla')
        self.client.create_collection(id='blocklist', bucket='mozilla')
        self.client.delete_collections(bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket(id='mozilla')
        deleted_collections = self.client.delete_collections(bucket='mozilla')
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})
        record = client.get_record(id=created['data']['id'])
        assert 'account:alexis' in record['permissions']['read']

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'},
                             permissions={'read': ['account:alexis']})
        records = client.get_records()
        assert len(records) == 1

    def test_records_timestamp_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={'foo': 'bar'},
                                      permissions={'read': ['account:alexis']})
        etag = client.get_records_timestamp()
        assert str(etag) == str(record["data"]["last_modified"])

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={'foo': 'bar'},
                                 permissions={'read': ['account:alexis']})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_single_record_save(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})
        created['data']['bar'] = 'baz'

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created['data']['id'], data=created['data'])

        retrieved = client.get_record(id=created['data']['id'])
        assert 'account:alexis' in retrieved['permissions']['read']
        assert retrieved['data']['foo'] == u'bar'
        assert retrieved['data']['bar'] == u'baz'
        assert created['data']['id'] == retrieved['data']['id']

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={'id': created['data']['id'], 'bar': 'baz'})

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'})
        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'},
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['account:alexis']})

        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'}, safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={'foo': 'bar'})
        deleted = client.delete_record(id=record['data']['id'])
        assert deleted['deleted'] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record(data={'foo': 'bar'})
        deleted = client.delete_record(id=record['data']['id'])
        deleted_if_exists = client.delete_record(id=record['data']['id'], if_exists=True)
        assert deleted['deleted'] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket(id='shared-bucket',
                                  permissions={'read': [alice_userid, ]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket(id='shared-bucket')

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla')
        client.create_bucket()
        client.create_group(id='payments', data={'members': []})
        client.patch_group(id='payments', data={'secret': 'psssssst!'})
        group = client.get_group(id='payments')
        assert group['data']['secret'] == 'psssssst!'

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={'secret': 'psssssst!'})
        collection = client.get_collection()
        assert collection['data']['secret'] == 'psssssst!'

    def test_collection_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket(id='bob-bucket')
        self.client.create_collection(
            id='shared',
            bucket='bob-bucket',
            permissions={'read': [alice_userid, ]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection(id='shared', bucket='bob-bucket')

    def test_record_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket(id='bob-bucket')
        self.client.create_collection(id='bob-personal-collection',
                                      bucket='bob-bucket')
        record = self.client.create_record(
            data={'foo': 'bar'},
            permissions={'read': [alice_userid, ]},
            bucket='bob-bucket',
            collection='bob-personal-collection')

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(
            id=record['data']['id'],
            bucket='bob-bucket',
            collection='bob-personal-collection')

        assert record['data']['foo'] == 'bar'

    def test_request_batching(self):
        with self.client.batch(bucket='mozilla', collection='fonts') as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={'foo': 'bar'},
                                permissions={'read': ['natim']})
            batch.create_record(data={'bar': 'baz'},
                                permissions={'read': ['account:alexis']})

        _, _, r1, r2 = batch.results()
        records = self.client.get_records(bucket='mozilla', collection='fonts')

        assert len(records) == 2
        assert records[0] == r2['data']
        assert records[1] == r1['data']

    def test_patch_record_jsonpatch(self):
        self.client.create_bucket(id='b1')
        self.client.create_collection(id='c1', bucket='b1')
        self.client.create_record(id='r1', collection='c1', bucket='b1',
                                  data={"hello": "world"})
        patch = JSONPatch([
            {'op': 'add', 'path': '/data/goodnight', 'value': 'moon'},
            {'op': 'add', 'path': '/permissions/read/alice'}
        ])
        self.client.patch_record(id='r1', collection='c1', bucket='b1',
                                 changes=patch)
        record = self.client.get_record(bucket='b1', collection='c1', id='r1')
        assert record['data']['hello'] == 'world'
        assert record['data']['goodnight'] == 'moon'
        assert record['permissions']['read'] == ['alice']

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket='origin', collection='coll') as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={'foo': 'bar', 'n': n})

        origin = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='origin',
            collection='coll'
        )
        destination = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='destination',
            collection='coll')

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket='destination',
                                          collection='coll')
        assert len(records) == 10
Beispiel #4
0
class ClientTest(unittest.TestCase):
    def setUp(self):
        self.session = mock.MagicMock()
        self.client = Client(session=self.session)
        mock_response(self.session)

    def test_server_info(self):
        self.client.server_info()
        self.session.request.assert_called_with('get', '/')

    def test_context_manager_works_as_expected(self):
        settings = {"batch_max_requests": 25}
        self.session.request.side_effect = [({"settings": settings}, []),
                                            ({"responses": []}, [])]

        with self.client.batch(bucket='mozilla', collection='test') as batch:
            batch.create_record(id=1234, data={'foo': 'bar'})
            batch.create_record(id=5678, data={'bar': 'baz'})

        self.session.request.assert_called_with(
            method='POST',
            endpoint='/batch',
            payload={'requests': [
                {'body': {'data': {'foo': 'bar'}},
                 'path': '/buckets/mozilla/collections/test/records/1234',
                 'method': 'PUT',
                 'headers': {'If-None-Match': '*'}},
                {'body': {'data': {'bar': 'baz'}},
                 'path': '/buckets/mozilla/collections/test/records/5678',
                 'method': 'PUT',
                 'headers': {'If-None-Match': '*'}}]})

    def test_batch_raises_exception(self):
        # Make the next call to sess.request raise a 403.
        exception = KintoException()
        exception.response = mock.MagicMock()
        exception.response.status_code = 403
        exception.request = mock.sentinel.request
        self.session.request.side_effect = exception

        with self.assertRaises(KintoException):
            with self.client.batch(bucket='moz', collection='test') as batch:
                batch.create_record(id=1234, data={'foo': 'bar'})

    def test_batch_raises_exception_if_subrequest_failed(self):
        error = {
            "errno": 121,
            "message": "This user cannot access this resource.",
            "code": 403,
            "error": "Forbidden"
        }
        self.session.request.side_effect = [
            ({"settings": {"batch_max_requests": 25}}, []),
            ({"responses": [
                {"status": 200, "path": "/url1", "body": {}, "headers": {}},
                {"status": 404, "path": "/url2", "body": error, "headers": {}}
            ]}, [])]

        with self.assertRaises(KintoException):
            with self.client.batch(bucket='moz', collection='test') as batch:
                batch.create_record(id=1234, data={'foo': 'bar'})
                batch.create_record(id=5678, data={'tutu': 'toto'})

    def test_batch_options_are_transmitted(self):
        settings = {"batch_max_requests": 25}
        self.session.request.side_effect = [({"settings": settings}, [])]
        with mock.patch('kinto_http.create_session') as create_session:
            with self.client.batch(bucket='moz', collection='test', retry=12,
                                   retry_after=20):
                _, last_call_kwargs = create_session.call_args_list[-1]
                self.assertEqual(last_call_kwargs['retry'], 12)
                self.assertEqual(last_call_kwargs['retry_after'], 20)

    def test_client_is_represented_properly(self):
        client = Client(
            server_url="https://kinto.notmyidea.org/v1",
            bucket="homebrewing",
            collection="recipes"
        )
        expected_repr = ("<KintoClient https://kinto.notmyidea.org/v1/"
                         "buckets/homebrewing/collections/recipes>")
        assert str(client) == expected_repr

    def test_client_uses_default_bucket_if_not_specified(self):
        mock_response(self.session)
        client = Client(session=self.session)
        client.get_bucket()
        self.session.request.assert_called_with('get', '/buckets/default')

    def test_client_uses_passed_bucket_if_specified(self):
        client = Client(
            server_url="https://kinto.notmyidea.org/v1",
            bucket="buck")
        assert client._bucket_name == "buck"

    def test_client_clone_with_auth(self):
        client_clone = self.client.clone(auth=("reviewer", ""))
        assert client_clone.session.auth == ("reviewer", "")
        assert self.client.session != client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_server_url(self):
        client_clone = self.client.clone(server_url="https://kinto.notmyidea.org/v1")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_new_session(self):
        session = create_session(auth=("reviewer", ""),
                                 server_url="https://kinto.notmyidea.org/v1")
        client_clone = self.client.clone(session=session)
        assert client_clone.session == session
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_auth_and_server_url(self):
        client_clone = self.client.clone(auth=("reviewer", ""),
                                         server_url="https://kinto.notmyidea.org/v1")
        assert client_clone.session.auth == ("reviewer", "")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_existing_session(self):
        client_clone = self.client.clone(session=self.client.session)
        assert self.client.session == client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_new_bucket_and_collection(self):
        client_clone = self.client.clone(bucket="bucket_blah", collection="coll_blah")
        assert self.client.session == client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name != client_clone._bucket_name
        assert self.client._collection_name != client_clone._collection_name
        assert client_clone._bucket_name == "bucket_blah"
        assert client_clone._collection_name == "coll_blah"

    def test_client_clone_with_auth_and_server_url_bucket_and_collection(self):
        client_clone = self.client.clone(auth=("reviewer", ""),
                                         server_url="https://kinto.notmyidea.org/v1",
                                         bucket="bucket_blah",
                                         collection="coll_blah")
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client._bucket_name != client_clone._bucket_name
        assert self.client._collection_name != client_clone._collection_name
        assert client_clone.session.auth == ("reviewer", "")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert client_clone._bucket_name == "bucket_blah"
        assert client_clone._collection_name == "coll_blah"
Beispiel #5
0
class ClientTest(unittest.TestCase):
    def setUp(self):
        self.session = mock.MagicMock()
        self.client = Client(session=self.session)
        mock_response(self.session)

    def test_server_info(self):
        self.client.server_info()
        self.session.request.assert_called_with('get', '/')

    def test_context_manager_works_as_expected(self):
        settings = {"batch_max_requests": 25}
        self.session.request.side_effect = [({"settings": settings}, []),
                                            ({"responses": []}, [])]

        with self.client.batch(bucket='mozilla', collection='test') as batch:
            batch.create_record(id=1234, data={'foo': 'bar'})
            batch.create_record(id=5678, data={'bar': 'baz'})
            batch.patch_record(id=5678, data={'bar': 'biz'})
            changes = JSONPatch([{'op': 'add', 'location': 'foo', 'value': 'bar'}])
            batch.patch_record(id=5678, changes=changes)

        self.session.request.assert_called_with(
            method='POST',
            endpoint='/batch',
            payload={'requests': [
                {'body': {'data': {'foo': 'bar'}},
                 'path': '/buckets/mozilla/collections/test/records/1234',
                 'method': 'PUT',
                 'headers': {'If-None-Match': '*'}},
                {'body': {'data': {'bar': 'baz'}},
                 'path': '/buckets/mozilla/collections/test/records/5678',
                 'method': 'PUT',
                 'headers': {'If-None-Match': '*'}},
                {'body': {'data': {'bar': 'biz'}},
                 'path': '/buckets/mozilla/collections/test/records/5678',
                 'method': 'PATCH',
                 'headers': {'Content-Type': 'application/json'}},
                {'body': [{'op': 'add', 'location': 'foo', 'value': 'bar'}],
                 'path': '/buckets/mozilla/collections/test/records/5678',
                 'method': 'PATCH',
                 'headers': {'Content-Type': 'application/json-patch+json'}}]})

    def test_batch_raises_exception(self):
        # Make the next call to sess.request raise a 403.
        exception = KintoException()
        exception.response = mock.MagicMock()
        exception.response.status_code = 403
        exception.request = mock.sentinel.request
        self.session.request.side_effect = exception

        with self.assertRaises(KintoException):
            with self.client.batch(bucket='moz', collection='test') as batch:
                batch.create_record(id=1234, data={'foo': 'bar'})

    def test_batch_raises_exception_if_subrequest_failed_with_code_5xx(self):
        error = {
            "errno": 121,
            "message": "This user cannot access this resource.",
            "code": 500,
            "error": "Server Internal Error"
        }
        self.session.request.side_effect = [
            ({"settings": {"batch_max_requests": 25}}, []),
            ({"responses": [
                {"status": 200, "path": "/url1", "body": {}, "headers": {}},
                {"status": 500, "path": "/url2", "body": error, "headers": {}}
            ]}, [])]

        with self.assertRaises(KintoException):
            with self.client.batch(bucket='moz', collection='test') as batch:
                batch.create_record(id=1234, data={'foo': 'bar'})
                batch.create_record(id=5678, data={'tutu': 'toto'})

    def test_batch_raises_exception_if_subrequest_failed_with_code_4xx(self):
        error_403 = {
            "errno": 121,
            "message": "Forbidden",
            "code": 403,
            "error": "This user cannot access this resource."
        }
        error_400 = {
            "code": 400,
            "errno": 104,
            "error": "Invalid parameters",
            "message": "Bad Request",
        }
        self.session.request.side_effect = [
            ({"settings": {"batch_max_requests": 25}}, []),
            ({"responses": [
                {"status": 200, "path": "/url1", "body": {}, "headers": {}},
                {"status": 403, "path": "/url2", "body": error_403, "headers": {}},
                {"status": 200, "path": "/url1", "body": {}, "headers": {}},
                {"status": 400, "path": "/url2", "body": error_400, "headers": {}},
            ]}, [])]

        with self.assertRaises(KintoBatchException) as cm:
            with self.client.batch(bucket='moz', collection='test') as batch:
                batch.create_record(id=1234, data={'foo': 'bar'})
                batch.create_record(id=1987, data={'maz': 'miz'})
                batch.create_record(id=1982, data={'plop': 'plip'})
                batch.create_record(id=5678, data={'tutu': 'toto'})

        raised = cm.exception
        assert "403" in str(raised)
        assert "400" in str(raised)
        assert isinstance(raised.exceptions[0], KintoException)
        assert raised.exceptions[0].response.status_code == 403
        assert raised.exceptions[1].response.status_code == 400
        resp, headers = raised.results[0]
        assert len(resp["responses"]) == 4
        assert resp["responses"][0]["status"] == 200

    def test_batch_does_not_raise_exception_if_batch_4xx_errors_are_ignored(self):
        error = {
            "errno": 121,
            "message": "Forbidden",
            "code": 403,
            "error": "This user cannot access this resource."
        }
        self.session.request.side_effect = [
            ({"settings": {"batch_max_requests": 25}}, []),
            ({"responses": [
                {"status": 200, "path": "/url1", "body": {}, "headers": {}},
                {"status": 403, "path": "/url2", "body": error, "headers": {}}
            ]}, [])]

        client = Client(session=self.session, ignore_batch_4xx=True)
        with client.batch(bucket='moz', collection='test') as batch:  # Do not raise
            batch.create_record(id=1234, data={'foo': 'bar'})
            batch.create_record(id=5678, data={'tutu': 'toto'})

    def test_batch_options_are_transmitted(self):
        settings = {"batch_max_requests": 25}
        self.session.request.side_effect = [({"settings": settings}, [])]
        with mock.patch('kinto_http.create_session') as create_session:
            with self.client.batch(bucket='moz', collection='test', retry=12,
                                   retry_after=20):
                _, last_call_kwargs = create_session.call_args_list[-1]
                self.assertEqual(last_call_kwargs['retry'], 12)
                self.assertEqual(last_call_kwargs['retry_after'], 20)

    def test_client_is_represented_properly_with_bucket_and_collection(self):
        client = Client(
            server_url="https://kinto.notmyidea.org/v1",
            bucket="homebrewing",
            collection="recipes"
        )
        expected_repr = ("<KintoClient https://kinto.notmyidea.org/v1/"
                         "buckets/homebrewing/collections/recipes>")
        assert str(client) == expected_repr

    def test_client_is_represented_properly_with_bucket(self):
        client = Client(
            server_url="https://kinto.notmyidea.org/v1",
            bucket="homebrewing",
        )
        expected_repr = ("<KintoClient https://kinto.notmyidea.org/v1/"
                         "buckets/homebrewing>")
        assert str(client) == expected_repr

    def test_client_is_represented_properly_without_bucket(self):
        client = Client(
            server_url="https://kinto.notmyidea.org/v1",
            bucket=None
        )
        expected_repr = ("<KintoClient https://kinto.notmyidea.org/v1/>")
        assert str(client) == expected_repr

    def test_client_uses_default_bucket_if_not_specified(self):
        mock_response(self.session)
        client = Client(session=self.session)
        client.get_bucket()
        self.session.request.assert_called_with('get', '/buckets/default')

    def test_client_uses_passed_bucket_if_specified(self):
        client = Client(
            server_url="https://kinto.notmyidea.org/v1",
            bucket="buck")
        assert client._bucket_name == "buck"

    def test_client_clone_with_auth(self):
        client_clone = self.client.clone(auth=("reviewer", ""))
        assert client_clone.session.auth == ("reviewer", "")
        assert self.client.session != client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_server_url(self):
        client_clone = self.client.clone(server_url="https://kinto.notmyidea.org/v1")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_new_session(self):
        session = create_session(auth=("reviewer", ""),
                                 server_url="https://kinto.notmyidea.org/v1")
        client_clone = self.client.clone(session=session)
        assert client_clone.session == session
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_auth_and_server_url(self):
        client_clone = self.client.clone(auth=("reviewer", ""),
                                         server_url="https://kinto.notmyidea.org/v1")
        assert client_clone.session.auth == ("reviewer", "")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_existing_session(self):
        client_clone = self.client.clone(session=self.client.session)
        assert self.client.session == client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_new_bucket_and_collection(self):
        client_clone = self.client.clone(bucket="bucket_blah", collection="coll_blah")
        assert self.client.session == client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name != client_clone._bucket_name
        assert self.client._collection_name != client_clone._collection_name
        assert client_clone._bucket_name == "bucket_blah"
        assert client_clone._collection_name == "coll_blah"

    def test_client_clone_with_auth_and_server_url_bucket_and_collection(self):
        client_clone = self.client.clone(auth=("reviewer", ""),
                                         server_url="https://kinto.notmyidea.org/v1",
                                         bucket="bucket_blah",
                                         collection="coll_blah")
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client._bucket_name != client_clone._bucket_name
        assert self.client._collection_name != client_clone._collection_name
        assert client_clone.session.auth == ("reviewer", "")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert client_clone._bucket_name == "bucket_blah"
        assert client_clone._collection_name == "coll_blah"
Beispiel #6
0
def backport_records(event, context, **kwargs):
    """Backport records creations, updates and deletions from one collection to another.
    """
    server_url = event["server"]
    source_auth = (
        event.get("backport_records_source_auth")
        or os.environ["BACKPORT_RECORDS_SOURCE_AUTH"]
    )
    source_bucket = (
        event.get("backport_records_source_bucket")
        or os.environ["BACKPORT_RECORDS_SOURCE_BUCKET"]
    )
    source_collection = (
        event.get("backport_records_source_collection")
        or os.environ["BACKPORT_RECORDS_SOURCE_COLLECTION"]
    )

    dest_auth = event.get(
        "backport_records_dest_auth",
        os.getenv("BACKPORT_RECORDS_DEST_AUTH", source_auth),
    )
    dest_bucket = event.get(
        "backport_records_dest_bucket",
        os.getenv("BACKPORT_RECORDS_DEST_BUCKET", source_bucket),
    )
    dest_collection = event.get(
        "backport_records_dest_collection",
        os.getenv("BACKPORT_RECORDS_DEST_COLLECTION", source_collection),
    )

    if source_bucket == dest_bucket and source_collection == dest_collection:
        raise ValueError("Cannot copy records: destination is identical to source")

    source_client = Client(
        server_url=server_url,
        bucket=source_bucket,
        collection=source_collection,
        auth=tuple(source_auth.split(":", 1))
        if ":" in source_auth
        else BearerTokenAuth(source_auth),
    )
    dest_client = Client(
        server_url=server_url,
        bucket=dest_bucket,
        collection=dest_collection,
        auth=tuple(dest_auth.split(":", 1))
        if ":" in dest_auth
        else BearerTokenAuth(dest_auth),
    )

    source_timestamp = source_client.get_records_timestamp()
    dest_timestamp = dest_client.get_records_timestamp()
    if source_timestamp <= dest_timestamp:
        print("Records are in sync. Nothing to do.")
        return

    source_records = source_client.get_records()
    dest_records_by_id = {r["id"]: r for r in dest_client.get_records()}

    with dest_client.batch() as dest_batch:
        # Create or update the destination records.
        for r in source_records:
            dest_record = dest_records_by_id.pop(r["id"], None)
            if dest_record is None:
                dest_batch.create_record(data=r)
            elif r["last_modified"] > dest_record["last_modified"]:
                dest_batch.update_record(data=r)
        # Delete the records missing from source.
        for r in dest_records_by_id.values():
            dest_batch.delete_record(id=r["id"])

    ops_count = len(dest_batch.results())

    # If destination has signing, request review or auto-approve changes.
    server_info = dest_client.server_info()
    signer_config = server_info["capabilities"].get("signer", {})
    signer_resources = signer_config.get("resources", [])
    # Check destination collection config (sign-off required etc.)
    signed_dest = [
        r
        for r in signer_resources
        if r["source"]["bucket"] == dest_bucket
        and r["source"]["collection"] == dest_collection
    ]
    if len(signed_dest) == 0:
        # Not explicitly configured. Check if configured at bucket level?
        signed_dest = [
            r
            for r in signer_resources
            if r["source"]["bucket"] == dest_bucket
            and r["source"]["collection"] is None
        ]
    # Destination has no signature enabled. Nothing to do.
    if len(signed_dest) == 0:
        print(f"Done. {ops_count} changes applied.")
        return

    has_autoapproval = not signed_dest[0].get(
        "to_review_enabled", signer_config["to_review_enabled"]
    ) and not signed_dest[0].get(
        "group_check_enabled", signer_config["group_check_enabled"]
    )
    if has_autoapproval:
        # Approve the changes.
        dest_client.patch_collection(data={"status": "to-sign"})
        print(f"Done. {ops_count} changes applied and signed.")
    else:
        # Request review.
        dest_client.patch_collection(data={"status": "to-review"})
        print(f"Done. Requested review for {ops_count} changes.")
class ClientTest(unittest.TestCase):
    def setUp(self):
        self.session = mock.MagicMock()
        self.client = Client(session=self.session)
        mock_response(self.session)

    def test_server_info(self):
        self.client.server_info()
        self.session.request.assert_called_with('get', '/')

    def test_context_manager_works_as_expected(self):
        settings = {"batch_max_requests": 25}
        self.session.request.side_effect = [({
            "settings": settings
        }, []), ({
            "responses": []
        }, [])]

        with self.client.batch(bucket='mozilla', collection='test') as batch:
            batch.create_record(id=1234, data={'foo': 'bar'})
            batch.create_record(id=5678, data={'bar': 'baz'})

        self.session.request.assert_called_with(
            method='POST',
            endpoint='/batch',
            payload={
                'requests': [{
                    'body': {
                        'data': {
                            'foo': 'bar'
                        }
                    },
                    'path': '/buckets/mozilla/collections/test/records/1234',
                    'method': 'PUT',
                    'headers': {
                        'If-None-Match': '*',
                        'User-Agent': USER_AGENT
                    }
                }, {
                    'body': {
                        'data': {
                            'bar': 'baz'
                        }
                    },
                    'path': '/buckets/mozilla/collections/test/records/5678',
                    'method': 'PUT',
                    'headers': {
                        'If-None-Match': '*',
                        'User-Agent': USER_AGENT
                    }
                }]
            })

    def test_batch_raises_exception(self):
        # Make the next call to sess.request raise a 403.
        exception = KintoException()
        exception.response = mock.MagicMock()
        exception.response.status_code = 403
        exception.request = mock.sentinel.request
        self.session.request.side_effect = exception

        with self.assertRaises(KintoException):
            with self.client.batch(bucket='moz', collection='test') as batch:
                batch.create_record(id=1234, data={'foo': 'bar'})

    def test_batch_raises_exception_if_subrequest_failed_with_code_5xx(self):
        error = {
            "errno": 121,
            "message": "This user cannot access this resource.",
            "code": 500,
            "error": "Server Internal Error"
        }
        self.session.request.side_effect = [({
            "settings": {
                "batch_max_requests": 25
            }
        }, []),
                                            ({
                                                "responses": [{
                                                    "status": 200,
                                                    "path": "/url1",
                                                    "body": {},
                                                    "headers": {}
                                                }, {
                                                    "status": 500,
                                                    "path": "/url2",
                                                    "body": error,
                                                    "headers": {}
                                                }]
                                            }, [])]

        with self.assertRaises(KintoException):
            with self.client.batch(bucket='moz', collection='test') as batch:
                batch.create_record(id=1234, data={'foo': 'bar'})
                batch.create_record(id=5678, data={'tutu': 'toto'})

    def test_batch_dont_raise_exception_if_subrequest_failed_with_code_4xx(
            self):
        error = {
            "errno": 121,
            "message": "Forbidden",
            "code": 403,
            "error": "This user cannot access this resource."
        }
        self.session.request.side_effect = [({
            "settings": {
                "batch_max_requests": 25
            }
        }, []),
                                            ({
                                                "responses": [{
                                                    "status": 200,
                                                    "path": "/url1",
                                                    "body": {},
                                                    "headers": {}
                                                }, {
                                                    "status": 403,
                                                    "path": "/url2",
                                                    "body": error,
                                                    "headers": {}
                                                }]
                                            }, [])]

        with self.client.batch(bucket='moz',
                               collection='test') as batch:  # Do not raise
            batch.create_record(id=1234, data={'foo': 'bar'})
            batch.create_record(id=5678, data={'tutu': 'toto'})

    def test_batch_options_are_transmitted(self):
        settings = {"batch_max_requests": 25}
        self.session.request.side_effect = [({"settings": settings}, [])]
        with mock.patch('kinto_http.create_session') as create_session:
            with self.client.batch(bucket='moz',
                                   collection='test',
                                   retry=12,
                                   retry_after=20):
                _, last_call_kwargs = create_session.call_args_list[-1]
                self.assertEqual(last_call_kwargs['retry'], 12)
                self.assertEqual(last_call_kwargs['retry_after'], 20)

    def test_client_is_represented_properly_with_bucket_and_collection(self):
        client = Client(server_url="https://kinto.notmyidea.org/v1",
                        bucket="homebrewing",
                        collection="recipes")
        expected_repr = ("<KintoClient https://kinto.notmyidea.org/v1/"
                         "buckets/homebrewing/collections/recipes>")
        assert str(client) == expected_repr

    def test_client_is_represented_properly_with_bucket(self):
        client = Client(
            server_url="https://kinto.notmyidea.org/v1",
            bucket="homebrewing",
        )
        expected_repr = ("<KintoClient https://kinto.notmyidea.org/v1/"
                         "buckets/homebrewing>")
        assert str(client) == expected_repr

    def test_client_is_represented_properly_without_bucket(self):
        client = Client(server_url="https://kinto.notmyidea.org/v1",
                        bucket=None)
        expected_repr = ("<KintoClient https://kinto.notmyidea.org/v1/>")
        assert str(client) == expected_repr

    def test_client_uses_default_bucket_if_not_specified(self):
        mock_response(self.session)
        client = Client(session=self.session)
        client.get_bucket()
        self.session.request.assert_called_with('get', '/buckets/default')

    def test_client_uses_passed_bucket_if_specified(self):
        client = Client(server_url="https://kinto.notmyidea.org/v1",
                        bucket="buck")
        assert client._bucket_name == "buck"

    def test_client_clone_with_auth(self):
        client_clone = self.client.clone(auth=("reviewer", ""))
        assert client_clone.session.auth == ("reviewer", "")
        assert self.client.session != client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_server_url(self):
        client_clone = self.client.clone(
            server_url="https://kinto.notmyidea.org/v1")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_new_session(self):
        session = create_session(auth=("reviewer", ""),
                                 server_url="https://kinto.notmyidea.org/v1")
        client_clone = self.client.clone(session=session)
        assert client_clone.session == session
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_auth_and_server_url(self):
        client_clone = self.client.clone(
            auth=("reviewer", ""), server_url="https://kinto.notmyidea.org/v1")
        assert client_clone.session.auth == ("reviewer", "")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_existing_session(self):
        client_clone = self.client.clone(session=self.client.session)
        assert self.client.session == client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client._bucket_name == client_clone._bucket_name
        assert self.client._collection_name == client_clone._collection_name

    def test_client_clone_with_new_bucket_and_collection(self):
        client_clone = self.client.clone(bucket="bucket_blah",
                                         collection="coll_blah")
        assert self.client.session == client_clone.session
        assert self.client.session.server_url == client_clone.session.server_url
        assert self.client.session.auth == client_clone.session.auth
        assert self.client.session.nb_retry == client_clone.session.nb_retry
        assert self.client.session.retry_after == client_clone.session.retry_after
        assert self.client._bucket_name != client_clone._bucket_name
        assert self.client._collection_name != client_clone._collection_name
        assert client_clone._bucket_name == "bucket_blah"
        assert client_clone._collection_name == "coll_blah"

    def test_client_clone_with_auth_and_server_url_bucket_and_collection(self):
        client_clone = self.client.clone(
            auth=("reviewer", ""),
            server_url="https://kinto.notmyidea.org/v1",
            bucket="bucket_blah",
            collection="coll_blah")
        assert self.client.session != client_clone.session
        assert self.client.session.server_url != client_clone.session.server_url
        assert self.client.session.auth != client_clone.session.auth
        assert self.client._bucket_name != client_clone._bucket_name
        assert self.client._collection_name != client_clone._collection_name
        assert client_clone.session.auth == ("reviewer", "")
        assert client_clone.session.server_url == "https://kinto.notmyidea.org/v1"
        assert client_clone._bucket_name == "bucket_blah"
        assert client_clone._collection_name == "coll_blah"
class FunctionalTest(unittest2.TestCase):
    def __init__(self, *args, **kwargs):
        super(FunctionalTest, self).__init__(*args, **kwargs)
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, 'config/kinto.ini'))
        self.client = Client(server_url=self.server_url, auth=self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, '/__flush__')
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def get_user_id(self, credentials):
        hmac_secret = self.config.get('app:main', 'kinto.userid_hmac_secret')
        credentials = '%s:%s' % credentials
        digest = kinto_core_utils.hmac_digest(hmac_secret, credentials)
        return 'basicauth:%s' % digest

    def test_bucket_creation(self):
        bucket = self.client.create_bucket('mozilla')
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket['permissions']['write']

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        # Should not raise.
        self.client.create_bucket('mozilla', if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket('mozilla')
        self.client.get_bucket('mozilla')
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket('mozilla', data={'version': 1})
        assert bucket['data']['version'] == 1
        bucket = self.client.patch_bucket('mozilla', data={'author': 'you'})
        assert bucket['data']['version'] == 1
        assert bucket['data']['author'] == 'you'
        bucket = self.client.update_bucket('mozilla', data={'date': 'today'})
        assert bucket['data']['date'] == 'today'
        assert 'version' not in bucket['data']

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound, self.client.get_bucket,
                          'non-existent')

    def test_bucket_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.client.delete_bucket('mozilla', if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.delete_buckets()
        assert buckets[0]['id'] == 'mozilla'
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket('mozilla', permissions={'write': ['alexis']})
        bucket = self.client.get_bucket('mozilla')
        assert 'alexis' in bucket['permissions']['write']

    def test_group_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]},
                                 permissions={'write': [
                                     'blah',
                                 ]})
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group('payments', bucket='mozilla')
        assert 'blah' in group['permissions']['write']

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]},
                                 permissions={'write': [
                                     'blah',
                                 ]},
                                 if_not_exists=True)

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException):
            self.client.create_group('payments',
                                     bucket='mozilla',
                                     data={'members': [
                                         'blah',
                                     ]})
            self.client.create_group('payments',
                                     bucket='mozilla',
                                     data={'members': [
                                         'blah',
                                     ]},
                                     if_not_exists=True)

    def test_group_update(self):
        self.client.create_bucket('mozilla')
        group = self.client.create_group('payments',
                                         bucket='mozilla',
                                         data={'members': [
                                             'blah',
                                         ]},
                                         if_not_exists=True)
        assert group['data']['members'][0] == 'blah'
        group = self.client.update_group(data={'members': ['blah', 'foo']},
                                         group='payments',
                                         bucket='mozilla')
        self.assertEquals(group['data']['members'][1], 'foo')

    def test_group_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('receipts',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.create_group('assets',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        # The returned groups should be strings.
        groups = self.client.get_groups('mozilla')
        self.assertEquals(2, len(groups))
        self.assertEquals(set([coll['id'] for coll in groups]),
                          set(['receipts', 'assets']))

    def test_group_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.delete_group('payments', bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.delete_group('payments', bucket='mozilla')
        self.client.delete_group('payments', bucket='mozilla', if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               'request',
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group('payments',
                                         bucket='mozilla',
                                         if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('amo',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.create_group('blocklist',
                                 bucket='mozilla',
                                 data={'members': [
                                     'blah',
                                 ]})
        self.client.delete_groups(bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket('mozilla')
        deleted_groups = self.client.delete_groups(bucket='mozilla')
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments',
                                      bucket='mozilla',
                                      permissions={'write': [
                                          'alexis',
                                      ]})

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection('payments', bucket='mozilla')
        assert 'alexis' in collection['permissions']['write']

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        # Should not raise.
        self.client.create_collection('payments',
                                      bucket='mozilla',
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('receipts', bucket='mozilla')
        self.client.create_collection('assets', bucket='mozilla')

        # The returned collections should be strings.
        collections = self.client.get_collections('mozilla')
        self.assertEquals(2, len(collections))

        self.assertEquals(set([coll['id'] for coll in collections]),
                          set(['receipts', 'assets']))

    def test_collection_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments',
                                      bucket='mozilla',
                                      if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session,
                               'request',
                               side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection('payments',
                                              bucket='mozilla',
                                              if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('amo', bucket='mozilla')
        self.client.create_collection('blocklist', bucket='mozilla')
        self.client.delete_collections(bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket('mozilla')
        deleted_collections = self.client.delete_collections(bucket='mozilla')
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        record = client.get_record(created['data']['id'])
        assert 'alexis' in record['permissions']['read']

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'},
                             permissions={'read': ['alexis']})
        records = client.get_records()
        assert len(records) == 1

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={'foo': 'bar'},
                                 permissions={'read': ['alexis']})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_single_record_save(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        created['data']['bar'] = 'baz'

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created['data']['id'], data=created['data'])

        retrieved = client.get_record(created['data']['id'])
        assert 'alexis' in retrieved['permissions']['read']
        assert retrieved['data']['foo'] == u'bar'
        assert retrieved['data']['bar'] == u'baz'
        assert created['data']['id'] == retrieved['data']['id']

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={
                'id': created['data']['id'],
                'bar': 'baz'
            })

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'})
        client.create_record(data={
            'id': created['data']['id'],
            'bar': 'baz'
        },
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        client.create_record(data={
            'id': created['data']['id'],
            'bar': 'baz'
        },
                             safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        assert deleted['deleted'] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        deleted_if_exists = client.delete_record(record['data']['id'],
                                                 if_exists=True)
        assert deleted['deleted'] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record({'foo': 'bar'})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket('shared-bucket',
                                  permissions={'read': [
                                      alice_userid,
                                  ]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket('shared-bucket')

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla')
        client.create_bucket()
        client.create_group('payments', data={'members': []})
        client.patch_group('payments', data={'secret': 'psssssst!'})
        group = client.get_group('payments')
        assert group['data']['secret'] == 'psssssst!'

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='mozilla',
                        collection='payments')
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={'secret': 'psssssst!'})
        collection = client.get_collection()
        assert collection['data']['secret'] == 'psssssst!'

    def test_collection_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket('bob-bucket')
        self.client.create_collection('shared',
                                      bucket='bob-bucket',
                                      permissions={'read': [
                                          alice_userid,
                                      ]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection('shared', bucket='bob-bucket')

    def test_record_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket('bob-bucket')
        self.client.create_collection('bob-personal-collection',
                                      bucket='bob-bucket')
        record = self.client.create_record(
            data={'foo': 'bar'},
            permissions={'read': [
                alice_userid,
            ]},
            bucket='bob-bucket',
            collection='bob-personal-collection')

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(id=record['data']['id'],
                                         bucket='bob-bucket',
                                         collection='bob-personal-collection')

        assert record['data']['foo'] == 'bar'

    def test_request_batching(self):
        with self.client.batch(bucket='mozilla', collection='fonts') as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={'foo': 'bar'},
                                permissions={'read': ['natim']})
            batch.create_record(data={'bar': 'baz'},
                                permissions={'read': ['alexis']})

        records = self.client.get_records(bucket='mozilla', collection='fonts')
        assert len(records) == 2

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket='origin', collection='coll') as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={'foo': 'bar', 'n': n})

        origin = Client(server_url=self.server_url,
                        auth=self.auth,
                        bucket='origin',
                        collection='coll')
        destination = Client(server_url=self.server_url,
                             auth=self.auth,
                             bucket='destination',
                             collection='coll')

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket='destination',
                                          collection='coll')
        assert len(records) == 10
Beispiel #9
0
class FunctionalTest(unittest2.TestCase):

    def __init__(self, *args, **kwargs):
        super(FunctionalTest, self).__init__(*args, **kwargs)
        # XXX Read the configuration from env variables.
        self.server_url = SERVER_URL
        self.auth = DEFAULT_AUTH

        # Read the configuration.
        self.config = configparser.RawConfigParser()
        self.config.read(os.path.join(__HERE__, 'config/kinto.ini'))
        self.client = Client(server_url=self.server_url, auth=self.auth)

    def tearDown(self):
        # Delete all the created objects
        flush_url = urljoin(self.server_url, '/__flush__')
        resp = requests.post(flush_url)
        resp.raise_for_status()

    def get_user_id(self, credentials):
        hmac_secret = self.config.get('app:main', 'kinto.userid_hmac_secret')
        credentials = '%s:%s' % credentials
        digest = kinto_core_utils.hmac_digest(hmac_secret, credentials)
        return 'basicauth:%s' % digest

    def test_bucket_creation(self):
        bucket = self.client.create_bucket('mozilla')
        user_id = self.get_user_id(self.auth)
        assert user_id in bucket['permissions']['write']

    def test_bucket_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        # Should not raise.
        self.client.create_bucket('mozilla', if_not_exists=True)

    def test_buckets_retrieval(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.get_buckets()
        assert len(buckets) == 1

    def test_bucket_retrieval(self):
        self.client.create_bucket('mozilla')
        self.client.get_bucket('mozilla')
        # XXX Add permissions handling during creation and check they are
        # present during retrieval.

    def test_bucket_modification(self):
        bucket = self.client.create_bucket('mozilla', data={'version': 1})
        assert bucket['data']['version'] == 1
        bucket = self.client.patch_bucket('mozilla', data={'author': 'you'})
        assert bucket['data']['version'] == 1
        assert bucket['data']['author'] == 'you'
        bucket = self.client.update_bucket('mozilla', data={'date': 'today'})
        assert bucket['data']['date'] == 'today'
        assert 'version' not in bucket['data']

    def test_bucket_retrieval_fails_when_not_created(self):
        self.assertRaises(BucketNotFound, self.client.get_bucket,
                          'non-existent')

    def test_bucket_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_bucket_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.delete_bucket('mozilla')
        self.client.delete_bucket('mozilla', if_exists=True)

    def test_buckets_deletion(self):
        self.client.create_bucket('mozilla')
        buckets = self.client.delete_buckets()
        assert buckets[0]['id'] == 'mozilla'
        self.assertRaises(BucketNotFound, self.client.get_bucket, 'mozilla')

    def test_buckets_deletion_when_no_buckets_exist(self):
        deleted_buckets = self.client.delete_buckets()
        assert len(deleted_buckets) == 0

    def test_bucket_save(self):
        self.client.create_bucket('mozilla', permissions={'write': ['alexis']})
        bucket = self.client.get_bucket('mozilla')
        assert 'alexis' in bucket['permissions']['write']

    def test_group_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_group(
            'payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]})
        # Test retrieval of a group gets the permissions as well.
        group = self.client.get_group('payments', bucket='mozilla')
        assert 'blah' in group['permissions']['write']

    def test_group_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group(
            'payments', bucket='mozilla',
            data={'members': ['blah', ]},
            permissions={'write': ['blah', ]},
            if_not_exists=True)

    def test_group_creation_if_bucket_does_not_exist(self):
        with pytest.raises(KintoException):
            self.client.create_group(
                'payments', bucket='mozilla',
                data={'members': ['blah', ]})
            self.client.create_group(
                'payments', bucket='mozilla',
                data={'members': ['blah', ]},
                if_not_exists=True)

    def test_group_update(self):
        self.client.create_bucket('mozilla')
        group = self.client.create_group(
                    'payments', bucket='mozilla',
                    data={'members': ['blah', ]},
                    if_not_exists=True)
        assert group['data']['members'][0] == 'blah'
        group = self.client.update_group(
                    data={'members': ['blah', 'foo']},
                    group='payments', bucket='mozilla')
        self.assertEquals(group['data']['members'][1], 'foo')

    def test_group_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('receipts', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group('assets', bucket='mozilla', data={'members': ['blah', ]})
        # The returned groups should be strings.
        groups = self.client.get_groups('mozilla')
        self.assertEquals(2, len(groups))
        self.assertEquals(set([coll['id'] for coll in groups]),
                          set(['receipts', 'assets']))

    def test_group_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group('payments', bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_group_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('payments', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_group('payments', bucket='mozilla')
        self.client.delete_group('payments', bucket='mozilla', if_exists=True)

    def test_group_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_group('payments', bucket='mozilla', if_exists=True)

    def test_groups_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_group('amo', bucket='mozilla', data={'members': ['blah', ]})
        self.client.create_group('blocklist', bucket='mozilla', data={'members': ['blah', ]})
        self.client.delete_groups(bucket='mozilla')
        assert len(self.client.get_groups(bucket='mozilla')) == 0

    def test_groups_deletion_when_no_groups_exist(self):
        self.client.create_bucket('mozilla')
        deleted_groups = self.client.delete_groups(bucket='mozilla')
        assert len(deleted_groups) == 0

    def test_collection_creation(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection(
            'payments', bucket='mozilla',
            permissions={'write': ['alexis', ]}
        )

        # Test retrieval of a collection gets the permissions as well.
        collection = self.client.get_collection('payments', bucket='mozilla')
        assert 'alexis' in collection['permissions']['write']

    def test_collection_creation_if_not_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        # Should not raise.
        self.client.create_collection('payments', bucket='mozilla',
                                      if_not_exists=True)

    def test_collection_list(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('receipts', bucket='mozilla')
        self.client.create_collection('assets', bucket='mozilla')

        # The returned collections should be strings.
        collections = self.client.get_collections('mozilla')
        self.assertEquals(2, len(collections))

        self.assertEquals(set([coll['id'] for coll in collections]),
                          set(['receipts', 'assets']))

    def test_collection_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collection_deletion_if_exists(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla')
        self.client.delete_collection('payments', bucket='mozilla', if_exists=True)

    def test_collection_deletion_can_still_raise_errors(self):
        error = KintoException("An error occured")
        with mock.patch.object(self.client.session, 'request', side_effect=error):
            with pytest.raises(KintoException):
                self.client.delete_collection('payments', bucket='mozilla', if_exists=True)

    def test_collections_deletion(self):
        self.client.create_bucket('mozilla')
        self.client.create_collection('amo', bucket='mozilla')
        self.client.create_collection('blocklist', bucket='mozilla')
        self.client.delete_collections(bucket='mozilla')
        assert len(self.client.get_collections(bucket='mozilla')) == 0

    def test_collections_deletion_when_no_collections_exist(self):
        self.client.create_bucket('mozilla')
        deleted_collections = self.client.delete_collections(bucket='mozilla')
        assert len(deleted_collections) == 0

    def test_record_creation_and_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        record = client.get_record(created['data']['id'])
        assert 'alexis' in record['permissions']['read']

    def test_records_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record(data={'foo': 'bar'},
                             permissions={'read': ['alexis']})
        records = client.get_records()
        assert len(records) == 1

    def test_records_paginated_list_retrieval(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        for i in range(10):
            client.create_record(data={'foo': 'bar'},
                                 permissions={'read': ['alexis']})
        # Kinto is running with kinto.paginate_by = 5
        records = client.get_records()
        assert len(records) == 10

    def test_single_record_save(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})
        created['data']['bar'] = 'baz'

        # XXX enhance this in order to have to pass only one argument, created.
        client.update_record(id=created['data']['id'], data=created['data'])

        retrieved = client.get_record(created['data']['id'])
        assert 'alexis' in retrieved['permissions']['read']
        assert retrieved['data']['foo'] == u'bar'
        assert retrieved['data']['bar'] == u'baz'
        assert created['data']['id'] == retrieved['data']['id']

    def test_single_record_doesnt_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        with self.assertRaises(KintoException):
            # Create a second record with the ID of the first one.
            client.create_record(data={'id': created['data']['id'],
                                       'bar': 'baz'})

    def test_single_record_creation_if_not_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'})
        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'},
                             if_not_exists=True)

    def test_single_record_can_overwrite(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        created = client.create_record(data={'foo': 'bar'},
                                       permissions={'read': ['alexis']})

        client.create_record(data={'id': created['data']['id'],
                                   'bar': 'baz'}, safe=False)

    def test_one_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        assert deleted['deleted'] is True
        assert len(client.get_records()) == 0

    def test_record_deletion_if_exists(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        record = client.create_record({'foo': 'bar'})
        deleted = client.delete_record(record['data']['id'])
        deleted_if_exists = client.delete_record(record['data']['id'], if_exists=True)
        assert deleted['deleted'] is True
        assert deleted_if_exists is None

    def test_multiple_record_deletion(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        client.create_record({'foo': 'bar'})
        client.delete_records()
        assert len(client.get_records()) == 0

    def test_records_deletion_when_no_records_exist(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()
        deleted_records = client.delete_records()
        assert len(deleted_records) == 0

    def test_bucket_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a bucket and share it with alice.
        self.client.create_bucket('shared-bucket',
                                  permissions={'read': [alice_userid, ]})

        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_bucket('shared-bucket')

    def test_updating_data_on_a_group(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla')
        client.create_bucket()
        client.create_group('payments', data={'members': []})
        client.patch_group('payments', data={'secret': 'psssssst!'})
        group = client.get_group('payments')
        assert group['data']['secret'] == 'psssssst!'

    def test_updating_data_on_a_collection(self):
        client = Client(server_url=self.server_url, auth=self.auth,
                        bucket='mozilla', collection='payments')
        client.create_bucket()
        client.create_collection()

        client.patch_collection(data={'secret': 'psssssst!'})
        collection = client.get_collection()
        assert collection['data']['secret'] == 'psssssst!'

    def test_collection_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        self.client.create_bucket('bob-bucket')
        self.client.create_collection(
            'shared',
            bucket='bob-bucket',
            permissions={'read': [alice_userid, ]})

        # Try to read the collection as Alice.
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        alice_client.get_collection('shared', bucket='bob-bucket')

    def test_record_sharing(self):
        alice_credentials = ('alice', 'p4ssw0rd')
        alice_userid = self.get_user_id(alice_credentials)

        # Create a record, and share it with Alice.
        self.client.create_bucket('bob-bucket')
        self.client.create_collection('bob-personal-collection',
                                      bucket='bob-bucket')
        record = self.client.create_record(
            data={'foo': 'bar'},
            permissions={'read': [alice_userid, ]},
            bucket='bob-bucket',
            collection='bob-personal-collection')

        # Try to read the record as Alice
        alice_client = Client(server_url=self.server_url,
                              auth=alice_credentials)
        record = alice_client.get_record(
            id=record['data']['id'],
            bucket='bob-bucket',
            collection='bob-personal-collection')

        assert record['data']['foo'] == 'bar'

    def test_request_batching(self):
        with self.client.batch(bucket='mozilla', collection='fonts') as batch:
            batch.create_bucket()
            batch.create_collection()
            batch.create_record(data={'foo': 'bar'},
                                permissions={'read': ['natim']})
            batch.create_record(data={'bar': 'baz'},
                                permissions={'read': ['alexis']})

        records = self.client.get_records(bucket='mozilla', collection='fonts')
        assert len(records) == 2

    def test_replication(self):
        # First, create a few records on the first kinto collection.
        with self.client.batch(bucket='origin', collection='coll') as batch:
            batch.create_bucket()
            batch.create_collection()

            for n in range(10):
                batch.create_record(data={'foo': 'bar', 'n': n})

        origin = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='origin',
            collection='coll'
        )
        destination = Client(
            server_url=self.server_url,
            auth=self.auth,
            bucket='destination',
            collection='coll')

        replication.replicate(origin, destination)
        records = self.client.get_records(bucket='destination',
                                          collection='coll')
        assert len(records) == 10