Beispiel #1
0
    def test_round_trip_with_delete_missing_records_ask_for_confirmation(self):
        # Load some data
        cmd = 'kinto-wizard {} --server={} --auth={}'
        load_cmd = cmd.format("load {}".format(self.file), self.server,
                              self.auth)
        sys.argv = load_cmd.split(" ")
        main()

        # Change something that could make the server to fail.
        client = Client(server_url=self.server,
                        auth=tuple(self.auth.split(':')))
        client.create_record(bucket='build-hub',
                             collection='archives',
                             id='8031d549-0a69-48dd-b240-feef94688d47',
                             data={})
        cmd = 'kinto-wizard {} --server={} -D --auth={} --delete-records'
        load_cmd = cmd.format("load {}".format(self.file), self.server,
                              self.auth)
        sys.argv = load_cmd.split(" ")

        with mockInput('yes'):
            main()

        with pytest.raises(exceptions.KintoException) as exc:
            client.get_record(bucket='build-hub',
                              collection='archives',
                              id='8031d549-0a69-48dd-b240-feef94688d47')
        assert "'Not Found'" in str(exc.value)
Beispiel #2
0
 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
Beispiel #3
0
 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
Beispiel #4
0
 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)
Beispiel #5
0
 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
Beispiel #6
0
 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
Beispiel #7
0
 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)
Beispiel #8
0
 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
Beispiel #9
0
    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)
Beispiel #10
0
    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)
Beispiel #11
0
 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
Beispiel #12
0
    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'})
Beispiel #13
0
 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
Beispiel #14
0
    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'})
Beispiel #15
0
 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 create_booth_with_auth(booth, username):
    password = accounts_helper.create_user(username)
    print(f'created user "{username}" with password "{password}"')

    client = Client(server_url=kinto_url,
                    auth=(username, password),
                    bucket="weihnachtsmarkt",
                    collection="booths")

    del_none(booth)
    client.create_record(data=booth)
    print(f'created booth {booth["name"]} for market {booth["market"]}')
Beispiel #17
0
 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)
Beispiel #18
0
 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
Beispiel #19
0
    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
Beispiel #20
0
    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)
Beispiel #21
0
    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"
            })
Beispiel #22
0
 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"])
Beispiel #23
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']
Beispiel #24
0
 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
Beispiel #25
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']
Beispiel #26
0
 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': ['alexis']})
     etag = client.get_records_timestamp()
     assert str(etag) == str(record["data"]["last_modified"])
Beispiel #27
0
 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
Beispiel #28
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
Beispiel #29
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
Beispiel #30
0
 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
Beispiel #31
0
 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"])
Beispiel #32
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"]
Beispiel #33
0
def test_delete_request_removes_data(conf, env, fxa_account, fxa_urls,
                                     fxa_client):
    if env == 'prod':
        pytest.skip('kintowe GDPR tests are not run in production')

    auth = FxABearerTokenAuth(
        fxa_account.email,
        fxa_account.password,
        scopes=['sync:addon_storage'],
        client_id=DEFAULT_CLIENT_ID,
        account_server_url=fxa_urls['authentication'],
        oauth_server_url=fxa_urls['oauth'],
    )

    # Add some data to chrome.storage (kintowe)
    we_client = Client(server_url=conf.get(env, 'we_server_url'), auth=auth)
    we_existing_records = we_client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                                bucket='default')
    assert len(we_existing_records) == 0
    data = {"payload": {"encrypted": "SmluZ28gdGVzdA=="}}
    we_record = we_client.create_record(
        data=data,
        collection=conf.get(env, 'qa_collection'),
        bucket='default',
        permissions={"read": ["system.Everyone"]})
    we_record_id = we_record['data']['id']
    we_updated_records = we_client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                               bucket='default')
    assert len(we_updated_records) == len(we_existing_records) + 1

    # Get the aliases of the bucket we are putting data in and
    # make sure that an unauthenticated user can see these records
    # before we delete the account
    we_bucket_id = we_client.server_info()["user"]["bucket"]
    anon_we_client = Client(server_url=conf.get(env, 'we_server_url'))
    resp = anon_we_client.get_record(id=we_record_id,
                                     bucket=we_bucket_id,
                                     collection=conf.get(env, 'qa_collection'))
    assert resp['data']['id'] == we_record_id

    # Delete FxA account
    fxa_client.destroy_account(fxa_account.email, fxa_account.password)

    # Wait 1 minute and then make sure the records do not exist because the
    # Kinto client will throw an exception for non-existent records
    time.sleep(60)
    with pytest.raises(KintoException):
        resp = anon_we_client.get_record(id=we_record_id,
                                         bucket=we_bucket_id,
                                         collection=conf.get(
                                             env, 'qa_collection'))
Beispiel #34
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
Beispiel #35
0
def test_delete_request_removes_data(conf, env, fxa_account, fxa_urls,
                                     fxa_client):
    if env == 'prod':
        pytest.skip('testpilot GDPR tests are not run in production')

    auth = FxABearerTokenAuth(
        fxa_account.email,
        fxa_account.password,
        client_id=DEFAULT_CLIENT_ID,
        scopes=["https://identity.mozilla.com/apps/notes"],
        account_server_url=fxa_urls['authentication'],
        oauth_server_url=fxa_urls['oauth'],
    )

    # Add some data to the Notes collection
    tp_client = Client(server_url=conf.get(env, 'tp_server_url'), auth=auth)
    tp_existing_records = tp_client.get_records(collection='notes',
                                                bucket='default')
    assert len(tp_existing_records) == 0
    data = {"subject": "QA Test", "value": "This stuff should get deleted"}
    tp_record = tp_client.create_record(
        data=data,
        collection='notes',
        permissions={"read": ["system.Everyone"]})
    tp_record_id = tp_record['data']['id']
    tp_updated_records = tp_client.get_records(collection='notes',
                                               bucket='default')
    assert len(tp_updated_records) == len(tp_existing_records) + 1

    # Get the aliases of the bucket we are putting data in and
    # make sure that an unauthenticated user can see these records
    # before we delete the account
    tp_bucket_id = tp_client.server_info()["user"]["bucket"]
    anon_tp_client = Client(server_url=conf.get(env, 'tp_server_url'))
    resp = anon_tp_client.get_record(id=tp_record_id,
                                     bucket=tp_bucket_id,
                                     collection='notes')
    assert resp['data']['id'] == tp_record_id

    # Delete FxA account
    fxa_client.destroy_account(fxa_account.email, fxa_account.password)

    # Wait 5 minutes and then make sure the records do not exist because the
    # Kinto client will throw an exception for non-existent records
    time.sleep(120)

    with pytest.raises(KintoException):
        resp = anon_tp_client.get_record(id=tp_record_id,
                                         bucket=tp_bucket_id,
                                         collection='notes')
Beispiel #36
0
def test_add_content(env, conf):
    # Grab a bearer token that we can use to talk to the webextensions endpoint
    acct = TestEmailAccount()
    email = acct.email
    passwd = str(uuid.uuid4())
    fxaclient = FxaClient("https://api.accounts.firefox.com")
    session = fxaclient.create_account(email, passwd)
    m = acct.wait_for_email(lambda m: "x-verify-code" in m["headers"])

    if m is None:
        raise RuntimeErrors("Verification email did not arrive")

    session.verify_email_code(m["headers"]["x-verify-code"])
    auth = FxABearerTokenAuth(
        email,
        passwd,
        scopes=['sync:addon_storage'],
        client_id=DEFAULT_CLIENT_ID,
        account_server_url=conf.get(env, 'account_server_url'),
        oauth_server_url=conf.get(env, 'oauth_server_url'),
    )
    client = Client(server_url=conf.get(env, 'we_server_url'), auth=auth)

    # Add a record to our QA collection and make sure we have N+1 records
    existing_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                          bucket='default')
    assert len(existing_records) == 0

    data = {"payload": {"encrypted": "SmluZ28gdGVzdA=="}}
    resp = client.create_record(data=data,
                                collection=conf.get(env, 'qa_collection'),
                                bucket='default')
    new_record_id = resp['data']['id']
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records) + 1

    client.delete_record(id=new_record_id,
                         collection=conf.get(env, 'qa_collection'))
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records)

    # Clean up the account that we created for the test
    acct.clear()
    fxaclient.destroy_account(email, passwd)
Beispiel #37
0
    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']
Beispiel #38
0
    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']
Beispiel #39
0
    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_add_content(env, conf, fxa_account, fxa_urls):
    if env == 'prod':
        pytest.skip('qa cannot create records in production')

    auth = FxABearerTokenAuth(
        fxa_account.email,
        fxa_account.password,
        scopes=['sync:addon_storage'],
        client_id=DEFAULT_CLIENT_ID,
        account_server_url=fxa_urls['authentication'],
        oauth_server_url=fxa_urls['oauth'],
    )
    client = Client(server_url=conf.get(env, 'we_server_url'), auth=auth)

    # Add a record to our QA collection and make sure we have N+1 records
    existing_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                          bucket='default')
    assert len(existing_records) == 0

    data = {"payload": {"encrypted": "SmluZ28gdGVzdA=="}}
    resp = client.create_record(data=data,
                                collection=conf.get(env, 'qa_collection'),
                                bucket='default')
    new_record_id = resp['data']['id']
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records) + 1

    client.delete_record(id=new_record_id,
                         collection=conf.get(env, 'qa_collection'))
    updated_records = client.get_records(collection=conf.get(
        env, 'qa_collection'),
                                         bucket='default')
    assert len(updated_records) == len(existing_records)
from kinto_http import Client

# Login
credentials = ('paco', '123456')
client = Client(server_url="http://localhost:8888/v1", auth=credentials)

# Create bucket
client.create_collection(id='Tasks', bucket='default')
client.create_collection(id='Test', bucket='default')

# Add records
client.create_record(data={
    'title': 'Create Documentation',
    'description': 'Create docs using Markdown',
    'status': 'Done'
},
                     collection='Test',
                     bucket='default')

client.create_record(data={
    'title': 'Create Dockerfile',
    'description': 'Create Parse Dockerfile',
    'status': 'Done'
},
                     collection='Test',
                     bucket='default')

client.create_record(data={
    'title': 'Update Computer',
    'description': 'Update Solus Distro',
    'status': 'Doing'
Beispiel #42
0
class RecordLoggingTest(unittest.TestCase):
    def setUp(self):
        self.session = mock.MagicMock()
        self.client = Client(session=self.session)
        mock_response(self.session)

    def test_create_record_logs_info_message(self):
        with mock.patch('kinto_http.logger') as mocked_logger:
            self.client.create_bucket(id='buck')
            self.client.create_collection(id='mozilla',
                                          bucket='buck')
            self.client.create_record(
                id='fake-record',
                data={'foo': 'bar'},
                permissions={'write': ['blah', ]},
                bucket='buck',
                collection='mozilla')
            mocked_logger.info.assert_called_with(
                "Create record with id 'fake-record' in collection 'mozilla' in bucket 'buck'")

    def test_update_record_logs_info_message(self):
        with mock.patch('kinto_http.logger') as mocked_logger:
            self.client.create_bucket(id='buck')
            self.client.create_collection(bucket='buck',
                                          id='mozilla')
            self.client.update_record(
                id='fake-record',
                data={'ss': 'aa'},
                bucket='buck',
                collection='mozilla')
            mocked_logger.info.assert_called_with(
                "Update record with id 'fake-record' in collection 'mozilla' in bucket 'buck'")

    def test_get_record_logs_info_message(self):
        with mock.patch('kinto_http.logger') as mocked_logger:
            self.client.create_bucket(id='buck')
            self.client.create_collection(id='mozilla',
                                          bucket='buck')
            self.client.get_record(
                id='fake-record',
                bucket='buck',
                collection='mozilla')
            mocked_logger.info.assert_called_with(
                "Get record with id 'fake-record' from collection 'mozilla' in bucket 'buck'")

    def test_delete_record_logs_info_message(self):
        with mock.patch('kinto_http.logger') as mocked_logger:
            self.client.create_bucket(id='buck')
            self.client.create_collection(id='mozilla',
                                          bucket='buck')
            self.client.delete_record(
                id='fake-record',
                bucket='buck',
                collection='mozilla')
            mocked_logger.info.assert_called_with(
                "Delete record with id 'fake-record' from collection 'mozilla' in bucket 'buck'")

    def test_delete_records_logs_info_message(self):
        with mock.patch('kinto_http.logger') as mocked_logger:
            self.client.create_bucket(id='buck')
            self.client.create_collection(id='mozilla',
                                          bucket='buck')
            self.client.delete_records(
                bucket='buck',
                collection='mozilla')
            mocked_logger.info.assert_called_with(
                "Delete records from collection 'mozilla' in bucket 'buck'")
Beispiel #43
0
class AppWindow(QtWidgets.QMainWindow,Ui_MainWindow):
    def __init__(self, parent = None):
        QtWidgets.QMainWindow.__init__(self, parent)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        self.setupUi(self)




######################################################################################
######################################################################################
        self.settings = Settings()
        self.initRep()
        self.initKintoClient()
        self.syncRecipes()
        self.dlgEditG = Dialog(self)
        self.dlgEditH = DialogH(self)
        self.dlgEditD = DialogD(self)
        self.dlgEditY = DialogL(self)
        self.dlgPref = DialogPref(self)
        self.dlgStep = DialogStep(self)
        self.dlgMash = DialogMash(self)



        self.base = ImportBase()
        self.mashProfileExport = ExportMash()


#        self.base.importBeerXML()
        self.s=0
        self.recipe = None


        #Les connexions
        self.actionEnregistrer.triggered.connect(self.enregistrer)
        self.actionQuitter.triggered.connect(app.quit)
#        self.connect(self.actionQuitter, QtCore.SIGNAL("triggered()"), app, QtCore.SLOT("quit()"))

        self.actionShowJournal.triggered.connect(self.showJournal)


        self.actionEditGrains.triggered.connect(self.editGrains)
        self.actionEditHoublons.triggered.connect(self.editHoublons)
        self.actionEditDivers.triggered.connect(self.editDivers)
        self.actionEditLevures.triggered.connect(self.editLevures)
        self.actionRestaurerIngredients.triggered.connect(self.restoreDataBase)
        self.actionImportIng.triggered.connect(self.importIng)
        self.actionManageProfiles.triggered.connect(self.seeMash)

        self.actionAbout.triggered.connect(self.about)

        self.actionAllTools.triggered.connect(self.showTools)

        self.actionPreferences.triggered.connect(self.dialogPreferences)



        #######################################################################################################
        # Profil de brassage       #########################################################################################################


        self.listWidgetSteps.itemSelectionChanged.connect (self.stepDetails)
        self.listWidgetMashProfiles.itemSelectionChanged.connect (self.mashClicked)
        self.buttonBoxMashDetails.rejected.connect(self.mashRejected)
#        self.comboBoxStepType.addItems(["Infusion", "Température", "Décoction"])
        self.pushButtonStepEdit.clicked.connect(self.stepEdit)
        self.dlgStep.stepChanged.connect(self.stepReload)
        self.pushButtonStepRemove.clicked.connect(self.removeStep)
        self.pushButtonNewStep.clicked.connect(self.addStep)
        self.pushButtonMashEdit.clicked.connect(self.mashEdit)
        self.dlgMash.mashChanged.connect(self.mashReload)
        self.pushButtonNewProfile.clicked.connect(self.addMash)
        self.pushButtonRemoveProfile.clicked.connect(self.removeMash)
        self.pushButtonSaveProfile.clicked.connect(self.saveProfile)

        #La bibliotheque
        ###################################################################################################################
        ###################################################################################################################


        # self.listdir(recettes_dir)
        self.showLib()

###################################################################################################
######## gestion des arguments au lancement du programme  #########################################


        argumentsList=QtWidgets.QApplication.arguments()
        if len(argumentsList) > 1 :
            logger.debug("la liste d'arguments: %s",argumentsList)
            logger.debug("le chemin: %s",argumentsList[1])
            # for part in argumentsList :
            #     recipePath=recipePath + " " + part
            try:
                recipePath= argumentsList[1]
                for part in argumentsList[2:] :
                    recipePath= recipePath +" " + part

                self.openRecipeFile(recipePath)
            except :
                pass
        else:
            pass

########################################################################################################################
####################################################################################################################
# le signal émit à la fermeture de la fenêtre de préférences
        self.dlgPref.prefAccepted.connect(self.prefReload)



###########################################################
############### Journal ##############################
######################################################

    def loadJournal(self):
        self.journal=Journal()
        self.journal.loadJournal()
        # self.actionEditJournal.setEnabled(True)

    @QtCore.pyqtSlot()
    def showJournal(self,entry=" '' ") :
        self.stackedWidget.setCurrentIndex(0)
        self.loadJournal()
        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
        self.webViewBiblio.setHtml(self.journal.export("html",entry), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject("main", self)
        # self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        # self.webInspector = QtWebKit.QWebInspector(self)
        # self.webInspector.setPage(self.webViewBiblio.page())
        # self.webInspector.setVisible(True)
        # self.verticalLayout_13.addWidget(self.webInspector)


    @QtCore.pyqtSlot(str, str)
    def addToJournal(self,event, recipeName) :
        self.loadJournal()
        entry = '''{recipe:%s,date:%s,event:%s,editing:'True'} ''' %( "'" + recipeName + "'", "'" + str(int(time.time())) + "'" , "'" + self.journal.eventsLabels[event] + "'")
        self.showJournal(entry)


    @QtCore.pyqtSlot(str)
    def dumpJournal(self,journalJson) :
        journalJson= '{"name":"journal","items": %s }' %journalJson
        d=json.loads(journalJson)
        with open(journal_file, mode="w", encoding="utf-8") as f :
            json.dump(d,f,indent=2)



############## Bibliothèque ##############################
##########################################################
    @QtCore.pyqtSlot()
    def showLib(self) :
        # data = json.dumps(self.recipesSummary)
        # data = data.replace("'","'")
        self.stackedWidget.setCurrentIndex(0)
        self.brewdayLock = 0

        self.webSettings = self.webViewBiblio.settings()
        self.webSettings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)

        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/html/"))
        self.webViewBiblio.setHtml(LibExporterRepository['html'](), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject("main", self)
        self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        self.webViewBiblio.page().action(QtWebKitWidgets.QWebPage.Reload).setVisible(False)


    @QtCore.pyqtSlot(str)
    def deleteLib(self,path) :
        confirmation = QtWidgets.QMessageBox.question(self,
                            self.tr("Supprimer"),
                            self.tr("La recette sera définitivement supprimée <br/> Continuer ?"),
                            QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        if (confirmation == QtWidgets.QMessageBox.Yes):
            os.remove(path)
            self.listdir(recettes_dir)
            self.showLib()
        else :
            self.showLib()


    @QtCore.pyqtSlot()
    def backWebViewBiblio(self) :
        self.stackedWidget.setCurrentIndex(0)


    @QtCore.pyqtSlot(str, str)
    def saveRecipe(self, recipe, path) :
        logger.debug(path)
        recipeFile = QtCore.QFile(path)
        if recipeFile.open(QtCore.QIODevice.WriteOnly):
            try:
                stream = QtCore.QTextStream(recipeFile)
                stream.setCodec("UTF-8")
                stream << recipe
            finally:
                recipeFile.close()
        else:
            # TODO : Prévenir l'utilisateur en cas d'échec de l'enregistrement
            pass

    @QtCore.pyqtSlot(result=str)
    def createPath(self, file_id=None) :
        if file_id is None:
            file_id = str(int(time.time()*10))
        path = recettes_dir + "/" + file_id + ".xml"
        logger.debug(path)
        return path

    @QtCore.pyqtSlot()
    def resetLock(self):
        self.brewdayLock = 0;




############# Mode Brassage ################################
############################################################

    @QtCore.pyqtSlot(str)
    def showBrewdayMode(self, data):
        if self.brewdayLock == 0 :
            self.stackedWidget.setCurrentIndex(1)
            self.brewdayLock = 1
            data = data.replace("'","&#39;")
            pyDir = os.path.abspath(os.path.dirname(__file__))
            baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
            self.webViewBrewday.setHtml(BrewdayExporterRepository['html'](data), baseUrl)
            self.webViewBrewday.page().mainFrame().addToJavaScriptWindowObject("main", self)
            self.webViewBrewday.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
            self.webViewBrewday.page().action(QtWebKitWidgets.QWebPage.Reload).setVisible(False)
        else :
            self.stackedWidget.setCurrentIndex(1)





###### Outils ############################################
##########################################################

    @QtCore.pyqtSlot()
    def showTools(self):
        self.stackedWidget.setCurrentIndex(0)
        pyDir = os.path.abspath(os.path.dirname(__file__))
        baseUrl = QtCore.QUrl.fromLocalFile(os.path.join(pyDir, "static/"))
        self.webViewBiblio.setHtml(ToolExporterRepository["html"](), baseUrl)
        self.webViewBiblio.page().mainFrame().addToJavaScriptWindowObject("main", self)
        # self.webViewBiblio.page().settings().setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True)
        # self.webInspector = QtWebKit.QWebInspector(self)
        # self.webInspector.setPage(self.webViewBiblio.page())
        # self.webInspector.setVisible(True)
        # self.verticalLayout_13.addWidget(self.webInspector)



    @QtCore.pyqtSlot(result=str)
    def dataRecipes(self) :
        # f = open(recipeData_file, 'w')
        # f.write(self.recipesSummary)
        self.listdir(recettes_dir)
        return self.recipesSummary

    @QtCore.pyqtSlot(result=str)
    def dataProfiles(self) :
        return self.mashProfileExport.exportJson(ImportBase().listeMashes)


    @QtCore.pyqtSlot(result=str)
    def dataIngredients(self) :
        return ImportBase().exportjson()


    @QtCore.pyqtSlot(result=str)
    def dataPref(self) :
        dic = {}
        dic["boilOffRate"] = settings.conf.value("BoilOffRate")
        dic["coolingLossRate"] = settings.conf.value("CoolingLoss")
        dic["grainTemp"] = settings.conf.value("GrainTemp")
        dic["fudgeFactor"] = settings.conf.value("FudgeFactor")
        dic["grainRetention"] = settings.conf.value("GrainRetention")
        dic = json.dumps(dic)
        return dic











    #Une fonction qui gère l'aperçu des couleurs.
    #Contient un tupple avec plusieurs références de couleurs, classées par rang selon la valeur SRM.
    #################################################################################################
    # def colorPreview (self) :
    #     self.colorTuppleSrm = ('FFE699', 'FFD878', 'FFCA5A', 'FFBF42', 'FBB123', 'F8A600', 'F39C00', 'EA8F00', 'E58500', 'DE7C00', 'D77200', 'CF6900', 'CB6200', 'C35900','BB5100', 'B54C00', 'B04500', 'A63E00', 'A13700', '9B3200', '952D00', '8E2900', '882300', '821E00', '7B1A00', '771900', '701400', '6A0E00', '660D00','5E0B00','5A0A02','600903', '520907', '4C0505', '470606', '440607', '3F0708', '3B0607', '3A070B', '36080A')

    #     colorRef= round(self.recipe.compute_EBC()/1.97)

    #     if colorRef >= 30 :
    #         color = "#" + self.colorTuppleSrm[30]
    #     elif colorRef <= 1 :
    #         color = "#" + self.colorTuppleSrm[0]
    #     else :
    #         color = "#" + self.colorTuppleSrm[colorRef-1]
    #     self.widgetColor.setStyleSheet("background-color :" + color)


    def liste_fichiers_recettes(self, rootdir):
        for root, subFolders, files in os.walk(rootdir):
            for file2 in files:
                yield (file2, os.path.join(root,file2))

    def listdir(self, rootdir) :
        summaries=[]
        for filename, recipe in self.liste_fichiers_recettes(rootdir):
            try :
                summaries.append(self.jsonRecipeLib(recipe))
            except :
                logger.debug("le fichier %s n'est pas une recette" %(recipe))
        self.recipesSummary = "[" + ",".join(summaries) + "]"
        logger.debug("%s recettes détectées" %(len(summaries)))



    def jsonRecipeLib(self,recipe) :
        self.s = recipe
        self.recipe = Recipe.parse(recipe)
        data = self.recipe.export("json")
        data = data[1:-1]
        return data



    def initRep(self) :
        home = QtCore.QDir(home_dir)
        config = QtCore.QDir(config_dir)
        logger.debug (config)
        if not config.exists() :
            home.mkpath (config_dir)
        else :
            pass
        database = QtCore.QFile(database_file)
        if not database.exists() :
            database.copy(database_root, database_file)
        else :
            pass
        recettes = QtCore.QFile(recettes_dir)
        if not recettes.exists() :
            try :
                shutil.copytree(samples_dir, samples_target)
            except :
                home.mkpath(recettes_dir)
        mash  = QtCore.QFile(mash_file)
        if not mash.exists() :
            mash.copy(mash_root, mash_file)
        else :
            pass
        journal  = QtCore.QFile(journal_file)
        if not journal.exists() :
            journal.copy(journal_root, journal_file)
        else :
            pass

        # on configure des valeurs par défaut
        if not settings.conf.contains("BoilOffRate") :
            settings.conf.setValue("BoilOffRate", 10)
        if not settings.conf.contains("CoolingLoss") :
            settings.conf.setValue("CoolingLoss", 5)
        if not settings.conf.contains("GrainTemp") :
            settings.conf.setValue("GrainTemp", 20)
        if not settings.conf.contains("FudgeFactor") :
            settings.conf.setValue("FudgeFactor", 1.7)
        if not settings.conf.contains("GrainRetention") :
            settings.conf.setValue("GrainRetention", 1)
        if not settings.conf.contains("Menus") :
            settings.conf.setValue("Menus", "button")


    def prefReload(self) :
        if platform == 'win32':
            recettes_dir = settings.conf.value("pathWin32")
        else :
            recettes_dir = settings.conf.value("pathUnix")
        self.initRep()
        self.initKintoClient()
        self.listdir(recettes_dir)
        self.showLib()

    def initKintoClient(self):
        self.kinto_client = None
        kinto_url = settings.conf.value("KintoServerUrl")
        if kinto_url is not None and kinto_url != "":
            kinto_bucket = settings.conf.value("KintoDefaultBucket")
            kinto_credentials=(settings.conf.value("KintoBasicUserCred"), settings.conf.value("KintoBasicPasswordCred"))
            logger.debug(kinto_credentials)
            if kinto_bucket is None or kinto_bucket == "":
                kinto_bucket = "joliebulle"
                logger.debug("using default bucket 'joliebulle'")
            try:
                tmp_client = Client(server_url=kinto_url, auth = kinto_credentials)
                tmp_client.create_bucket(id=kinto_bucket, if_not_exists=True)
                self.kinto_client = Client(server_url=kinto_url, bucket=kinto_bucket, auth = kinto_credentials)

                # Création des collections
                self.kinto_client.create_collection(id='recipes', if_not_exists=True)
                self.kinto_client.create_collection(id='ingredients', if_not_exists=True)
                
                logger.info("Synchronize with kinto server at :" + repr(self.kinto_client))
            except Exception as e:
                logger.warn("Failed to initialize Kinto synchronisation: " + repr(e))

    def syncRecipes(self):
        recipes_collection = 'recipes'
        if self.kinto_client is None:
            return

        # Sync local recipes
        id_recettes_locales=[]
        for filename, full_filename in self.liste_fichiers_recettes(recettes_dir):
            recipe_id = filename.replace('.xml', '')
            id_recettes_locales.append(recipe_id)
            try:
                remote_recipe = self.kinto_client.get_record(id=recipe_id, collection=recipes_collection)
                logger.debug("recipe " + recipe_id + " already exists on server, update if needed")
                remote_timestamp = int(remote_recipe['data']['last_modified']/1000)
                local_timestamp = int(os.path.getmtime(full_filename))
                logger.debug(str(remote_timestamp) + " " + str(local_timestamp))
                if remote_timestamp < local_timestamp:
                    logger.info("Mise à jour de la recette distante " + recipe_id)
                    # La recette distant doit être mise à jour
                    local_recipe = Recipe.parse(full_filename)
                    data = local_recipe.export("dict")
                    ret = self.kinto_client.update_record(id=recipe_id, collection=recipes_collection, data=data)
                    new_timestamp = ret['data']['last_modified']
                    #MAJ du mtime du fichier pour marquer la synchronisation
                    os.utime(full_filename, times=(int(new_timestamp/1000), int(new_timestamp/1000)))
                if remote_timestamp > local_timestamp:
                    # La recette distante doit être mise à jour
                    logger.info("Mise à jour de la recette locale " + recipe_id)
                    new_recipe = Recipe.parse(remote_recipe['data'], "dict")
                    self.doEnregistrerRecette(new_recipe, full_filename)
                    os.utime(full_filename, times=(int(remote_timestamp), int(remote_timestamp)))
            except KintoException:
                logger.debug("recipe " + recipe_id + " doesn't exists on server, sending it")
                new_recipe = Recipe.parse(full_filename)
                data = new_recipe.export("dict")
                ret = self.kinto_client.create_record(id=recipe_id, collection=recipes_collection, data=data)
                new_timestamp = ret['data']['last_modified']
                #MAJ du mtime du fichier pour marquer la synchronisation
                os.utime(full_filename, times=(int(new_timestamp/1000), int(new_timestamp/1000)))

        recipes = self.kinto_client.get_records(collection='recipes')
        for recipe in recipes:
            if recipe['id'] not in id_recettes_locales:
                #Recette distante non présente localement
                new_recipe = Recipe.parse(recipe, "dict")
                fullname = recipe['id'] + ".xml"
                logger.debug("Création de la recette " + fullname)
                self.doEnregistrerRecette(new_recipe, os.path.join(recettes_dir, fullname))
                os.utime(full_filename, times=(int(remote_timestamp), int(remote_timestamp)))


    @QtCore.pyqtSlot()
    def switchToLibrary(self) :
        self.stackedWidget.setCurrentIndex(0)
        # self.viewRecipeLib(self.s)

    def switchToMash(self) :
        self.stackedWidget.setCurrentIndex(2)


    def restoreDataBase(self) :
        home = QtCore.QDir(home_dir)
        config = QtCore.QDir(config_dir)
        database = QtCore.QFile(database_file)
        confirmation = QtWidgets.QMessageBox.question(self,
                                    self.tr("Remplacer la base ?"),
                                    self.tr("La base des ingrédients actuelle va être effacée et remplacée par la base originale. Toutes vos modifications vont être effacées. Un redémarrage de l'application sera nécessaire.<br> Continuer ?"),
                                    QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
        if (confirmation == QtWidgets.QMessageBox.Yes):
            database.remove(database_file)
            database.copy(database_root, database_file)
        else :

            pass


    def editGrains(self) :
        self.dlgEditG.setModal(True)
        self.dlgEditG.setModel()
        self.dlgEditG.show()

    def editHoublons(self) :
        self.dlgEditH.setModal(True)
        self.dlgEditH.setModel()
        self.dlgEditH.show()

    def editDivers(self) :
        self.dlgEditD.setModal(True)
        self.dlgEditD.setModel()
        self.dlgEditD.show()

    def editLevures(self) :
        self.dlgEditY.setModal(True)
        self.dlgEditY.setModel()
        self.dlgEditY.show()

    @QtCore.pyqtSlot(float, float, float, float)
    def preBoilCheck(self,volPreBoil,preBoilSg,GU,volume) :
        self.dlgPreBoil = DialogPreBoil(self)
        self.dlgPreBoil.setData(volPreBoil,preBoilSg,GU,volume)
        self.dlgPreBoil.setModal(True)
        self.dlgPreBoil.show()


    def dialogPreferences (self) :
        self.dlgPref.setModal(True)
        self.dlgPref.show()




    def importBeerXML(self) :
        fichierBeerXML = self.s
        try:
            self.recipe = Recipe.parse(fichierBeerXML)
            self.currentRecipeMash = self.recipe.mash

        except :
            errors = Errors()
            errors.warningXml()


    def about(self) :
        about = DialogAbout(self)
        about.show()


    def doEnregistrerRecette(self, recipe, destination):
        recipeFile = QtCore.QFile(destination)
        if recipeFile.open(QtCore.QIODevice.WriteOnly):
            try:
                stream = QtCore.QTextStream(recipeFile)
                stream.setCodec("UTF-8")
                stream << recipe.export("beerxml")
            finally:
                recipeFile.close()
        else:
            # TODO : Prévenir l'utilisateur en cas d'échec de l'enregistrement
            pass

    def enregistrerRecette(self, destination):
        self.doEnregistrerRecette(self.recipe, destination)
        self.fileSaved = True

    def enregistrer (self) :
        if self.recipe.name != self.lineEditRecette.text() :
            self.nameChanged = True
        else :
            self.nameChanged = False

        self.recipe.name = self.lineEditRecette.text()
        self.recipe.style = self.lineEditGenre.text()
        self.recipe.brewer = self.lineEditBrewer.text()
        self.recipe.boil = self.spinBoxBoil.value()
        if not self.s:
            destination = recettes_dir + "/" + self.recipe.name.replace('/', ' ') + ".xml"
            if os.path.exists(destination) :
                errors=Errors()
                errors.warningExistingPath()
                self.fileSaved = False
            else :
                self.s = destination
                self.enregistrerRecette(destination)
        else :
            self.enregistrerRecette(self.s)




    def enregistrerSous (self) :
        self.s = QtGui.QFileDialog.getSaveFileName (self,
                                                    self.tr("Enregistrer dans un fichier"),
                                                    recettes_dir + "/" + self.recipe.name.replace('/', ' ') + ".xml",
                                                    "BeerXML (*.xml)")
        self.enregistrerRecette(self.s)

    @QtCore.pyqtSlot(str)
    def copyBbcode (self, bbcode):
        app.clipboard().setText(bbcode)


    def importIng(self):
        s = QtWidgets.QFileDialog.getOpenFileName(self,
            self.tr("Ouvrir un fichier"),
            home_dir,
            )
        if not s :
            pass
        else :
            self.importIngList = ImportIng()
            self.importIngList.parseFile(s)


    def mashComboChanged (self) :
        #on remet le verrou à 0, il va falloir recalculer en repassant en brewday mode
        self.brewdayLock = 0
        try :
            i =self.comboBoxMashProfiles.currentIndex()
            self.currentMash = ImportBase().listeMashes[i]
        except :
            self.currentMash = self.currentRecipeMash
        if i == -1 :
            self.currentMash = Mash()
        self.recipe.mash = self.currentMash

    def seeMash(self) :
        self.switchToMash()
        index = self.listWidgetMashProfiles.currentRow()
        i = self.listWidgetSteps.currentRow()
        self.listWidgetMashProfiles.clear()
        self.listWidgetSteps.clear()

        self.numMash = len(ImportBase().listeMashes)
        #self.numSteps = self.mashProfilesBase.numSteps
        self.popMashList()
        self.pushButtonMashEdit.setEnabled(False)
        self.pushButtonRemoveProfile.setEnabled(False)
        self.pushButtonStepRemove.setEnabled(False)
        self.pushButtonStepEdit.setEnabled(False)
        self.listWidgetMashProfiles.setCurrentRow(index)
        self.listWidgetSteps.setCurrentRow(i)

    def popMashList(self) :
        self.listWidgetMashProfiles.clear()
        for mash in ImportBase().listeMashes :
           self.listWidgetMashProfiles.addItem(mash.name)

    def mashClicked(self) :
        self.listWidgetSteps.clear()
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            mash = ImportBase().listeMashes[index]
            for step in mash.listeSteps :
                self.listWidgetSteps.addItem(step.name)

            self.labelStepName.setTextFormat(QtCore.Qt.RichText)
            self.labelMashName.setText("<b>" + mash.name + "</b>")
            self.labelMashPh.setText("%.1f" %float(mash.ph))
    #        self.labelMashGrainTemp.setText("%.1f" %float(self.dicMashDetail['grainTemp']))
    #        self.labelMashTunTemp.setText("%.1f" %float(self.dicMashDetail['tunTemp']))
            try :
                self.labelMashSpargeTemp.setText("%.1f" %float(mash.spargeTemp))
            except :
                pass
            try :
                self.listWidgetSteps.setCurrentRow(0)
            except :
                pass
    #        print(self.dicMashDetail)
            self.pushButtonMashEdit.setEnabled(True)
            self.pushButtonRemoveProfile.setEnabled(True)

    def mashDetails(self) :
        self.dlgMashDetail = DialogMashDetail(self)
        self.dlgMashDetail.setModal(True)
        self.dlgMashDetail.show()
        self.dlgMashDetail.setFields(self.currentMash)
        self.dlgMashDetail.setAttribute( QtCore.Qt.WA_DeleteOnClose, True )


    def stepDetails(self) :
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                try:
                    selected_step = selected_mash.listeSteps[i]
                    self.labelStepName.setTextFormat(QtCore.Qt.RichText)
                    self.labelStepName.setText("<b>" + selected_step.name +"</b>")
                    self.labelStepType.setText(selected_step.type)
                    self.labelStepTemp.setText(MashStepView.temp_to_display(selected_step.temp))
                    self.labelStepTime.setText(MashStepView.time_to_display(selected_step.time))
                    self.pushButtonStepRemove.setEnabled(True)
                    self.pushButtonStepEdit.setEnabled(True)
                except:
                    pass


    def stepEdit(self) :
        index = self.listWidgetMashProfiles.currentRow()
        if  index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                selected_step = selected_mash.listeSteps[i]

                self.dlgStep.show()
                self.dlgStep.fields (selected_step)

    def stepReload(self, step) :
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                selected_step = selected_mash.listeSteps[i]

                selected_step.name = step.name
                selected_step.type = step.type
                selected_step.temp = step.temp
                selected_step.time = step.time
                self.seeMash()
                self.stepDetails()
                self.listWidgetMashProfiles.setCurrentRow(index)
                self.listWidgetSteps.setCurrentRow(i)

    def removeStep(self) :
        index = self.listWidgetMashProfiles.currentRow()
        if index > -1:
            selected_mash = ImportBase().listeMashes[index]
            i = self.listWidgetSteps.currentRow()
            if i > -1:
                item = self.listWidgetSteps.currentItem()
                del selected_mash.listeSteps[i]
                # self.listWidgetSteps.clearSelection()
                #self.listWidgetSteps.takeItem(item)
                #On force la sélection sur la ligne précédente
                self.listWidgetSteps.setCurrentRow(i-1)
                self.seeMash()

    def addStep(self) :
        index = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[index]
        i = self.listWidgetSteps.currentRow()
        step = MashStep()
        step.name = 'Nouveau palier'
        step.type = 'Infusion'
        step.time = '0'
        step.temp = '0'
        step.vol = '0'
        selected_mash.listeSteps.append(step)

        self.listWidgetMashProfiles.setCurrentRow(index)
        self.seeMash()
        self.stepDetails()
        self.listWidgetMashProfiles.setCurrentRow(index)
        # self.listWidgetSteps.setCurrentRow(i-1)
        # self.stepEdit()

    def mashEdit(self) :
        index = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[index]
        self.dlgMash.show()
        self.dlgMash.fields(selected_mash)

    def mashReload(self,mash) :
        #on remet le verrou à 0, il va falloir recalculer en repassant en brewday mode
        self.brewdayLock = 0
        f = self.listWidgetMashProfiles.currentRow()
        selected_mash = ImportBase().listeMashes[f]
        selected_mash.name = mash.name
        selected_mash.ph = mash.ph
        selected_mash.grainTemp = 20
        selected_mash.tunTemp = 20
        selected_mash.spargeTemp = mash.spargeTemp
        self.popMashList()
        self.listWidgetMashProfiles.setCurrentRow(f)

    def addMash(self) :
        new_mash = Mash()
        new_mash.name = 'Nouveau profil'
        new_mash.grainTemp = '0'
        new_mash.tunTemp = '0'
        new_mash.spargeTemp = '78'
        new_mash.ph = 5.4
        new_step = MashStep()
        new_step.name = 'Nouveau Palier'
        new_step.type = 'Infusion'
        new_step.time = '0'
        new_step.temp = '0'
        new_mash.listeSteps.append(new_step)
        ImportBase().listeMashes.append(new_mash)
        self.seeMash()
        self.listWidgetMashProfiles.setCurrentRow(len(ImportBase().listeMashes)-1)

    def removeMash(self) :
        i = self.listWidgetMashProfiles.currentRow()
        del ImportBase().listeMashes[i]
        self.seeMash()
        self.listWidgetSteps.clear()

    def mashRejected (self) :
        self.showLib()

    def saveProfile(self) :
        self.mashProfileExport.export(ImportBase().listeMashes)
        self.mashProfileExport.enregistrer(mash_file)

    @QtCore.pyqtSlot()
    def printRecipe (self) :
        printer=QtPrintSupport.QPrinter()
        dialog = QtPrintSupport.QPrintDialog(printer)
        dialog.setModal(True)
        dialog.setWindowTitle("Print Document" )
        if dialog.exec_() == True:
            self.webViewBiblio.print(printer)
            # document=QtGui.QTextDocument()
            # stringHtml=self.recipe.export("print")
            # document.setHtml(stringHtml)
            # document.print(printer)


    @QtCore.pyqtSlot()
    def printBrewday(self) :
        printer=QtPrintSupport.QPrinter()
        dialog = QtPrintSupport.QPrintDialog(printer)
        dialog.setModal(True)
        dialog.setWindowTitle("Print Document" )
        if dialog.exec_() == True:
            self.webViewBrewday.print(printer)
Beispiel #44
0
class RecordTest(unittest.TestCase):
    def setUp(self):
        self.session = mock.MagicMock()
        self.client = Client(
            session=self.session, bucket='mybucket',
            collection='mycollection')

    def test_record_id_is_given_after_creation(self):
        mock_response(self.session, data={'id': 5678})
        record = self.client.create_record({'foo': 'bar'})
        assert 'id' in record['data'].keys()

    def test_generated_record_id_is_an_uuid(self):
        mock_response(self.session)
        self.client.create_record({'foo': 'bar'})
        id = self.session.request.mock_calls[0][1][1].split('/')[-1]

        uuid_regexp = r'[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}'
        self.assertRegexpMatches(id, uuid_regexp)

    def test_records_handles_permissions(self):
        mock_response(self.session)
        self.client.create_record(
            {'id': '1234', 'foo': 'bar'},
            permissions=mock.sentinel.permissions)
        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/mycollection/records/1234',
            data={'foo': 'bar', 'id': '1234'},
            permissions=mock.sentinel.permissions,
            headers=DO_NOT_OVERWRITE)

    def test_collection_argument_takes_precedence(self):
        mock_response(self.session)
        # Specify a different collection name for the client and the operation.
        client = Client(session=self.session, bucket='mybucket',
                        collection='wrong_collection')
        client.update_record(data={'id': '1234'}, collection='good_collection',
                             permissions=mock.sentinel.permissions)

        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/good_collection/records/1234',
            data={'id': '1234'},
            headers=None,
            permissions=mock.sentinel.permissions)

    def test_record_id_is_derived_from_data_if_present(self):
        mock_response(self.session)
        self.client.create_record(data={'id': '1234', 'foo': 'bar'},
                                  permissions=mock.sentinel.permissions)

        self.session.request.assert_called_with(
            'put',
            '/buckets/mybucket/collections/mycollection/records/1234',
            data={'id': '1234', 'foo': 'bar'},
            permissions=mock.sentinel.permissions,
            headers=DO_NOT_OVERWRITE)

    def test_data_and_permissions_are_added_on_create(self):
        mock_response(self.session)
        data = {'foo': 'bar'}
        permissions = {'read': ['mle']}

        self.client.create_record(
            id='1234',
            data=data,
            permissions=permissions)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=permissions,
            headers=DO_NOT_OVERWRITE)

    def test_creation_sends_if_none_match_by_default(self):
        mock_response(self.session)
        data = {'foo': 'bar'}

        self.client.create_record(
            id='1234',
            data=data)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=None, headers=DO_NOT_OVERWRITE)

    def test_creation_doesnt_add_if_none_match_when_overwrite(self):
        mock_response(self.session)
        data = {'foo': 'bar'}

        self.client.create_record(id='1234', data=data, safe=False)

        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with(
            'put', url, data=data, permissions=None, headers=None)

    def test_records_issues_a_request_on_delete(self):
        mock_response(self.session)
        self.client.delete_record('1234')
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('delete', url, headers=None)

    def test_record_issues_a_request_on_retrieval(self):
        mock_response(self.session, data={'foo': 'bar'})
        record = self.client.get_record('1234')

        self.assertEquals(record['data'], {'foo': 'bar'})
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('get', url)

    def test_collection_can_retrieve_all_records(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}])
        records = self.client.get_records()
        assert list(records) == [{'id': 'foo'}, {'id': 'bar'}]

    def test_collection_can_retrieve_records_timestamp(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        timestamp = self.client.get_records_timestamp()
        assert timestamp == '12345'

    def test_records_timestamp_is_cached(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        self.client.get_records()
        timestamp = self.client.get_records_timestamp()
        assert timestamp == '12345'
        assert self.session.request.call_count == 1

    def test_records_timestamp_is_cached_per_collection(self):
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"12345"'})
        self.client.get_records(collection="foo")
        mock_response(self.session, data=[{'id': 'foo'}, {'id': 'bar'}],
                      headers={"ETag": '"67890"'})
        self.client.get_records(collection="bar")

        timestamp = self.client.get_records_timestamp("foo")
        assert timestamp == '12345'

        timestamp = self.client.get_records_timestamp("bar")
        assert timestamp == '67890'

    def test_pagination_is_followed(self):
        # Mock the calls to request.
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            # First one returns a list of items with a pagination token.
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            # Second one returns a list of items without a pagination token.
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
            ),
        ]
        records = self.client.get_records('bucket', 'collection')

        assert list(records) == [
            {'id': '1', 'value': 'item1'},
            {'id': '2', 'value': 'item2'},
            {'id': '3', 'value': 'item3'},
            {'id': '4', 'value': 'item4'},
        ]

    def test_pagination_supports_if_none_match(self):
        link = ('http://example.org/buckets/buck/collections/coll/records/'
                '?token=1234')

        self.session.request.side_effect = [
            # First one returns a list of items with a pagination token.
            build_response(
                [{'id': '1', 'value': 'item1'},
                 {'id': '2', 'value': 'item2'}, ],
                {'Next-Page': link}),
            # Second one returns a list of items without a pagination token.
            build_response(
                [{'id': '3', 'value': 'item3'},
                 {'id': '4', 'value': 'item4'}, ],
            ),
        ]
        self.client.get_records('bucket', 'collection',
                                if_none_match="1234")

        # Check that the If-None-Match header is present in the requests.
        self.session.request.assert_any_call(
            'get', '/buckets/collection/collections/bucket/records',
            headers={'If-None-Match': '"1234"'}, params={})
        self.session.request.assert_any_call(
            'get', link, headers={'If-None-Match': '"1234"'}, params={})

    def test_collection_can_delete_a_record(self):
        mock_response(self.session, data={'id': 1234})
        resp = self.client.delete_record(id=1234)
        assert resp == {'id': 1234}
        url = '/buckets/mybucket/collections/mycollection/records/1234'
        self.session.request.assert_called_with('delete', url, headers=None)

    def test_record_delete_if_match(self):
        data = {}
        mock_response(self.session, data=data)
        deleted = self.client.delete_record(
            collection='mycollection',
            bucket='mybucket',
            id='1',
            if_match=1234)
        assert deleted == data
        url = '/buckets/mybucket/collections/mycollection/records/1'
        self.session.request.assert_called_with(
            'delete', url, headers={'If-Match': '"1234"'})

    def test_record_delete_if_match_not_included_if_not_safe(self):
        data = {}
        mock_response(self.session, data=data)
        deleted = self.client.delete_record(
            collection='mycollection',
            bucket='mybucket',
            id='1',
            if_match=1234,
            safe=False)
        assert deleted == data
        url = '/buckets/mybucket/collections/mycollection/records/1'
        self.session.request.assert_called_with(
            'delete', url, headers=None)

    def test_update_record_gets_the_id_from_data_if_exists(self):
        mock_response(self.session)
        self.client.update_record(
            bucket='mybucket', collection='mycollection',
            data={'id': 1, 'foo': 'bar'})

        self.session.request.assert_called_with(
            'put', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=None, permissions=None)

    def test_update_record_handles_if_match(self):
        mock_response(self.session)
        self.client.update_record(
            bucket='mybucket', collection='mycollection',
            data={'id': 1, 'foo': 'bar'}, if_match=1234)

        headers = {'If-Match': '"1234"'}
        self.session.request.assert_called_with(
            'put', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=headers, permissions=None)

    def test_patch_record_uses_the_patch_method(self):
        mock_response(self.session)
        self.client.patch_record(
            bucket='mybucket', collection='mycollection',
            data={'id': 1, 'foo': 'bar'})

        self.session.request.assert_called_with(
            'patch', '/buckets/mybucket/collections/mycollection/records/1',
            data={'id': 1, 'foo': 'bar'}, headers=None, permissions=None)

    def test_update_record_raises_if_no_id_is_given(self):
        with self.assertRaises(KeyError) as cm:
            self.client.update_record(
                data={'foo': 'bar'},  # Omit the id on purpose here.
                bucket='mybucket',
                collection='mycollection'
            )
        assert text_type(cm.exception) == (
            "'Unable to update a record, need an id.'")

    def test_get_or_create_doesnt_raise_in_case_of_conflict(self):
        data = {
            'permissions': mock.sentinel.permissions,
            'data': {'foo': 'bar'}
        }
        self.session.request.side_effect = [
            get_http_error(status=412),
            (data, None)
        ]
        returned_data = self.client.create_record(
            bucket="buck",
            collection="coll",
            data={'id': 1234,
                  'foo': 'bar'},
            if_not_exists=True)  # Should not raise.
        assert returned_data == data

    def test_get_or_create_raise_in_other_cases(self):
        self.session.request.side_effect = get_http_error(status=500)
        with self.assertRaises(KintoException):
            self.client.create_record(
                bucket="buck",
                collection="coll",
                data={'foo': 'bar'},
                if_not_exists=True)

    def test_create_record_raises_a_special_error_on_403(self):
        self.session.request.side_effect = get_http_error(status=403)
        with self.assertRaises(KintoException) as e:
            self.client.create_record(
                bucket="buck",
                collection="coll",
                data={'foo': 'bar'})
        expected_msg = ("Unauthorized. Please check that the collection exists"
                        " and that you have the permission to create or write "
                        "on this collection record.")
        assert e.exception.message == expected_msg
Beispiel #45
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