Пример #1
0
def test_rolling_ttl_remove_expirary(params_from_base_test_setup, sg_conf_name):
    """
    1. PUT /db/doc1 via SG with property "_exp":3
    2. Once per second for 10 seconds, update /db/doc1 with a new revision (also with "_exp":3)
    3. Update /db/doc1 with a revision with no expiry
    3. Get /db/doc1.  Assert response is 200
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(
        cluster_config=cluster_config,
        sync_gateway_config=sg_conf
    )

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_rolling_ttl_remove_expirary'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin, db=sg_db, name=sg_user_name, password=sg_user_password, channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin, db=sg_db, name=sg_user_name)

    doc_exp_3_body = document.create_doc(doc_id="exp_3", expiry=3, channels=sg_user_channels)
    doc_exp_10_body = document.create_doc(doc_id="exp_10", expiry=10, channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_3_body, auth=sg_user_session)
    doc_exp_10 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_10_body, auth=sg_user_session)

    client.update_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], number_updates=10, expiry=3, delay=1, auth=sg_user_session)
    client.update_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], number_updates=1, auth=sg_user_session)

    # If expiry was not removed in the last update, this would expire doc_exp_3
    time.sleep(5)

    # doc_exp_3 should still be around due to removal of expiry
    doc_exp_3 = client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], auth=sg_user_session)
    assert doc_exp_3["_id"] == "exp_3"

    # doc_exp_10 should be expired due to the updates (10s) + sleep (5s)
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_10["id"], auth=sg_user_session)
    assert he.value[0].startswith("404 Client Error: Not Found for url:")
Пример #2
0
def test_rolling_ttl_expires(params_from_base_test_setup, sg_conf_name):
    """
    1. PUT /db/doc1 via SG with property "_exp":3
    2. Update /db/doc1 10 times with a new revision (also with "_exp":3)
    3. Wait 5 seconds
    4. Get /db/doc1.  Assert response is 200
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(
        cluster_config=cluster_config,
        sync_gateway_config=sg_conf
    )

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_rolling_ttl_expires'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin, db=sg_db, name=sg_user_name, password=sg_user_password, channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin, db=sg_db, name=sg_user_name)

    doc_exp_3_body = document.create_doc(doc_id="exp_3", expiry=3, channels=sg_user_channels)
    doc_exp_10_body = document.create_doc(doc_id="exp_10", expiry=10, channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_3_body, auth=sg_user_session)
    doc_exp_10 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_10_body, auth=sg_user_session)

    client.update_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], number_updates=10, expiry=3, auth=sg_user_session)

    # Sleep should allow doc_exp_3 to expire, but still be in the window to get doc_exp_10
    time.sleep(5)

    # doc_exp_3 should be expired
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], auth=sg_user_session)
    assert he.value[0].startswith("404 Client Error: Not Found for url:")

    # doc_exp_10 should be available still
    doc_exp_10_result = client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_10["id"], auth=sg_user_session)
    assert doc_exp_10_result["_id"] == "exp_10"
Пример #3
0
def test_removing_expiry(params_from_base_test_setup, sg_conf_name):
    """
    1. PUT /db/doc1 via SG with property "_exp":3
    2. Update /db/doc1 with a new revision with no expiry value
    3. After 10 updates, update /db/doc1 with a revision with no expiry
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(
        cluster_config=cluster_config,
        sync_gateway_config=sg_conf
    )

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_removing_expiry'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin, db=sg_db, name=sg_user_name, password=sg_user_password, channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin, db=sg_db, name=sg_user_name)

    doc_exp_3_body = document.create_doc(doc_id="exp_3", expiry=3, channels=sg_user_channels)
    doc_exp_10_body = document.create_doc(doc_id="exp_10", expiry=10, channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_3_body, auth=sg_user_session)
    doc_exp_10 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_10_body, auth=sg_user_session)

    doc_exp_3_updated = client.update_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], number_updates=10, auth=sg_user_session)

    # Sleep should allow an expiry to happen on doc_exp_3 if it had not been removed.
    # Expected behavior is that the doc_exp_3 will still be around due to the removal of the expiry
    time.sleep(5)

    # doc_exp_3 should no longer have an expiry and should not raise an exception
    doc_exp_3_updated_result = client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3_updated["id"], auth=sg_user_session)
    assert doc_exp_3_updated_result["_id"] == "exp_3"

    # doc_exp_10 should be available still and should not raise an exception
    doc_exp_10_result = client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_10["id"], auth=sg_user_session)
    assert doc_exp_10_result["_id"] == "exp_10"
def test_raw_attachment(setup_client_syncgateway_test):
    """
    1.  Add Text attachment to sync_gateway
    2.  Try to get the raw attachment
    Pass: It is possible to get the raw attachment
    """

    log_info("Running 'test_raw_attachment'")

    ls_url = setup_client_syncgateway_test["ls_url"]
    log_info("ls_url: {}".format(ls_url))

    client = MobileRestClient()

    ls_db = client.create_database(ls_url, name="ls_db")

    ls_user_channels = ["NBC"]

    doc_with_att = document.create_doc(
        doc_id="att_doc",
        content={"sample_key": "sample_val"},
        attachment_name="sample_text.txt",
        channels=ls_user_channels,
    )

    doc = client.add_doc(url=ls_url, db=ls_db, doc=doc_with_att)

    att = client.get_attachment(url=ls_url, db=ls_db, doc_id=doc["id"], attachment_name="sample_text.txt")

    expected_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

    assert expected_text == att
def test_raw_attachment(setup_client_syncgateway_test):
    """
    1.  Add Text attachment to sync_gateway
    2.  Try to get the raw attachment
    Pass: It is possible to get the raw attachment
    """

    log_info("Running 'test_raw_attachment'")

    ls_url = setup_client_syncgateway_test["ls_url"]
    log_info("ls_url: {}".format(ls_url))

    client = MobileRestClient()

    ls_db = client.create_database(ls_url, name="ls_db")

    ls_user_channels = ["NBC"]

    atts = attachment.load_from_data_dir(["sample_text.txt"])
    doc_with_att = document.create_doc(doc_id="att_doc",
                                       content={"sample_key": "sample_val"},
                                       attachments=atts,
                                       channels=ls_user_channels)

    doc = client.add_doc(url=ls_url, db=ls_db, doc=doc_with_att)

    att = client.get_attachment(url=ls_url,
                                db=ls_db,
                                doc_id=doc["id"],
                                attachment_name="sample_text.txt")

    expected_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

    assert expected_text == att
def test_writing_attachment_to_couchbase_server(params_from_base_test_setup, sg_conf_name):
    """
    1. Start sync_gateway with sync function that rejects all writes:
    function(doc, oldDoc) {
      throw({forbidden:"No writes!"});
    }
    2. Create a doc with attachment
    3. Use CBS sdk to see if attachment doc exists.  Doc ID will look like _sync:att:sha1-Kq5sNclPz7QV2+lfQIuc6R7oRu0= (where the suffix is the digest)
    4. Assert att doc does not exist
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    cluster_helper.reset_cluster(cluster_config, sg_conf)

    topology = cluster_helper.get_cluster_topology(cluster_config)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]
    sg_db = "db"
    bucket = "data-bucket"

    log_info("Running 'test_writing_attachment_to_couchbase_server'")
    log_info("Using cbs_url: {}".format(cbs_url))
    log_info("Using sg_url: {}".format(sg_url))
    log_info("Using sg_url_admin: {}".format(sg_url_admin))
    log_info("Using sg_db: {}".format(sg_db))
    log_info("Using bucket: {}".format(bucket))

    sg_user_name = "sg_user"
    sg_user_password = "******"

    sg_user_channels = ["NBC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin, db=sg_db, name=sg_user_name, password=sg_user_password, channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin, db=sg_db, name=sg_user_name)

    docs = client.add_docs(url=sg_url, db=sg_db, number=100, id_prefix=sg_db, channels=sg_user_channels, auth=sg_user_session)
    assert len(docs) == 100

    # Create doc with attachment and push to sync_gateway
    doc_with_att = document.create_doc(doc_id="att_doc", content={"sample_key": "sample_val"}, attachment_name="sample_text.txt", channels=sg_user_channels)

    client.add_doc(url=sg_url, db=sg_db, doc=doc_with_att, auth=sg_user_session)
    server = CouchbaseServer(cbs_url)

    # Assert that the attachment doc gets written to couchbase server
    server_att_docs = server.get_server_docs_with_prefix(bucket=bucket, prefix="_sync:att:")
    num_att_docs = len(server_att_docs)
    assert num_att_docs == 1
Пример #7
0
def test_stale_revision_should_not_be_in_the_index(setup_client_syncgateway_test):
    """original ticket: https://github.com/couchbase/couchbase-lite-android/issues/855

    scenario:
    1. Running sync_gateway
    2. Create database and starts both push and pull replicators through client REST API
    3. Create two or more views through client REST API
    4. Add doc, and verify doc is index with current revision through client REST API
    5. Make sure document is pushed to sync gateway through sync gateway REST API
    6. Update doc with sync gateway (not client side) through sync gateway REST API
    7. Make sure updated document is pull replicated to client  through client REST API
    8. Make sure updated document is indexed through client REST API
    9. Make sure stale revision is deleted from index.  through client REST API
    10. Pass criteria
    """

    cluster_config = setup_client_syncgateway_test["cluster_config"]
    ls_url = setup_client_syncgateway_test["ls_url"]
    sg_url = setup_client_syncgateway_test["sg_url"]
    sg_admin_url = setup_client_syncgateway_test["sg_admin_url"]

    num_docs = 10
    num_revs = 100

    d_doc_name = "dd"
    sg_db = "db"
    sg_user_name = "sg_user"

    sg_helper = SyncGateway()
    sg_helper.start_sync_gateway(
        cluster_config=cluster_config,
        url=sg_url,
        config="{}/walrus.json".format(SYNC_GATEWAY_CONFIGS)
    )

    log_info("Running 'test_stale_revision_should_not_be_in_the_index'")
    log_info("ls_url: {}".format(ls_url))
    log_info("sg_admin_url: {}".format(sg_admin_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("num_docs: {}".format(num_docs))
    log_info("num_revs: {}".format(num_revs))

    client = MobileRestClient()

    sg_user_channels = ["NBC"]
    client.create_user(url=sg_admin_url, db=sg_db, name=sg_user_name, password="******", channels=sg_user_channels)
    sg_session = client.create_session(url=sg_admin_url, db=sg_db, name=sg_user_name)

    view = """{
    "language" : "javascript",
    "views" : {
        "content_view" : {
            "map" : "function(doc, meta) { if (doc.content) { emit(doc._id, doc._rev); } }"
        },
        "update_view" : {
            "map" : "function(doc, meta) { emit(doc.updates, null); }"
        }
    }
}"""

    ls_db = client.create_database(url=ls_url, name="ls_db")

    # Setup continuous push / pull replication from ls_db1 to sg_db
    client.start_replication(
        url=ls_url,
        continuous=True,
        from_db=ls_db,
        to_url=sg_admin_url, to_db=sg_db
    )

    client.start_replication(
        url=ls_url,
        continuous=True,
        from_url=sg_admin_url, from_db=sg_db,
        to_db=ls_db
    )

    design_doc_id = client.add_design_doc(url=ls_url, db=ls_db, name=d_doc_name, doc=view)
    client.get_doc(url=ls_url, db=ls_db, doc_id=design_doc_id)

    doc_body = document.create_doc(doc_id="doc_1", content={"hi": "I should be in the view"}, channels=sg_user_channels)

    log_info(doc_body)

    doc_body_2 = document.create_doc(doc_id="doc_2", channels=sg_user_channels)

    doc = client.add_doc(url=ls_url, db=ls_db, doc=doc_body)
    doc_2 = client.add_doc(url=ls_url, db=ls_db, doc=doc_body_2)

    content_view_rows = client.get_view(url=ls_url, db=ls_db, design_doc_id=design_doc_id, view_name="content_view")
    client.verify_view_row_num(view_response=content_view_rows, expected_num_rows=1)

    update_view_rows = client.get_view(url=ls_url, db=ls_db, design_doc_id=design_doc_id, view_name="update_view")
    client.verify_view_row_num(view_response=update_view_rows, expected_num_rows=2)

    expected_docs_list = [doc, doc_2]
    client.verify_docs_present(url=sg_url, db=sg_db, expected_docs=expected_docs_list, auth=sg_session)

    updated_doc = client.update_doc(url=sg_url, db=sg_db, doc_id=doc["id"], number_updates=10, auth=sg_session)

    client.verify_docs_present(url=ls_url, db=ls_db, expected_docs=updated_doc)

    content_view_rows_2 = client.get_view(url=ls_url, db=ls_db, design_doc_id=design_doc_id, view_name="content_view")
    client.verify_view_row_num(view_response=content_view_rows_2, expected_num_rows=1)

    client.verify_view_contains_keys(view_response=content_view_rows_2, keys=doc["id"])
    client.verify_view_contains_values(view_response=content_view_rows_2, values=updated_doc["rev"])
def test_non_winning_revisions(params_from_base_test_setup, sg_conf_name):
    """ Add non-winning revisions to the revision tree and ensure
    that the changes feed returns the correct revisions

    Steps:
    - Create a doc
    - Add 5 revs
    - changes, assert rev starts with "6-" from 0, store "last_seq_1"
    - Create a conflict off first revision ("2-foo") (POST docs, new_edits == false)
    - changes, assert rev starts with "6-" from "last_seq_1", store "last_seq_2"
    - changes, assert rev starts with "6-" from 0
    - Add a "3-foo" rev with rev "2-foo" as parent
    - changes, assert rev starts with "6-" from "last_seq_2", store "last_seq_3"
    - changes, assert rev starts with "6-" from 0
    - add tombstone rev as child of "6-" i.e. issue delete on ("6-")
    - changes, assert rev starts with "3-foo" from "last_seq_3"
    - changes, assert rev starts with "3-foo" from 0
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    c = cluster.Cluster(cluster_config)
    c.reset(sg_conf)

    client = MobileRestClient()

    seth_user_info = userinfo.UserInfo(
        name="seth",
        password="******",
        channels=["NATGEO"],
        roles=[]
    )

    seth_auth = client.create_user(
        url=sg_admin_url,
        db=sg_db,
        name=seth_user_info.name,
        password=seth_user_info.password,
        channels=seth_user_info.channels
    )

    test_doc_body = document.create_doc(doc_id="test_doc", channels=seth_user_info.channels)
    rev_gen_1_doc = client.add_doc(url=sg_url, db=sg_db, doc=test_doc_body, auth=seth_auth)

    rev_gen_6_doc = client.update_doc(url=sg_url, db=sg_db, doc_id=rev_gen_1_doc["id"], number_updates=5, auth=seth_auth)
    assert rev_gen_6_doc["rev"].startswith("6-")

    # Get changes until rev generation 6 document shows up
    start = time.time()
    last_seq = 0
    while True:
        if time.time() - start > keywords.constants.CLIENT_REQUEST_TIMEOUT:
            raise keywords.exceptions.TimeoutError("Wait for Replication Status Idle: TIMEOUT")

        changes_1 = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=seth_auth, skip_user_docs=True)
        last_seq = changes_1["last_seq"]

        # break when expected rev shows up in changes feed
        if changes_1["results"] and changes_1["results"][0]["changes"][0]["rev"].startswith("6-"):
            break

    assert len(changes_1["results"]) == 1
    assert changes_1["results"][0]["id"] == "test_doc"
    assert changes_1["results"][0]["changes"][0]["rev"].startswith("6-")

    # Create a conflict off of rev one
    rev_gen_2_doc_conflict = client.add_conflict(
        url=sg_url,
        db=sg_db,
        doc_id=rev_gen_1_doc["id"],
        parent_revisions=rev_gen_1_doc["rev"],
        new_revision="2-foo",
        auth=seth_auth
    )
    assert rev_gen_2_doc_conflict["id"] == "test_doc"
    assert rev_gen_2_doc_conflict["rev"] == "2-foo"

    # Issue changes since changes_1 last_seq above
    changes_2 = client.get_changes(url=sg_url, db=sg_db, since=changes_1["last_seq"], auth=seth_auth)
    assert len(changes_2["results"]) == 1
    assert changes_2["results"][0]["id"] == "test_doc"
    assert changes_2["results"][0]["changes"][0]["rev"].startswith("6-")

    # Issue changes since 0, strip user doc and make sure the doc is still the '6-' rev
    changes_from_0_one = client.get_changes(url=sg_url, db=sg_db, since=0, auth=seth_auth, skip_user_docs=True)
    assert len(changes_from_0_one["results"]) == 1
    assert changes_from_0_one["results"][0]["id"] == "test_doc"
    assert changes_from_0_one["results"][0]["changes"][0]["rev"].startswith("6-")

    # Create a 3-foo rev with 2-foo as the parent
    rev_gen_3_doc_conflict = client.add_conflict(
        url=sg_url,
        db=sg_db,
        doc_id=rev_gen_2_doc_conflict["id"],
        parent_revisions=rev_gen_2_doc_conflict["rev"],
        new_revision="3-foo",
        auth=seth_auth
    )
    assert rev_gen_3_doc_conflict["id"] == "test_doc"
    assert rev_gen_3_doc_conflict["rev"] == "3-foo"

    # Issue changes since changes_2 last_seq above
    changes_3 = client.get_changes(url=sg_url, db=sg_db, since=changes_2["last_seq"], auth=seth_auth)
    assert len(changes_3["results"]) == 1
    assert changes_3["results"][0]["id"] == "test_doc"
    assert changes_3["results"][0]["changes"][0]["rev"].startswith("6-")

    # Issue changes since 0, strip user doc and make sure the doc is still the '6-' rev
    changes_from_0_two = client.get_changes(url=sg_url, db=sg_db, since=0, auth=seth_auth, skip_user_docs=True)
    assert len(changes_from_0_two["results"]) == 1
    assert changes_from_0_two["results"][0]["id"] == "test_doc"
    assert changes_from_0_two["results"][0]["changes"][0]["rev"].startswith("6-")

    # Delete test_doc at rev 6-*
    client.delete_doc(url=sg_url, db=sg_db, doc_id=rev_gen_6_doc["id"], rev=rev_gen_6_doc["rev"], auth=seth_auth)

    # Issue changes since changes_3 last_seq above
    changes_4 = client.get_changes(url=sg_url, db=sg_db, since=changes_3["last_seq"], auth=seth_auth)
    assert len(changes_4["results"]) == 1
    assert changes_4["results"][0]["id"] == "test_doc"
    assert changes_4["results"][0]["changes"][0]["rev"] == "3-foo"

    # Issue a oneshot changes since changes_4 last_seq and assert no results are returned
    changes_5 = client.get_changes(url=sg_url, db=sg_db, since=changes_4["last_seq"], feed="normal", auth=seth_auth)
    assert len(changes_5["results"]) == 0
Пример #9
0
def test_rolling_ttl_remove_expirary(params_from_base_test_setup,
                                     sg_conf_name):
    """
    1. PUT /db/doc1 via SG with property "_exp":3
    2. Once per second for 10 seconds, update /db/doc1 with a new revision (also with "_exp":3)
    3. Update /db/doc1 with a revision with no expiry
    3. Get /db/doc1.  Assert response is 200
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    xattrs_enabled = params_from_base_test_setup['xattrs_enabled']

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(cluster_config=cluster_config,
                                 sync_gateway_config=sg_conf)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_rolling_ttl_remove_expirary'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]
    bucket_name = "data-bucket"
    cbs_ip = host_for_url(cbs_url)

    sdk_client = Bucket('couchbase://{}/{}'.format(cbs_ip, bucket_name),
                        password='******')

    client = MobileRestClient()

    client.create_user(url=sg_url_admin,
                       db=sg_db,
                       name=sg_user_name,
                       password=sg_user_password,
                       channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin,
                                            db=sg_db,
                                            name=sg_user_name)

    doc_exp_3_body = document.create_doc(doc_id="exp_3",
                                         expiry=3,
                                         channels=sg_user_channels)
    doc_exp_10_body = document.create_doc(doc_id="exp_10",
                                          expiry=10,
                                          channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url,
                               db=sg_db,
                               doc=doc_exp_3_body,
                               auth=sg_user_session)
    doc_exp_10 = client.add_doc(url=sg_url,
                                db=sg_db,
                                doc=doc_exp_10_body,
                                auth=sg_user_session)

    client.update_doc(url=sg_url,
                      db=sg_db,
                      doc_id=doc_exp_3["id"],
                      number_updates=10,
                      expiry=3,
                      delay=1,
                      auth=sg_user_session)
    client.update_doc(url=sg_url,
                      db=sg_db,
                      doc_id=doc_exp_3["id"],
                      number_updates=1,
                      auth=sg_user_session)

    # If expiry was not removed in the last update, this would expire doc_exp_3
    time.sleep(5)

    # doc_exp_3 should still be around due to removal of expiry
    doc_exp_3 = client.get_doc(url=sg_url,
                               db=sg_db,
                               doc_id=doc_exp_3["id"],
                               auth=sg_user_session)
    assert doc_exp_3["_id"] == "exp_3"

    # doc_exp_10 should be expired due to the updates (10s) + sleep (5s)
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url,
                       db=sg_db,
                       doc_id=doc_exp_10["id"],
                       auth=sg_user_session)

    # In XATTR mode, the expiry results in a tombstone
    # In Doc Meta mode, the expiry results in a purge
    if xattrs_enabled:
        assert he.value[0].startswith("403 Client Error: Forbidden for url:")
    else:
        assert he.value[0].startswith("404 Client Error: Not Found for url:")

    verify_doc_deletion_on_server(doc_id=doc_exp_10["id"],
                                  sdk_client=sdk_client,
                                  sg_client=client,
                                  sg_admin_url=sg_url_admin,
                                  sg_db=sg_db,
                                  xattrs_enabled=xattrs_enabled)
Пример #10
0
def test_document_attachment_not_list():
    with pytest.raises(TypeError):
        document.create_doc(None, None, ATTACHMENTS[0], None, None)
Пример #11
0
def test_string_expiry_as_unix_date(params_from_base_test_setup, sg_conf_name):
    """
    1. Calculate (server time + 3 seconds) as unix time (i.e. Epoch time, e.g. 1466465122)
    2. PUT /db/doc1 via SG with property "_exp":"[unix time]"
       PUT /db/doc2 via SG with property "_exp":"1767225600"  (Jan 1 2026) Note: the maximum epoch time supported by CBS is maxUint32, or Sun 07 Feb 2106, in case you want to move it out further than 2026.
    3. Wait five seconds
    4. Get /db/doc1.  Assert response is 404
       Get /db/doc2.  Assert response is 200
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    xattrs_enabled = params_from_base_test_setup['xattrs_enabled']

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(cluster_config=cluster_config,
                                 sync_gateway_config=sg_conf)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_string_expiry_as_unix_date'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]
    bucket_name = "data-bucket"
    cbs_ip = host_for_url(cbs_url)

    sdk_client = Bucket('couchbase://{}/{}'.format(cbs_ip, bucket_name),
                        password='******')

    client = MobileRestClient()

    client.create_user(url=sg_url_admin,
                       db=sg_db,
                       name=sg_user_name,
                       password=sg_user_password,
                       channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin,
                                            db=sg_db,
                                            name=sg_user_name)

    time_util = Time()
    unix_time_3s_ahead = time_util.get_unix_timestamp(delta=3)

    # Convert unix timestamp to string
    unix_time_3s_ahead_string = str(unix_time_3s_ahead)

    # Using string representation for unix time
    doc_exp_3_body = document.create_doc(doc_id="exp_3",
                                         expiry=unix_time_3s_ahead_string,
                                         channels=sg_user_channels)
    doc_exp_years_body = document.create_doc(doc_id="exp_years",
                                             expiry="1767225600",
                                             channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url,
                               db=sg_db,
                               doc=doc_exp_3_body,
                               auth=sg_user_session)
    doc_exp_years = client.add_doc(url=sg_url,
                                   db=sg_db,
                                   doc=doc_exp_years_body,
                                   auth=sg_user_session)

    # Sleep should allow doc_exp_3 to expire
    time.sleep(10)

    # doc_exp_3 should be expired
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url,
                       db=sg_db,
                       doc_id=doc_exp_3["id"],
                       auth=sg_user_session)

    # In XATTR mode, the expiry results in a tombstone
    # In Doc Meta mode, the expiry results in a purge
    if xattrs_enabled:
        assert he.value[0].startswith("403 Client Error: Forbidden for url:")
    else:
        assert he.value[0].startswith("404 Client Error: Not Found for url:")

    verify_doc_deletion_on_server(doc_id=doc_exp_3["id"],
                                  sdk_client=sdk_client,
                                  sg_client=client,
                                  sg_admin_url=sg_url_admin,
                                  sg_db=sg_db,
                                  xattrs_enabled=xattrs_enabled)

    # doc_exp_years should be available still
    doc_exp_years_result = client.get_doc(url=sg_url,
                                          db=sg_db,
                                          doc_id=doc_exp_years["id"],
                                          auth=sg_user_session)
    assert doc_exp_years_result["_id"] == "exp_years"
Пример #12
0
def test_numeric_expiry_as_ttl(params_from_base_test_setup, sg_conf_name):
    """
    1. PUT /db/doc1 via SG with property "_exp":3
       PUT /db/doc2 via SG with property "_exp":10
    2. Wait five seconds
    3. Get /db/doc1.  Assert response is 404
       Get /db/doc2.  Assert response is 200
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    xattrs_enabled = params_from_base_test_setup['xattrs_enabled']

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(cluster_config=cluster_config,
                                 sync_gateway_config=sg_conf)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_numeric_expiry_as_ttl'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]
    bucket_name = "data-bucket"
    cbs_ip = host_for_url(cbs_url)

    sdk_client = Bucket('couchbase://{}/{}'.format(cbs_ip, bucket_name),
                        password='******')
    client = MobileRestClient()

    client.create_user(url=sg_url_admin,
                       db=sg_db,
                       name=sg_user_name,
                       password=sg_user_password,
                       channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin,
                                            db=sg_db,
                                            name=sg_user_name)

    doc_exp_3_body = document.create_doc(doc_id="exp_3",
                                         expiry=3,
                                         channels=sg_user_channels)
    doc_exp_10_body = document.create_doc(doc_id="exp_10",
                                          expiry=10,
                                          channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url,
                               db=sg_db,
                               doc=doc_exp_3_body,
                               auth=sg_user_session)
    doc_exp_10 = client.add_doc(url=sg_url,
                                db=sg_db,
                                doc=doc_exp_10_body,
                                auth=sg_user_session)

    # Sleep should allow doc_exp_3 to expire, but still be in the window to get doc_exp_10
    time.sleep(5)

    # doc_exp_3 should be expired
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url,
                       db=sg_db,
                       doc_id=doc_exp_3["id"],
                       auth=sg_user_session)

    # In XATTR mode, the expiry results in a tombstone
    # In Doc Meta mode, the expiry results in a purge
    if xattrs_enabled:
        assert he.value[0].startswith("403 Client Error: Forbidden for url:")
    else:
        assert he.value[0].startswith("404 Client Error: Not Found for url:")

    verify_doc_deletion_on_server(doc_id=doc_exp_3["id"],
                                  sdk_client=sdk_client,
                                  sg_client=client,
                                  sg_admin_url=sg_url_admin,
                                  sg_db=sg_db,
                                  xattrs_enabled=xattrs_enabled)

    # doc_exp_10 should be available still
    doc_exp_10_result = client.get_doc(url=sg_url,
                                       db=sg_db,
                                       doc_id=doc_exp_10["id"],
                                       auth=sg_user_session)
    assert doc_exp_10_result["_id"] == "exp_10"
Пример #13
0
def test_longpoll_awaken_channels(params_from_base_test_setup, sg_conf_name):

    cluster_conf = params_from_base_test_setup["cluster_config"]
    cluster_topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)
    sg_admin_url = cluster_topology["sync_gateways"][0]["admin"]
    sg_url = cluster_topology["sync_gateways"][0]["public"]

    log_info("sg_conf: {}".format(sg_conf))
    log_info("sg_admin_url: {}".format(sg_admin_url))
    log_info("sg_url: {}".format(sg_url))

    cluster = Cluster(config=cluster_conf)
    cluster.reset(sg_config_path=sg_conf)

    adam_user_info = userinfo.UserInfo(name="adam", password="******", channels=["NBC", "ABC"], roles=[])
    traun_user_info = userinfo.UserInfo(name="traun", password="******", channels=[], roles=[])
    andy_user_info = userinfo.UserInfo(name="andy", password="******", channels=[], roles=[])
    sg_db = "db"
    doc_id = "adam_doc_0"

    client = MobileRestClient()

    adam_auth = client.create_user(url=sg_admin_url, db=sg_db,
                                   name=adam_user_info.name, password=adam_user_info.password, channels=adam_user_info.channels)

    traun_auth = client.create_user(url=sg_admin_url, db=sg_db,
                                    name=traun_user_info.name, password=traun_user_info.password, channels=traun_user_info.channels)

    andy_auth = client.create_user(url=sg_admin_url, db=sg_db,
                                   name=andy_user_info.name, password=andy_user_info.password, channels=andy_user_info.channels)

    ############################################################
    # changes feed wakes with Channel Access via Admin API
    ############################################################

    # Get starting sequence of docs, use the last seq to progress past any _user docs.
    adam_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=adam_auth)
    traun_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=traun_auth)
    andy_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=andy_auth)

    with concurrent.futures.ProcessPoolExecutor() as ex:

        # Start changes feed for 3 users
        adam_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=adam_changes["last_seq"], timeout=10, auth=adam_auth)
        traun_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=traun_changes["last_seq"], timeout=10, auth=traun_auth)
        andy_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=andy_changes["last_seq"], timeout=10, auth=andy_auth)

        # Wait for changes feed to notice there are no changes and enter wait. 2 seconds should be more than enough
        time.sleep(2)

        # Make sure the changes future is still running and has not exited due to any new changes, the feed should be caught up
        # and waiting
        assert not adam_changes_task.done()
        assert not traun_changes_task.done()
        assert not andy_changes_task.done()

        # Add add a doc for adam with "NBC" and "ABC" channels
        # Add one doc, this should wake up the changes feed
        adam_add_docs_task = ex.submit(client.add_docs, url=sg_url, db=sg_db,
                                       number=1, id_prefix="adam_doc",
                                       auth=adam_auth, channels=adam_user_info.channels)

        # Wait for docs adds to complete
        adam_docs = adam_add_docs_task.result()
        assert len(adam_docs) == 1

        # Assert that the changes feed woke up and that the doc change was propagated
        adam_changes = adam_changes_task.result()
        assert len(adam_changes["results"]) == 1
        assert adam_changes["results"][0]["id"] == doc_id

        # Verify that the changes feed is still listening for Traun and Andy
        assert not traun_changes_task.done()
        assert not andy_changes_task.done()

        # Update the traun and andy to have one of adam's channels
        update_traun_user_task = ex.submit(client.update_user, url=sg_admin_url, db=sg_db,
                                           name=traun_user_info.name, password=traun_user_info.password, channels=["NBC"])
        traun_auth = update_traun_user_task.result()

        update_andy_user_task = ex.submit(client.update_user, url=sg_admin_url, db=sg_db,
                                          name=andy_user_info.name, password=andy_user_info.password, channels=["ABC"])
        andy_auth = update_andy_user_task.result()

        # Make sure changes feed wakes up and contains at least one change, 2 may be possible if the _user doc is included
        # Make sure the first change is 'adam_doc'
        traun_changes = traun_changes_task.result()
        assert 1 <= len(traun_changes["results"]) <= 2
        assert traun_changes["results"][0]["id"] == "adam_doc_0" or traun_changes["results"][0]["id"] == "_user/traun"

        andy_changes = andy_changes_task.result()
        assert 1 <= len(andy_changes["results"]) <= 2
        assert andy_changes["results"][0]["id"] == "adam_doc_0" or andy_changes["results"][0]["id"] == "_user/andy"

    # Block until user docs are seen
    client.verify_doc_id_in_changes(url=sg_url, db=sg_db, expected_doc_id="_user/adam", auth=adam_auth)
    client.verify_doc_id_in_changes(url=sg_url, db=sg_db, expected_doc_id="_user/traun", auth=traun_auth)
    client.verify_doc_id_in_changes(url=sg_url, db=sg_db, expected_doc_id="_user/andy", auth=andy_auth)

    # Make sure that adams doc shows up in changes due to the fact that the changes feed may be woken up with a _user doc above
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=adam_docs, auth=adam_auth)
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=adam_docs, auth=traun_auth)
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=adam_docs, auth=andy_auth)

    ############################################################
    # changes feed wakes with Channel Removal via Sync function
    ############################################################

    # Get latest last_seq for next test section
    adam_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=adam_auth)
    traun_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=traun_auth)
    andy_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=andy_auth)

    with concurrent.futures.ProcessPoolExecutor() as ex:

        # Start changes feed for 3 users from latest last_seq
        adam_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=adam_changes["last_seq"], timeout=10, auth=adam_auth)
        traun_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=traun_changes["last_seq"], timeout=10, auth=traun_auth)
        andy_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=andy_changes["last_seq"], timeout=10, auth=andy_auth)

        # Wait for changes feed to notice there are no changes and enter wait. 2 seconds should be more than enough
        time.sleep(2)

        # Make sure the changes future is still running and has not exited due to any new changes, the feed should be caught up
        # and waiting
        assert not adam_changes_task.done()
        assert not traun_changes_task.done()
        assert not andy_changes_task.done()

        # Remove the channels property from the doc
        client.update_doc(url=sg_url, db=sg_db, doc_id=doc_id, auth=traun_auth, channels=[])

        # All three changes feeds should wake up and return one result
        adam_changes = adam_changes_task.result()
        assert len(adam_changes["results"]) == 1
        assert adam_changes["results"][0]["removed"] == ["ABC", "NBC"]

        traun_changes = traun_changes_task.result()
        assert len(traun_changes["results"]) == 1
        assert traun_changes["results"][0]["removed"] == ["NBC"]

        andy_changes = andy_changes_task.result()
        assert len(andy_changes["results"]) == 1
        assert andy_changes["results"][0]["removed"] == ["ABC"]

    # Verify that users no longer can access the doc
    for user_auth in [adam_auth, traun_auth, andy_auth]:
        with pytest.raises(requests.exceptions.HTTPError) as excinfo:
            client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id, auth=user_auth)
        assert "403 Client Error: Forbidden for url:" in excinfo.value.message

    ############################################################
    # changes feed wakes with Channel Grant via Sync function
    ############################################################

    # Get latest last_seq for next test section
    adam_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=adam_auth)
    traun_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=traun_auth)
    andy_changes = client.get_changes(url=sg_url, db=sg_db, since=0, feed="normal", auth=andy_auth)

    admin_auth = client.create_user(url=sg_admin_url, db=sg_db, name="admin", password="******", channels=["admin"])

    channel_grant_doc_id = "channel_grant_with_doc_intially"

    # Add another doc with no channels
    channel_grant_doc_body = document.create_doc(doc_id=channel_grant_doc_id, channels=["admin"])
    client.add_doc(url=sg_url, db=sg_db, doc=channel_grant_doc_body, auth=admin_auth)

    with concurrent.futures.ProcessPoolExecutor() as ex:
        # Start changes feed for 3 users from latest last_seq
        adam_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=adam_changes["last_seq"], timeout=10, auth=adam_auth)
        traun_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=traun_changes["last_seq"], timeout=10, auth=traun_auth)
        andy_changes_task = ex.submit(client.get_changes, url=sg_url, db=sg_db, since=andy_changes["last_seq"], timeout=10, auth=andy_auth)

        # Wait for changes feed to notice there are no changes and enter wait. 2 seconds should be more than enough
        time.sleep(2)

        # Make sure the changes future is still running and has not exited due to any new changes, the feed should be caught up
        # and waiting
        assert not adam_changes_task.done()
        assert not traun_changes_task.done()
        assert not andy_changes_task.done()

        # update the grant doc to have channel for all users
        update_task = ex.submit(client.update_doc, url=sg_url, db=sg_db, doc_id=channel_grant_doc_id, auth=admin_auth, channels=["admin", "ABC", "NBC"])
        updated_doc = update_task.result()
        assert updated_doc["rev"].startswith("2-")

        # Verify that access grant wakes up changes feed for adam, traun, and Andy
        adam_changes = adam_changes_task.result()
        assert len(adam_changes["results"]) == 1
        assert adam_changes["results"][0]["id"] == "channel_grant_with_doc_intially"
        assert adam_changes["results"][0]["changes"][0]["rev"].startswith("2-")

        traun_changes = traun_changes_task.result()
        assert len(traun_changes["results"]) == 1
        assert traun_changes["results"][0]["id"] == "channel_grant_with_doc_intially"
        assert traun_changes["results"][0]["changes"][0]["rev"].startswith("2-")

        andy_changes = andy_changes_task.result()
        assert len(andy_changes["results"]) == 1
        assert andy_changes["results"][0]["id"] == "channel_grant_with_doc_intially"
        assert andy_changes["results"][0]["changes"][0]["rev"].startswith("2-")
def test_backfill_channels_oneshot_changes(params_from_base_test_setup, sg_conf_name, grant_type):

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin", "pass", channels=["A"], roles=[])
    user_b_user_info = userinfo.UserInfo("USER_B", "pass", channels=["B"], roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url, db=sg_db,
                       name=admin_user_info.name, password=admin_user_info.password, channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url, db=sg_db,
                       name=user_b_user_info.name, password=user_b_user_info.password, channels=user_b_user_info.channels)

    admin_session = client.create_session(url=sg_admin_url, db=sg_db, name=admin_user_info.name, password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url, db=sg_db, name=user_b_user_info.name, password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url, db=sg_db, number=50, id_prefix=None, auth=admin_session, channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="b_doc", auth=user_b_session, channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=b_docs, auth=user_b_session, strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url, db=sg_db, since=0, auth=user_b_session, feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info("Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url, db=sg_db,
                           name=user_b_user_info.name, channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url, db=sg_db, doc=access_doc, auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])
        client.update_user(url=sg_admin_url, db=sg_db, name="USER_B", roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info("Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url, db=sg_db, doc=role_access_doc, auth=admin_session)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    user_b_changes_after_grant = client.get_changes(url=sg_url, db=sg_db,
                                                    since=user_b_changes["last_seq"], auth=user_b_session, feed="normal")

    # User B shoud have recieved 51 docs (a_docs + 1 _user/USER_B doc) if a REST grant or 50 changes if the grant
    # is via the sync function
    changes_results = user_b_changes_after_grant["results"]
    assert 50 <= len(changes_results) <= 51

    # Create a dictionary of id rev pair of all the docs that are not "_user/" docs from changes
    ids_and_revs_from_user_changes = {
        change["id"]: change["changes"][0]["rev"]
        for change in changes_results if not change["id"].startswith("_user/")
    }

    assert len(ids_and_revs_from_user_changes) == 50

    # Create a list of id rev pair of all of the channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}

    assert len(ids_and_revs_from_a_docs) == 50

    # Check that the changes and the a_docs are identical in id and rev
    assert ids_and_revs_from_user_changes == ids_and_revs_from_a_docs

    # Get changes from last_seq of the changes request after the grant. There should be no new changes
    user_b_changes = client.get_changes(url=sg_url, db=sg_db,
                                        since=user_b_changes_after_grant["last_seq"], auth=user_b_session, feed="normal")
    assert len(user_b_changes["results"]) == 0
Пример #15
0
def test_awaken_backfill_channels_longpoll_changes_with_limit(
        params_from_base_test_setup, sg_conf_name, grant_type):
    """
    Test that checks that docs are backfilled for logpoll changes with limit for a access grant (via REST or SYNC)

    CHANNEL-REST = Channel is granted to user via REST
    CHANNEL-SYNC = Channel is granted to user via sync function access()
    ROLE-REST = Role is granted to user via REST
    ROLE-SYNC = Role is granted to user via sync function role()
    CHANNEL-TO-ROLE-REST = Channel is added to existing role via REST
    CHANNEL-TO-ROLE-SYNC = Channel is added to existing role via sync access()
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin",
                                        "pass",
                                        channels=["A"],
                                        roles=[])

    if grant_type == "CHANNEL-TO-ROLE-REST" or grant_type == "CHANNEL-TO-ROLE-SYNC":
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=[])
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=["empty_role"])
    else:
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=[])

    # Create users / sessions
    client.create_user(
        url=sg_admin_url,
        db=sg_db,
        name=admin_user_info.name,
        password=admin_user_info.password,
        channels=admin_user_info.channels,
    )

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=user_b_user_info.name,
                       password=user_b_user_info.password,
                       channels=user_b_user_info.channels,
                       roles=user_b_user_info.roles)

    admin_session = client.create_session(url=sg_admin_url,
                                          db=sg_db,
                                          name=admin_user_info.name,
                                          password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url,
                                           db=sg_db,
                                           name=user_b_user_info.name,
                                           password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=50,
                             id_prefix=None,
                             auth=admin_session,
                             channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=1,
                             id_prefix="b_doc",
                             auth=user_b_session,
                             channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=b_docs,
                                  auth=user_b_session,
                                  strict=True)

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url,
                                        db=sg_db,
                                        since=0,
                                        auth=user_b_session,
                                        feed="normal")

    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as ex:

        # Start long poll changes feed.
        changes_task = ex.submit(client.get_changes,
                                 url=sg_url,
                                 db=sg_db,
                                 since=user_b_changes["last_seq"],
                                 auth=user_b_session,
                                 timeout=10,
                                 limit=20)

        # Grant access to channel "A"
        if grant_type == "CHANNEL-REST":
            log_info(
                "Granting user access to channel A via Admin REST user update")
            # Grant via update to user in Admin API
            client.update_user(url=sg_admin_url,
                               db=sg_db,
                               name=user_b_user_info.name,
                               channels=["A", "B"])

        elif grant_type == "CHANNEL-SYNC":
            log_info(
                "Granting user access to channel A sync function access()")
            # Grant via access() in sync_function,
            # then id 'channel_access' will trigger an access(doc.users, doc.channels)
            access_doc = document.create_doc("channel_access", channels=["A"])
            access_doc["users"] = ["USER_B"]
            client.add_doc(url=sg_url,
                           db=sg_db,
                           doc=access_doc,
                           auth=admin_session)

        elif grant_type == "ROLE-REST":
            log_info(
                "Granting user access to channel A via Admin REST role grant")
            # Create role with channel A
            client.create_role(url=sg_admin_url,
                               db=sg_db,
                               name="channel-A-role",
                               channels=["A"])
            client.update_user(url=sg_admin_url,
                               db=sg_db,
                               name=user_b_user_info.name,
                               roles=["channel-A-role"])

        elif grant_type == "ROLE-SYNC":
            log_info(
                "Granting user access to channel A via sync function role() grant"
            )
            # Create role with channel A
            client.create_role(url=sg_admin_url,
                               db=sg_db,
                               name="channel-A-role",
                               channels=["A"])

            # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
            role_access_doc = document.create_doc("role_access")
            role_access_doc["users"] = ["USER_B"]
            role_access_doc["roles"] = ["role:channel-A-role"]
            client.add_doc(sg_url,
                           db=sg_db,
                           doc=role_access_doc,
                           auth=admin_session)

        elif grant_type == "CHANNEL-TO-ROLE-REST":
            # Update the empty_role to have channel "A"
            client.update_role(url=sg_admin_url,
                               db=sg_db,
                               name="empty_role",
                               channels=["A"])

        elif grant_type == "CHANNEL-TO-ROLE-SYNC":
            # Grant empty_role access to channel "A" via sync function
            # Grant channel access to role via sync function
            access_doc = document.create_doc("channel_grant_to_role")
            access_doc["roles"] = ["role:empty_role"]
            access_doc["channels"] = ["A"]
            client.add_doc(url=sg_url,
                           db=sg_db,
                           doc=access_doc,
                           auth=admin_session,
                           use_post=True)

        else:
            pytest.fail("Unsupported grant_type!!!!")

        # Block on return of longpoll changes, feed should wake up and return 20 results
        changes = changes_task.result()

    assert len(changes["results"]) == 20
    num_requests = 1

    # append _user/doc to the doc scratch pad if a REST grant
    if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
        ids_and_revs_from_a_docs["_user/USER_B"] = None

    # Cross the results off from the 'a_docs' dictionary
    for doc in changes["results"]:
        del ids_and_revs_from_a_docs[doc["id"]]

    # Start looping longpoll changes with limit, cross off changes from dictionary each time one is found
    # Since 20 changes should be crossed off already, this should execute 2x.
    log_info("Starting looping longpoll changes with limit!")
    last_seq = changes["last_seq"]
    while True:

        if len(ids_and_revs_from_a_docs.keys()) == 0:
            log_info("All docs were found! Exiting polling loop")
            break

        changes = client.get_changes(url=sg_url,
                                     db=sg_db,
                                     since=last_seq,
                                     auth=user_b_session,
                                     limit=20,
                                     timeout=10)
        num_requests += 1

        # There are more than 2 requests, throw an exception.
        if num_requests == 2:
            assert len(changes["results"]) == 20
        elif num_requests == 3:
            # This will be 10 or 11 depending on if the _user/ doc is returned
            if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
                assert len(changes["results"]) == 11
            else:
                assert len(changes["results"]) == 10
        else:
            raise exceptions.ChangesError(
                "Looping longpoll should only have to perform 3 requests to get all the changes!!"
            )

        # Cross the results off from the 'a_docs' dictionary.
        # This will blow up in docs duplicate docs are sent to changes
        for doc in changes["results"]:
            del ids_and_revs_from_a_docs[doc["id"]]

        last_seq = changes["last_seq"]

    # Shanges after longpoll
    zero_results = client.get_changes(url=sg_url,
                                      db=sg_db,
                                      since=last_seq,
                                      auth=user_b_session,
                                      feed="normal")

    # Changes should be caught up and there should be no results
    assert len(zero_results["results"]) == 0
def test_mobile_opt_in(params_from_base_test_setup, sg_conf_name):
    """
    Scenario: Enable mobile opt in sync function in sync-gateway configuration file
    - Check xattrs/mobile-opt-in_cc or di json files
    - 8 cases covered
    - doc : https://docs.google.com/document/d/1XxLIBsjuj_UxTTJs4Iu7C7uZdos8ZEzeckrVc17y3sw/edit
    - #1 Create doc via sdk with mobile opt in and verify doc is imported
    - #2 Create doc via sdk with mobile opt out and verify doc is not imported
    - #3 Create doc via sg with mobile opt in and update via sdk and verify doc is imported
    - #4 Create doc via sg with mobile opt out and update via sdk and verify doc is not imported
         - Try to update same doc via sg and verify 409 conflict error is thrown
         - Create a doc with same doc id and verify doc is created successfully
    - #5 Create doc via sg with mobile opt out and update via sdk which created no revisions
         - Now do sdk create with mobile opt in should import case #5
    - #6 Create doc via sg with mobile opt out  and update via sdk with opt in
         - Verify type is overrided and doc is imported
    - #7 Create doc via sg with mobile opt in  and update via sdk with opt out
         - Verify type is overrided and doc is not imported
    - #8 Disable import in the sg config and have mobile opt in function
         Create doc via sdk with mobile property and verify sg update succeeds
    - #9 Same config as #8 and have mobile opt in function in config
         Create doc via sdk without mobile property and create new doc via sg with same doc id and
         verify it succeeds
    """

    bucket_name = 'data-bucket'
    sg_db = 'db'

    cluster_conf = params_from_base_test_setup['cluster_config']
    cluster_topology = params_from_base_test_setup['cluster_topology']
    mode = params_from_base_test_setup['mode']
    xattrs_enabled = params_from_base_test_setup['xattrs_enabled']

    # This test should only run when using xattr meta storage
    if not xattrs_enabled:
        pytest.skip('XATTR tests require --xattrs flag')

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)
    sg_admin_url = cluster_topology['sync_gateways'][0]['admin']
    sg_url = cluster_topology['sync_gateways'][0]['public']
    cbs_url = cluster_topology['couchbase_servers'][0]

    log_info('sg_conf: {}'.format(sg_conf))
    log_info('sg_admin_url: {}'.format(sg_admin_url))
    log_info('sg_url: {}'.format(sg_url))
    log_info('cbs_url: {}'.format(cbs_url))

    cluster = Cluster(config=cluster_conf)
    cluster.reset(sg_config_path=sg_conf)

    # Create clients
    sg_client = MobileRestClient()
    cbs_ip = host_for_url(cbs_url)
    sdk_client = Bucket('couchbase://{}/{}'.format(cbs_ip, bucket_name), password='******', timeout=SDK_TIMEOUT)

    # Create user / session
    auto_user_info = UserInfo(name='autotest', password='******', channels=['mobileOptIn'], roles=[])
    sg_client.create_user(
        url=sg_admin_url,
        db=sg_db,
        name=auto_user_info.name,
        password=auto_user_info.password,
        channels=auto_user_info.channels
    )

    test_auth_session = sg_client.create_session(
        url=sg_admin_url,
        db=sg_db,
        name=auto_user_info.name,
        password=auto_user_info.password
    )

    def update_mobile_prop():
        return {
            'updates': 0,
            'type': 'mobile',
        }

    def update_non_mobile_prop():
        return {
            'updates': 0,
            'test': 'true',
            'type': 'mobile opt out',
        }

    # Create first doc via SDK with type mobile. Case #1
    doc_id1 = 'mobile_opt_in_sdk_doc'
    doc = document.create_doc(doc_id=doc_id1, channels=['mobileOptIn'], prop_generator=update_mobile_prop)
    sdk_client.upsert(doc_id1, doc)
    sg_get_doc1 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id1, auth=test_auth_session)
    assert sg_get_doc1['_rev'].startswith('1-') and sg_get_doc1['_id'] == doc_id1
    # Additional coverage for case #1
    sg_client.update_doc(url=sg_url, db=sg_db, doc_id=doc_id1, number_updates=1, auth=test_auth_session)
    sg_get_doc1 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id1, auth=test_auth_session)
    assert sg_get_doc1['_rev'].startswith('2-') and sg_get_doc1['_id'] == doc_id1

    # Create second doc via SDK with type non mobile. Case #2
    doc_id2 = 'mobile_opt_out_sdk_doc'
    doc = document.create_doc(doc_id=doc_id2, channels=['mobileOptIn'], prop_generator=update_non_mobile_prop)
    sdk_client.upsert(doc_id2, doc)
    with pytest.raises(HTTPError) as he:
        sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id2, auth=test_auth_session)
    log_info(he.value)
    assert he.value.message.startswith('404 Client Error: Not Found for url:')

    # Create third sg doc with mobile opt in  and update via sdk. Case #3
    doc_id3 = 'mobile_opt_in_sg_doc'
    doc_body = document.create_doc(doc_id=doc_id3, channels=['mobileOptIn'], prop_generator=update_mobile_prop)
    doc = sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    sg_get_doc3 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id3, auth=test_auth_session)
    sg_get_doc3["updated_sdk_via_sg"] = "1"
    sdk_client.upsert(doc_id3, sg_get_doc3)
    sg_get_doc3 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id3, auth=test_auth_session)
    assert sg_get_doc3['_rev'].startswith('2-') and sg_get_doc3['_id'] == doc_id3
    log_info("sg get doc3 is {}".format(sg_get_doc3))

    # Create fourth sg doc with mobile opt out and update via sdk. Case #4 and case #8
    doc_id4 = 'mobile_opt_out_sg_doc'
    doc_body = document.create_doc(doc_id=doc_id4, channels=['mobileOptIn'], prop_generator=update_non_mobile_prop)
    doc = sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    # update vis SDK
    sg_get_doc4 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id4, auth=test_auth_session)
    rev = sg_get_doc4['_rev']
    sg_get_doc4["updated_sdk_via_sg"] = "1"
    sdk_client.upsert(doc_id4, sg_get_doc4)
    with pytest.raises(HTTPError) as he:
        sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id4, auth=test_auth_session)
    log_info(he.value)
    assert he.value.message.startswith('404 Client Error: Not Found for url:')
    # update via SG
    with pytest.raises(HTTPError) as he:
        sg_client.put_doc(url=sg_url, db=sg_db, doc_id=doc_id4, doc_body={'sg_rewrite': 'True'}, rev=rev, auth=test_auth_session)
    log_info(he.value)
    assert he.value.message.startswith('409 Client Error: Conflict for url:')
    # Create same doc again to verify there is not existing key error covers case #8
    doc_body = document.create_doc(doc_id=doc_id4, channels=['mobileOptIn'], prop_generator=update_non_mobile_prop)
    sg_get_doc4_1 = sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    log_info("4th doc after recreate vis sg is {}".format(sg_get_doc4_1))
    assert sg_get_doc4_1['rev'].startswith('1-') and sg_get_doc4_1['id'] == doc_id4

    # Create Fifth sg doc with mobile opt in and delete doc which created no revisions i.e tombstone doc
    # Now do sdk create with mobile opt in should import case #5
    doc_id5 = 'mobile_sdk_recreate_no_activerev'
    doc_body = document.create_doc(doc_id=doc_id5, channels=['mobileOptIn'], prop_generator=update_mobile_prop)
    doc = sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    rev = doc['rev']
    sg_client.delete_doc(url=sg_url, db=sg_db, doc_id=doc_id5, rev=rev, auth=test_auth_session)
    # At this point no active revisions for this doc, so now update via sdk with mobile opt in should be successful
    # in getting doc
    doc = document.create_doc(doc_id=doc_id5, channels=['mobileOptIn'], prop_generator=update_mobile_prop)
    sdk_client.upsert(doc_id5, doc)
    sg_get_doc5 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id5, auth=test_auth_session)
    log_info("sg get doc 5 is {}".format(sg_get_doc5))
    assert sg_get_doc5['_rev'].startswith('1-') and sg_get_doc5['_id'] == doc_id5

    # Create sixth sg doc with mobile opt out  and update via sdk with opt in
    doc_id6 = 'mobileoptout_sg_doc_sdkupdate_optin'
    doc_body = document.create_doc(doc_id=doc_id6, channels=['mobileOptIn'], prop_generator=update_non_mobile_prop)
    doc = sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    sg_get_doc6 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id6, auth=test_auth_session)
    log_info("Sg sixth doc is {}".format(sg_get_doc6))
    sg_get_doc6["type"] = "mobile"
    sdk_client.upsert(doc_id6, sg_get_doc6)
    sg_get_doc6 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id6, auth=test_auth_session)
    assert sg_get_doc6['_rev'].startswith('2-') and sg_get_doc6['_id'] == doc_id6

    # Create seventh sg doc with mobile opt in  and update via sdk with opt out
    doc_id7 = 'mobileoptin_sg_doc_sdkupdate_optout'
    doc_body = document.create_doc(doc_id=doc_id7, channels=['mobileOptIn'], prop_generator=update_mobile_prop)
    doc = sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    sg_get_doc7 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id7, auth=test_auth_session)
    log_info("Sg sixth doc is {}".format(sg_get_doc7))
    sg_get_doc7["type"] = "mobile opt out"
    sdk_client.upsert(doc_id7, sg_get_doc7)
    with pytest.raises(HTTPError) as he:
        sg_get_doc7 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id7, auth=test_auth_session)
    log_info(he.value)
    assert he.value.message.startswith('404 Client Error: Not Found for url:')
    # TODO : verify _changes that it shows tombstone revisions -> it will happen on 2.0

    # Create eighth sdk doc with import disabled and add mobile property and update via sg. Case #7
    sg_conf_name = "xattrs/mobile_opt_in_no_import"
    sg_no_import_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)
    sg_util = SyncGateway()
    sg_util.start_sync_gateways(cluster_config=cluster_conf, url=sg_url, config=sg_no_import_conf)

    doc_id8 = 'mobile_opt_in_sg_rewrite_with_importdisabled'
    doc_body = document.create_doc(doc_id=doc_id8, channels=['mobileOptIn'], prop_generator=update_mobile_prop)
    sdk_client.upsert(doc_id8, doc_body)
    with pytest.raises(HTTPError) as he:
        sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    log_info(he.value)
    assert he.value.message.startswith('409 Client Error: Conflict for url:')
    sg_client.update_doc(url=sg_url, db=sg_db, doc_id=doc_id8, number_updates=1, auth=test_auth_session)
    sg_get_doc8 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id8, auth=test_auth_session)
    assert sg_get_doc8['_rev'].startswith('2-') and sg_get_doc8['_id'] == doc_id8

    # Create ninth sdk doc with import disabled and add mobile property and update via sg. Case #8
    doc_id9 = 'mobile_opt_out_sg_rewrite_with_importdisabled'
    doc_body = document.create_doc(doc_id=doc_id9, channels=['mobileOptIn'], prop_generator=update_non_mobile_prop)
    sdk_client.upsert(doc_id9, doc_body)
    sg_client.add_doc(url=sg_url, db=sg_db, doc=doc_body, auth=test_auth_session)
    # sg_client.update_doc(url=sg_url, db=sg_db, doc_id=doc_id8, number_updates=1, auth=test_auth_session)
    sg_get_doc9 = sg_client.get_doc(url=sg_url, db=sg_db, doc_id=doc_id9, auth=test_auth_session)
    assert sg_get_doc9['_rev'].startswith('1-') and sg_get_doc9['_id'] == doc_id9
Пример #17
0
def test_backfill_channels_oneshot_limit_changes(params_from_base_test_setup,
                                                 sg_conf_name, grant_type):
    """
    Test that checks that docs are backfilled for one shot changes with limit for a access grant (via REST or SYNC)

    CHANNEL-REST = Channel is granted to user via REST
    CHANNEL-SYNC = Channel is granted to user via sync function access()
    ROLE-REST = Role is granted to user via REST
    ROLE-SYNC = Role is granted to user via sync function role()
    CHANNEL-TO-ROLE-REST = Channel is added to existing role via REST
    CHANNEL-TO-ROLE-SYNC = Channel is added to existing role via sync access()
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin",
                                        "pass",
                                        channels=["A"],
                                        roles=[])

    if grant_type == "CHANNEL-TO-ROLE-REST" or grant_type == "CHANNEL-TO-ROLE-SYNC":
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=[])
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=["empty_role"])
    else:
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=admin_user_info.name,
                       password=admin_user_info.password,
                       channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=user_b_user_info.name,
                       password=user_b_user_info.password,
                       channels=user_b_user_info.channels,
                       roles=user_b_user_info.roles)

    admin_session = client.create_session(url=sg_admin_url,
                                          db=sg_db,
                                          name=admin_user_info.name,
                                          password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url,
                                           db=sg_db,
                                           name=user_b_user_info.name,
                                           password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=50,
                             id_prefix=None,
                             auth=admin_session,
                             channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=1,
                             id_prefix="b_doc",
                             auth=user_b_session,
                             channels=["B"])
    assert len(b_docs) == 1

    # Loop until admin user sees docs in changes
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=a_docs,
                                  auth=admin_session)

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=b_docs,
                                  auth=user_b_session,
                                  strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url,
                                        db=sg_db,
                                        since=0,
                                        auth=user_b_session,
                                        feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info(
            "Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name=user_b_user_info.name,
                           channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name="USER_B",
                           roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info(
            "Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url,
                       db=sg_db,
                       doc=role_access_doc,
                       auth=admin_session)

    elif grant_type == "CHANNEL-TO-ROLE-REST":
        # Update the empty_role to have channel "A"
        client.update_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=["A"])

    elif grant_type == "CHANNEL-TO-ROLE-SYNC":
        # Grant empty_role access to channel "A" via sync function
        # Grant channel access to role via sync function
        access_doc = document.create_doc("channel_grant_to_role")
        access_doc["roles"] = ["role:empty_role"]
        access_doc["channels"] = ["A"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session,
                       use_post=True)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    log_info("Doing 3, 1 shot changes with limit and last seq!")
    # Issue 3 oneshot changes with a limit of 20

    # Issue one shot changes to make sure access grant is successful, the change may not propagate immediately so retry.
    num_retries = 3
    count = 0
    while True:
        if count == num_retries:
            raise exceptions.ChangesError(
                "Didn't get all expected changes before timing out!")

        user_b_changes_after_grant_one = client.get_changes(
            url=sg_url,
            db=sg_db,
            since=user_b_changes["last_seq"],
            auth=user_b_session,
            feed="normal",
            limit=20)

        if len(user_b_changes_after_grant_one["results"]) > 0:
            # Found changes, break out an validate changes!
            break

        time.sleep(1)
        count += 1

    #################
    # Changes Req #1
    #################

    # Expect a user doc in the changes
    if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
        ids_and_revs_from_a_docs["_user/USER_B"] = None

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_one["results"]) == 20

    for doc in user_b_changes_after_grant_one["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #2
    #################
    user_b_changes_after_grant_two = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant_one["last_seq"],
        auth=user_b_session,
        feed="normal",
        limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_two["results"]) == 20

    for doc in user_b_changes_after_grant_two["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #3
    #################
    user_b_changes_after_grant_three = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant_two["last_seq"],
        auth=user_b_session,
        feed="normal",
        limit=20)

    # User B should have recieved 10 docs due to limit or 11 docs with with a terminating _user doc
    # The terminating user doc only happens with a grant via REST
    if grant_type == "CHANNEL-REST" or grant_type == "ROLE-REST":
        assert len(user_b_changes_after_grant_three["results"]) == 11
    else:
        assert len(user_b_changes_after_grant_three["results"]) == 10

    for doc in user_b_changes_after_grant_three["results"]:
        # cross off non user doc keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    # Make sure all the docs have been crossed out
    assert len(ids_and_revs_from_a_docs) == 0

    #################
    # Changes Req #4
    #################
    user_b_changes_after_grant_four = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant_three["last_seq"],
        auth=user_b_session,
        feed="normal",
        limit=20)

    # Changes should be caught up and there should be no results
    assert len(user_b_changes_after_grant_four["results"]) == 0
Пример #18
0
def test_stale_revision_should_not_be_in_the_index(setup_client_syncgateway_test):
    """original ticket: https://github.com/couchbase/couchbase-lite-android/issues/855

    scenario:
    1. Running sync_gateway
    2. Create database and starts both push and pull replicators through client REST API
    3. Create two or more views through client REST API
    4. Add doc, and verify doc is index with current revision through client REST API
    5. Make sure document is pushed to sync gateway through sync gateway REST API
    6. Update doc with sync gateway (not client side) through sync gateway REST API
    7. Make sure updated document is pull replicated to client  through client REST API
    8. Make sure updated document is indexed through client REST API
    9. Make sure stale revision is deleted from index.  through client REST API
    10. Pass criteria
    """

    cluster_config = setup_client_syncgateway_test["cluster_config"]
    sg_mode = setup_client_syncgateway_test["sg_mode"]
    ls_url = setup_client_syncgateway_test["ls_url"]
    sg_url = setup_client_syncgateway_test["sg_url"]
    sg_admin_url = setup_client_syncgateway_test["sg_admin_url"]

    num_docs = 10
    num_revs = 100

    d_doc_name = "dd"
    sg_db = "db"
    sg_user_name = "sg_user"

    sg_config = sync_gateway_config_path_for_mode("listener_tests/listener_tests", sg_mode)
    c = cluster.Cluster(config=cluster_config)
    c.reset(sg_config_path=sg_config)

    log_info("Running 'test_stale_revision_should_not_be_in_the_index'")
    log_info("ls_url: {}".format(ls_url))
    log_info("sg_admin_url: {}".format(sg_admin_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("num_docs: {}".format(num_docs))
    log_info("num_revs: {}".format(num_revs))

    client = MobileRestClient()

    sg_user_channels = ["NBC"]
    client.create_user(url=sg_admin_url, db=sg_db, name=sg_user_name, password="******", channels=sg_user_channels)
    sg_session = client.create_session(url=sg_admin_url, db=sg_db, name=sg_user_name)

    view = """{
    "language" : "javascript",
    "views" : {
        "content_view" : {
            "map" : "function(doc, meta) { if (doc.content) { emit(doc._id, doc._rev); } }"
        },
        "update_view" : {
            "map" : "function(doc, meta) { emit(doc.updates, null); }"
        }
    }
}"""

    ls_db = client.create_database(url=ls_url, name="ls_db")

    # Setup continuous push / pull replication from ls_db1 to sg_db
    client.start_replication(
        url=ls_url,
        continuous=True,
        from_db=ls_db,
        to_url=sg_admin_url, to_db=sg_db
    )

    client.start_replication(
        url=ls_url,
        continuous=True,
        from_url=sg_admin_url, from_db=sg_db,
        to_db=ls_db
    )

    design_doc_id = client.add_design_doc(url=ls_url, db=ls_db, name=d_doc_name, doc=view)
    client.get_doc(url=ls_url, db=ls_db, doc_id=design_doc_id)

    doc_body = document.create_doc(doc_id="doc_1", content={"hi": "I should be in the view"}, channels=sg_user_channels)

    log_info(doc_body)

    doc_body_2 = document.create_doc(doc_id="doc_2", channels=sg_user_channels)

    doc = client.add_doc(url=ls_url, db=ls_db, doc=doc_body)
    doc_2 = client.add_doc(url=ls_url, db=ls_db, doc=doc_body_2)

    content_view_rows = client.get_view(url=ls_url, db=ls_db, design_doc_name=d_doc_name, view_name="content_view")
    client.verify_view_row_num(view_response=content_view_rows, expected_num_rows=1)

    update_view_rows = client.get_view(url=ls_url, db=ls_db, design_doc_name=d_doc_name, view_name="update_view")
    client.verify_view_row_num(view_response=update_view_rows, expected_num_rows=2)

    expected_docs_list = [doc, doc_2]
    client.verify_docs_present(url=sg_url, db=sg_db, expected_docs=expected_docs_list, auth=sg_session)

    updated_doc = client.update_doc(url=sg_url, db=sg_db, doc_id=doc["id"], number_updates=10, auth=sg_session)

    client.verify_docs_present(url=ls_url, db=ls_db, expected_docs=updated_doc)

    content_view_rows_2 = client.get_view(url=ls_url, db=ls_db, design_doc_name=d_doc_name, view_name="content_view")
    client.verify_view_row_num(view_response=content_view_rows_2, expected_num_rows=1)

    client.verify_view_contains_keys(view_response=content_view_rows_2, keys=doc["id"])
    client.verify_view_contains_values(view_response=content_view_rows_2, values=updated_doc["rev"])
def test_inline_large_attachments(setup_client_syncgateway_test):
    """
    1.  Start LiteServ and Sync Gateway
    2.  Create 2 databases on LiteServ (ls_db1, ls_db2)
    3.  Start continuous push replication from ls_db1 to sg_db
    4.  Start continuous pull replication from sg_db to ls_db2
    5.  PUT 5 large inline attachments to ls_db1
    6.  DELETE the docs on ls_db1
    7.  PUT same 5 large inline attachments to ls_db1
    8.  Verify docs replicate to ls_db2
    9.  Purge ls_db1
    10. Verify docs removed
    """

    log_info("Running 'test_inline_large_attachments' ...")

    sg_url = setup_client_syncgateway_test["sg_url"]
    sg_url_admin = setup_client_syncgateway_test["sg_admin_url"]

    ls_url = setup_client_syncgateway_test["ls_url"]

    log_info("ls_url: {}".format(ls_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    ls_db1 = "ls_db1"
    ls_db2 = "ls_db2"
    sg_db = "db"

    client = MobileRestClient()
    client.create_database(ls_url, ls_db1)
    client.create_database(ls_url, ls_db2)

    # Start continuous push replication from ls_db1 -> sg_db
    client.start_replication(url=ls_url, continuous=True, from_db=ls_db1, to_url=sg_url, to_db=sg_db)

    # Start continuous push replication from sg_db -> ls_db2
    client.start_replication(url=ls_url, continuous=True, from_url=sg_url, from_db=sg_db, to_db=ls_db2)

    # doc with 2.36 PNG attachment
    attachment_docs = []
    for i in range(5):
        doc = document.create_doc(
            doc_id="large_attach_{}".format(i), attachment_name="golden_gate_large.jpg", channels=["ABC"]
        )
        attachment_docs.append(doc)

    # add large attachments to ls_db1
    docs = []
    for doc in attachment_docs:
        docs.append(client.add_doc(ls_url, ls_db1, doc, use_post=False))

    # Delete docs
    client.delete_docs(ls_url, ls_db1, docs)
    client.verify_docs_deleted(ls_url, ls_db1, docs)

    # Recreated docs
    recreated_docs = []
    for doc in attachment_docs:
        recreated_docs.append(client.add_doc(ls_url, ls_db1, doc, use_post=False))

    client.verify_docs_present(ls_url, ls_db1, recreated_docs)
    client.verify_docs_present(sg_url, sg_db, recreated_docs)
    client.verify_docs_present(ls_url, ls_db2, recreated_docs)

    purged_docs = client.purge_docs(ls_url, ls_db1, recreated_docs)
    log_info(purged_docs)

    # All purged docs should have replicated and should be gone now.
    # This is currently failing due to some docs not replicating to ls_db2
    client.verify_docs_deleted(ls_url, ls_db1, recreated_docs)
    client.verify_docs_deleted(sg_url, sg_db, recreated_docs)
    client.verify_docs_deleted(ls_url, ls_db2, recreated_docs)
Пример #20
0
def test_string_expiry_as_iso_8601_date(params_from_base_test_setup, sg_conf_name):
    """
    1. Calculate (server time + 3 seconds) as ISO-8601 date (e.g. 2016-01-01T00:00:00.000+00:00)
    2. PUT /db/doc1 via SG with property "_exp":"[date]"
       PUT /db/doc2 via SG with property "_exp":"2026-01-01T00:00:00.000+00:00"
    3. Wait five seconds
    4. Get /db/doc1.  Assert response is 404
       Get /db/doc2.  Assert response is 20
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(
        cluster_config=cluster_config,
        sync_gateway_config=sg_conf
    )

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_string_expiry_as_ISO_8601_Date'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin, db=sg_db, name=sg_user_name, password=sg_user_password, channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin, db=sg_db, name=sg_user_name)

    time_util = Time()
    iso_datetime = time_util.get_iso_datetime(delta=3)

    doc_exp_3_body = document.create_doc(doc_id="exp_3", expiry=iso_datetime, channels=sg_user_channels)
    doc_exp_years_body = document.create_doc(doc_id="exp_years", expiry="2026-01-01T00:00:00.000+00:00", channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_3_body, auth=sg_user_session)
    doc_exp_years = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_years_body, auth=sg_user_session)

    # Sleep should allow doc_exp_3 to expire
    time.sleep(10)

    # doc_exp_3 should be expired
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], auth=sg_user_session)
    assert he.value[0].startswith("404 Client Error: Not Found for url:")

    # doc_exp_years should be available still
    doc_exp_years_result = client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_years["id"], auth=sg_user_session)
    assert doc_exp_years_result["_id"] == "exp_years"
Пример #21
0
def test_string_expiry_as_unix_date(params_from_base_test_setup, sg_conf_name):
    """
    1. Calculate (server time + 3 seconds) as unix time (i.e. Epoch time, e.g. 1466465122)
    2. PUT /db/doc1 via SG with property "_exp":"[unix time]"
       PUT /db/doc2 via SG with property "_exp":"1767225600"  (Jan 1 2026) Note: the maximum epoch time supported by CBS is maxUint32, or Sun 07 Feb 2106, in case you want to move it out further than 2026.
    3. Wait five seconds
    4. Get /db/doc1.  Assert response is 404
       Get /db/doc2.  Assert response is 200
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(
        cluster_config=cluster_config,
        sync_gateway_config=sg_conf
    )

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_string_expiry_as_unix_date'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin, db=sg_db, name=sg_user_name, password=sg_user_password, channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin, db=sg_db, name=sg_user_name)

    time_util = Time()
    unix_time_3s_ahead = time_util.get_unix_timestamp(delta=3)

    # Convert unix timestamp to string
    unix_time_3s_ahead_string = str(unix_time_3s_ahead)

    # Using string representation for unix time
    doc_exp_3_body = document.create_doc(doc_id="exp_3", expiry=unix_time_3s_ahead_string, channels=sg_user_channels)
    doc_exp_years_body = document.create_doc(doc_id="exp_years", expiry="1767225600", channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_3_body, auth=sg_user_session)
    doc_exp_years = client.add_doc(url=sg_url, db=sg_db, doc=doc_exp_years_body, auth=sg_user_session)

    # Sleep should allow doc_exp_3 to expire
    time.sleep(10)

    # doc_exp_3 should be expired
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_3["id"], auth=sg_user_session)
    assert he.value[0].startswith("404 Client Error: Not Found for url:")

    # doc_exp_years should be available still
    doc_exp_years_result = client.get_doc(url=sg_url, db=sg_db, doc_id=doc_exp_years["id"], auth=sg_user_session)
    assert doc_exp_years_result["_id"] == "exp_years"
Пример #22
0
def test_backfill_channels_oneshot_changes(params_from_base_test_setup,
                                           sg_conf_name, grant_type):
    """
    Test that checks that docs are backfilled for one shot changes for a access grant (via REST or SYNC)

    CHANNEL-REST = Channel is granted to user via REST
    CHANNEL-SYNC = Channel is granted to user via sync function access()
    ROLE-REST = Role is granted to user via REST
    ROLE-SYNC = Role is granted to user via sync function role()
    CHANNEL-TO-ROLE-REST = Channel is added to existing role via REST
    CHANNEL-TO-ROLE-SYNC = Channel is added to existing role via sync access()
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin",
                                        "pass",
                                        channels=["A"],
                                        roles=[])

    if grant_type == "CHANNEL-TO-ROLE-REST" or grant_type == "CHANNEL-TO-ROLE-SYNC":
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=[])
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=["empty_role"])
    else:
        user_b_user_info = userinfo.UserInfo("USER_B",
                                             "pass",
                                             channels=["B"],
                                             roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=admin_user_info.name,
                       password=admin_user_info.password,
                       channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=user_b_user_info.name,
                       password=user_b_user_info.password,
                       channels=user_b_user_info.channels,
                       roles=user_b_user_info.roles)

    admin_session = client.create_session(url=sg_admin_url,
                                          db=sg_db,
                                          name=admin_user_info.name,
                                          password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url,
                                           db=sg_db,
                                           name=user_b_user_info.name,
                                           password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=50,
                             id_prefix=None,
                             auth=admin_session,
                             channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=1,
                             id_prefix="b_doc",
                             auth=user_b_session,
                             channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until admin user sees docs in changes
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=a_docs,
                                  auth=admin_session)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=b_docs,
                                  auth=user_b_session,
                                  strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url,
                                        db=sg_db,
                                        since=0,
                                        auth=user_b_session,
                                        feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info(
            "Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name=user_b_user_info.name,
                           channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])
        client.update_user(url=sg_admin_url,
                           db=sg_db,
                           name="USER_B",
                           channels=["B"],
                           roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info(
            "Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url,
                           db=sg_db,
                           name="channel-A-role",
                           channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url,
                       db=sg_db,
                       doc=role_access_doc,
                       auth=admin_session)

    elif grant_type == "CHANNEL-TO-ROLE-REST":
        # Update the empty_role to have channel "A"
        client.update_role(url=sg_admin_url,
                           db=sg_db,
                           name="empty_role",
                           channels=["A"])

    elif grant_type == "CHANNEL-TO-ROLE-SYNC":
        # Grant empty_role access to channel "A" via sync function
        # Grant channel access to role via sync function
        access_doc = document.create_doc("channel_grant_to_role")
        access_doc["roles"] = ["role:empty_role"]
        access_doc["channels"] = ["A"]
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=admin_session,
                       use_post=True)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    # Issue one shot changes to make sure access grant is successful, the change may not propagate immediately so retry.
    num_retries = 3
    count = 0
    while True:
        if count == num_retries:
            raise exceptions.ChangesError(
                "Didn't get all expected changes before timing out!")

        user_b_changes_after_grant = client.get_changes(
            url=sg_url,
            db=sg_db,
            since=user_b_changes["last_seq"],
            auth=user_b_session,
            feed="normal")

        if len(user_b_changes_after_grant["results"]) > 0:
            # Found changes, break out an validate changes!
            break

        time.sleep(1)
        count += 1

    # User B shoud have recieved 51 docs (a_docs + 1 _user/USER_B doc) if a REST grant or 50 changes if the grant
    # is via the sync function
    changes_results = user_b_changes_after_grant["results"]
    assert 50 <= len(changes_results) <= 51

    # Create a dictionary of id rev pair of all the docs that are not "_user/" docs from changes
    ids_and_revs_from_user_changes = {
        change["id"]: change["changes"][0]["rev"]
        for change in changes_results if not change["id"].startswith("_user/")
    }

    assert len(ids_and_revs_from_user_changes) == 50

    # Create a list of id rev pair of all of the channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}

    assert len(ids_and_revs_from_a_docs) == 50

    # Check that the changes and the a_docs are identical in id and rev
    assert ids_and_revs_from_user_changes == ids_and_revs_from_a_docs

    # Get changes from last_seq of the changes request after the grant. There should be no new changes
    user_b_changes = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=user_b_changes_after_grant["last_seq"],
        auth=user_b_session,
        feed="normal")
    assert len(user_b_changes["results"]) == 0
Пример #23
0
def test_attachment_revpos_when_ancestor_unavailable_active_revision_doesnt_share_ancestor(
        params_from_base_test_setup, sg_conf_name):
    """
    Creates a document with an attachment, then updates that document so that
    the body of the revision that originally pushed the document is no
    longer available.  Add a new revision that's not a child of the
    active revision, and validate that it's uploaded successfully.
    Example:
       1. Document is created with no attachment at rev-1
       2. Server adds revision with attachment at rev-2 {"hello.txt", revpos=2}
       2. Document is updated multiple times on the server, goes to rev-4
       3. Client attempts to add a new (conflicting) revision 3a, with ancestors rev-2a (with it's own attachment), rev-1.
       4. When client attempts to push rev-3a with attachment stub {"hello.txt", revpos=2}.  Should throw an error, since the revpos
       of the attachment is later than the common ancestor (rev-1)
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    no_conflicts_enabled = params_from_base_test_setup["no_conflicts_enabled"]

    if no_conflicts_enabled:
        pytest.skip('--no-conflicts is not enabled, so skipping the test')

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    cluster_helper.reset_cluster(cluster_config, sg_conf)

    topology = cluster_helper.get_cluster_topology(cluster_config)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]
    sg_db = "db"
    bucket = "data-bucket"

    log_info(
        "Running 'test_attachment_revpos_when_ancestor_unavailable_active_revision_doesnt_share_ancestor'"
    )
    log_info("Using cbs_url: {}".format(cbs_url))
    log_info("Using sg_url: {}".format(sg_url))
    log_info("Using sg_url_admin: {}".format(sg_url_admin))
    log_info("Using sg_db: {}".format(sg_db))
    log_info("Using bucket: {}".format(bucket))

    sg_user_name = "sg_user"
    sg_user_password = "******"

    sg_user_channels = ["NBC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin,
                       db=sg_db,
                       name=sg_user_name,
                       password=sg_user_password,
                       channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin,
                                            db=sg_db,
                                            name=sg_user_name)

    doc = document.create_doc(doc_id="doc_1",
                              content={"sample_key": "sample_val"},
                              channels=sg_user_channels)
    doc_gen_1 = client.add_doc(url=sg_url,
                               db=sg_db,
                               doc=doc,
                               auth=sg_user_session)
    client.update_doc(url=sg_url,
                      db=sg_db,
                      doc_id=doc_gen_1["id"],
                      attachment_name="sample_text.txt",
                      auth=sg_user_session)
    client.update_doc(url=sg_url,
                      db=sg_db,
                      doc_id=doc_gen_1["id"],
                      auth=sg_user_session)
    client.update_doc(url=sg_url,
                      db=sg_db,
                      doc_id=doc_gen_1["id"],
                      auth=sg_user_session)

    parent_rev_list = ["2-foo2", doc_gen_1["rev"]]

    # Sync Gateway should error since it has no references attachment in its ancestors
    with pytest.raises(HTTPError) as he:
        client.add_conflict(url=sg_url,
                            db=sg_db,
                            doc_id=doc_gen_1["id"],
                            parent_revisions=parent_rev_list,
                            new_revision="3-foo3",
                            auth=sg_user_session)
    assert he.value[0].startswith("400 Client Error: Bad Request for url: ")
Пример #24
0
def test_backfill_channel_grant_to_role_longpoll(params_from_base_test_setup,
                                                 sg_conf_name, grant_type,
                                                 channels_to_grant):
    """
    Test that check that docs are backfilled for a channel grant (via REST or SYNC) to existing role

    1. Create a 'grantee' user with an empty role
    2. 'pusher' user adds docs with channel(s) that will later be granted to 'grantee'
    3. Verify that the 'pusher' sees the docs on its changes feed
    4. Grant the 'grantee's role access to the pushers channels (either via REST or via sync function)
    5. Verify that 'grantee' gets all of the docs after the grant
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"
    num_docs_per_channel = 100
    empty_role_name = "empty_role"

    log_info("grant_type: {}".format(grant_type))
    log_info("channels to grant access to: {}".format(channels_to_grant))

    is_multi_channel_grant = False
    if len(channels_to_grant) == 3:
        is_multi_channel_grant = True
    log_info("is_multi_channel_grant: {}".format(is_multi_channel_grant))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()
    client.create_role(url=sg_admin_url,
                       db=sg_db,
                       name=empty_role_name,
                       channels=[])

    pusher_info = userinfo.UserInfo("pusher",
                                    "pass",
                                    channels=channels_to_grant,
                                    roles=[])
    grantee_info = userinfo.UserInfo("grantee",
                                     "pass",
                                     channels=[],
                                     roles=[empty_role_name])

    # Create users
    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=pusher_info.name,
                       password=pusher_info.password,
                       channels=pusher_info.channels,
                       roles=pusher_info.roles)
    pusher_session = client.create_session(url=sg_admin_url,
                                           db=sg_db,
                                           name=pusher_info.name,
                                           password=pusher_info.password)

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=grantee_info.name,
                       password=grantee_info.password,
                       channels=grantee_info.channels,
                       roles=grantee_info.roles)
    grantee_session = client.create_session(url=sg_admin_url,
                                            db=sg_db,
                                            name=grantee_info.name,
                                            password=grantee_info.password)

    pusher_changes = client.get_changes(url=sg_url,
                                        db=sg_db,
                                        since=0,
                                        auth=pusher_session)

    # Make sure _user docs shows up in the changes feed
    assert len(pusher_changes["results"]
               ) == 1 and pusher_changes["results"][0]["id"] == "_user/pusher"

    # Add docs with the appropriate channels
    a_docs = client.add_docs(url=sg_url,
                             db=sg_db,
                             number=num_docs_per_channel,
                             id_prefix=None,
                             auth=pusher_session,
                             channels=["A"])
    assert len(a_docs) == 100
    expected_docs = a_docs

    if is_multi_channel_grant:
        b_docs = client.add_docs(url=sg_url,
                                 db=sg_db,
                                 number=num_docs_per_channel,
                                 id_prefix=None,
                                 auth=pusher_session,
                                 channels=["B"])
        assert len(b_docs) == 100
        expected_docs += b_docs

        c_docs = client.add_docs(url=sg_url,
                                 db=sg_db,
                                 number=num_docs_per_channel,
                                 id_prefix=None,
                                 auth=pusher_session,
                                 channels=["C"])
        assert len(c_docs) == 100
        expected_docs += c_docs

    # Wait for all docs to show up on the changes feed before access grant
    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=expected_docs,
                                  auth=pusher_session)

    # Get changes for granted before grant and assert the only changes is the user doc
    grantee_changes_before_grant = client.get_changes(url=sg_url,
                                                      db=sg_db,
                                                      since=0,
                                                      auth=grantee_session)
    assert len(grantee_changes_before_grant["results"]) == 1
    assert grantee_changes_before_grant["results"][0]["id"] == "_user/grantee"

    if grant_type == "CHANNEL-REST":
        # Grant channel access to role via REST
        client.update_role(url=sg_admin_url,
                           db=sg_db,
                           name=empty_role_name,
                           channels=channels_to_grant)
    elif grant_type == "CHANNEL-SYNC":
        # Grant channel access to role via sync function
        access_doc = document.create_doc(doc_id="channel_grant_to_role")
        access_doc["roles"] = ["role:{}".format(empty_role_name)]
        access_doc["channels"] = channels_to_grant
        client.add_doc(url=sg_url,
                       db=sg_db,
                       doc=access_doc,
                       auth=pusher_session,
                       use_post=True)

    # Issue changes request after grant
    grantee_changes_post_grant = client.get_changes(
        url=sg_url,
        db=sg_db,
        since=grantee_changes_before_grant["last_seq"],
        auth=grantee_session,
        feed="longpoll")

    # grantee should have all the docs now
    if is_multi_channel_grant:
        # Check that the grantee gets all of the docs for channels ["A", "B", "C"]
        assert len(
            grantee_changes_post_grant["results"]) == num_docs_per_channel * 3
    else:
        # Check that the grantee gets all of the docs for channels ["A"]
        assert len(
            grantee_changes_post_grant["results"]) == num_docs_per_channel

    # Disable this conditional if https://github.com/couchbase/sync_gateway/issues/2277 is fixed
    if mode == "di":
        # Issue one more changes request from the post grant last seq and make sure there are no other changes
        grantee_changes_post_post_grant = client.get_changes(
            url=sg_url,
            db=sg_db,
            since=grantee_changes_post_grant["last_seq"],
            auth=grantee_session,
            feed="normal")
        assert len(grantee_changes_post_post_grant["results"]) == 0
Пример #25
0
def test_attachment_revpos_when_ancestor_unavailable(
        params_from_base_test_setup, sg_conf_name):
    """
    Creates a document with an attachment, then updates that document so that
    the body of the revision that originally pushed the document is no
    longer available.  Add a new revision that's not a child of the
    active revision, and validate that it's uploaded successfully.
    Example:
       1. Document is created with attachment at rev-1
       2. Document is updated (strip digests and length, only put revpos & stub) multiple times on the server, goes to rev-10
       3. Client attempts to add a new (conflicting) revision 2, with parent rev-1.
       4. If the body of rev-1 is no longer available on the server (temporary backup of revision has expired, and is no longer stored
         in the in-memory rev cache), we were throwing an error to client
         because we couldn't verify based on the _attachments property in rev-1.
       5. In this scenario, before returning error, we are now checking if the active revision has a common ancestor with the incoming revision.
    If so, we can validate any revpos values equal to or earlier than the common ancestor against the active revision
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    no_conflicts_enabled = params_from_base_test_setup["no_conflicts_enabled"]

    if no_conflicts_enabled:
        pytest.skip('--no-conflicts is not enabled, so skipping the test')

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    cluster_helper.reset_cluster(cluster_config, sg_conf)

    topology = cluster_helper.get_cluster_topology(cluster_config)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]
    sg_db = "db"
    bucket = "data-bucket"

    log_info("Running 'test_attachment_revpos_when_ancestor_unavailable'")
    log_info("Using cbs_url: {}".format(cbs_url))
    log_info("Using sg_url: {}".format(sg_url))
    log_info("Using sg_url_admin: {}".format(sg_url_admin))
    log_info("Using sg_db: {}".format(sg_db))
    log_info("Using bucket: {}".format(bucket))

    channels_list = ["ABC"]

    client = MobileRestClient()
    sg_util = SyncGateway()
    cb_server = couchbaseserver.CouchbaseServer(cbs_url)

    user1 = client.create_user(url=sg_url_admin,
                               db=sg_db,
                               name="user1",
                               password="******",
                               channels=channels_list)
    atts = attachment.load_from_data_dir(["sample_text.txt"])
    doc_with_att = document.create_doc(doc_id="att_doc",
                                       content={"sample_key": "sample_val"},
                                       attachments=atts,
                                       channels=channels_list)

    doc_gen_1 = client.add_doc(url=sg_url,
                               db=sg_db,
                               doc=doc_with_att,
                               auth=user1)
    client.update_doc(url=sg_url,
                      db=sg_db,
                      doc_id=doc_gen_1["id"],
                      number_updates=10,
                      auth=user1)

    # Clear cached rev doc bodys from server and cycle sync_gateway
    sg_util.stop_sync_gateways(cluster_config=cluster_config, url=sg_url)

    cb_server.delete_couchbase_server_cached_rev_bodies(bucket=bucket)
    sg_util.start_sync_gateways(cluster_config=cluster_config,
                                url=sg_url,
                                config=sg_conf)

    client.add_conflict(url=sg_url,
                        db=sg_db,
                        doc_id=doc_gen_1["id"],
                        parent_revisions=doc_gen_1["rev"],
                        new_revision="2-foo",
                        auth=user1)
Пример #26
0
def test_inline_large_attachments(setup_client_syncgateway_test):
    """
    1.  Start LiteServ and Sync Gateway
    2.  Create 2 databases on LiteServ (ls_db1, ls_db2)
    3.  Start continuous push replication from ls_db1 to sg_db
    4.  Start continuous pull replication from sg_db to ls_db2
    5.  PUT 5 large inline attachments to ls_db1
    6.  DELETE the docs on ls_db1
    7.  PUT same 5 large inline attachments to ls_db1
    8.  Verify docs replicate to ls_db2
    9.  Purge ls_db1
    10. Verify docs removed
    """

    log_info("Running 'test_inline_large_attachments' ...")

    cluster_config = setup_client_syncgateway_test["cluster_config"]
    sg_mode = setup_client_syncgateway_test["sg_mode"]
    sg_url = setup_client_syncgateway_test["sg_url"]
    sg_url_admin = setup_client_syncgateway_test["sg_admin_url"]

    ls_url = setup_client_syncgateway_test["ls_url"]

    log_info("ls_url: {}".format(ls_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    ls_db1 = "ls_db1"
    ls_db2 = "ls_db2"
    sg_db = "db"

    # Reset cluster to ensure no data in system
    sg_config = sync_gateway_config_path_for_mode(
        "listener_tests/listener_tests", sg_mode)
    c = cluster.Cluster(config=cluster_config)
    c.reset(sg_config_path=sg_config)

    client = MobileRestClient()
    client.create_database(ls_url, ls_db1)
    client.create_database(ls_url, ls_db2)

    # Start continuous push replication from ls_db1 -> sg_db
    client.start_replication(url=ls_url,
                             continuous=True,
                             from_db=ls_db1,
                             to_url=sg_url_admin,
                             to_db=sg_db)

    # Start continuous push replication from sg_db -> ls_db2
    client.start_replication(url=ls_url,
                             continuous=True,
                             from_url=sg_url_admin,
                             from_db=sg_db,
                             to_db=ls_db2)

    # doc with 2.36 PNG attachment
    attachment_docs = []
    for i in range(5):
        atts = attachment.load_from_data_dir(["golden_gate_large.jpg"])
        doc = document.create_doc(doc_id="large_attach_{}".format(i),
                                  attachments=atts,
                                  channels=["ABC"])
        attachment_docs.append(doc)

    # add large attachments to ls_db1
    docs = []
    for doc in attachment_docs:
        docs.append(client.add_doc(ls_url, ls_db1, doc, use_post=False))

    # Delete docs
    client.delete_docs(ls_url, ls_db1, docs)
    client.verify_docs_deleted(ls_url, ls_db1, docs)

    # Recreated docs
    recreated_docs = []
    for doc in attachment_docs:
        recreated_docs.append(
            client.add_doc(ls_url, ls_db1, doc, use_post=False))

    client.verify_docs_present(ls_url, ls_db1, recreated_docs)
    client.verify_docs_present(sg_url_admin, sg_db, recreated_docs)
    client.verify_docs_present(ls_url, ls_db2, recreated_docs)

    purged_docs = client.purge_docs(ls_url, ls_db1, recreated_docs)
    log_info(purged_docs)
    client.verify_docs_deleted(ls_url,
                               ls_db1,
                               recreated_docs,
                               reason="missing")
Пример #27
0
def test_writing_attachment_to_couchbase_server(params_from_base_test_setup,
                                                sg_conf_name):
    """
    1. Start sync_gateway with sync function that rejects all writes:
    function(doc, oldDoc) {
      throw({forbidden:"No writes!"});
    }
    2. Create a doc with attachment
    3. Use CBS sdk to see if attachment doc exists.  Doc ID will look like _sync:att:sha1-Kq5sNclPz7QV2+lfQIuc6R7oRu0= (where the suffix is the digest)
    4. Assert att doc does not exist
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    cluster_helper.reset_cluster(cluster_config, sg_conf)

    topology = cluster_helper.get_cluster_topology(cluster_config)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]
    sg_db = "db"
    bucket = "data-bucket"

    log_info("Running 'test_writing_attachment_to_couchbase_server'")
    log_info("Using cbs_url: {}".format(cbs_url))
    log_info("Using sg_url: {}".format(sg_url))
    log_info("Using sg_url_admin: {}".format(sg_url_admin))
    log_info("Using sg_db: {}".format(sg_db))
    log_info("Using bucket: {}".format(bucket))

    sg_user_name = "sg_user"
    sg_user_password = "******"

    sg_user_channels = ["NBC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin,
                       db=sg_db,
                       name=sg_user_name,
                       password=sg_user_password,
                       channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin,
                                            db=sg_db,
                                            name=sg_user_name)

    docs = client.add_docs(url=sg_url,
                           db=sg_db,
                           number=100,
                           id_prefix=sg_db,
                           channels=sg_user_channels,
                           auth=sg_user_session)
    assert len(docs) == 100

    # Create doc with attachment and push to sync_gateway
    atts = attachment.load_from_data_dir(["sample_text.txt"])
    doc_with_att = document.create_doc(doc_id="att_doc",
                                       content={"sample_key": "sample_val"},
                                       attachments=atts,
                                       channels=sg_user_channels)

    client.add_doc(url=sg_url,
                   db=sg_db,
                   doc=doc_with_att,
                   auth=sg_user_session)
    server = couchbaseserver.CouchbaseServer(cbs_url)

    # Assert that the attachment doc gets written to couchbase server
    server_att_docs = server.get_server_docs_with_prefix(bucket=bucket,
                                                         prefix="_sync:att:")
    num_att_docs = len(server_att_docs)
    assert num_att_docs == 1
def test_backfill_channels_oneshot_limit_changes(params_from_base_test_setup, sg_conf_name, grant_type):

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin", "pass", channels=["A"], roles=[])
    user_b_user_info = userinfo.UserInfo("USER_B", "pass", channels=["B"], roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url, db=sg_db,
                       name=admin_user_info.name, password=admin_user_info.password, channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url, db=sg_db,
                       name=user_b_user_info.name, password=user_b_user_info.password, channels=user_b_user_info.channels)

    admin_session = client.create_session(url=sg_admin_url, db=sg_db, name=admin_user_info.name, password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url, db=sg_db, name=user_b_user_info.name, password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url, db=sg_db, number=50, id_prefix=None, auth=admin_session, channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="b_doc", auth=user_b_session, channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=b_docs, auth=user_b_session, strict=True)

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url, db=sg_db, since=0, auth=user_b_session, feed="normal")

    # Grant access to channel "A"
    if grant_type == "CHANNEL-REST":
        log_info("Granting user access to channel A via Admin REST user update")
        # Grant via update to user in Admin API
        client.update_user(url=sg_admin_url, db=sg_db,
                           name=user_b_user_info.name, channels=["A", "B"])

    elif grant_type == "CHANNEL-SYNC":
        log_info("Granting user access to channel A sync function access()")
        # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
        access_doc = document.create_doc("channel_access", channels=["A"])
        access_doc["users"] = ["USER_B"]
        client.add_doc(url=sg_url, db=sg_db, doc=access_doc, auth=admin_session)

    elif grant_type == "ROLE-REST":
        log_info("Granting user access to channel A via Admin REST role grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])
        client.update_user(url=sg_admin_url, db=sg_db, name="USER_B", roles=["channel-A-role"])

    elif grant_type == "ROLE-SYNC":
        log_info("Granting user access to channel A via sync function role() grant")
        # Create role with channel A
        client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])

        # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
        role_access_doc = document.create_doc("role_access")
        role_access_doc["users"] = ["USER_B"]
        role_access_doc["roles"] = ["role:channel-A-role"]
        client.add_doc(sg_url, db=sg_db, doc=role_access_doc, auth=admin_session)

    else:
        pytest.fail("Unsupported grant_type!!!!")

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    log_info("Doing 3, 1 shot changes with limit and last seq!")
    # Issue 3 oneshot changes with a limit of 20

    #################
    # Changes Req #1
    #################
    user_b_changes_after_grant_one = client.get_changes(url=sg_url, db=sg_db,
                                                        since=user_b_changes["last_seq"], auth=user_b_session, feed="normal", limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_one["results"]) == 20

    for doc in user_b_changes_after_grant_one["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #2
    #################
    user_b_changes_after_grant_two = client.get_changes(url=sg_url, db=sg_db,
                                                        since=user_b_changes_after_grant_one["last_seq"],
                                                        auth=user_b_session, feed="normal", limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_two["results"]) == 20

    for doc in user_b_changes_after_grant_two["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    #################
    # Changes Req #3
    #################
    user_b_changes_after_grant_three = client.get_changes(url=sg_url, db=sg_db,
                                                          since=user_b_changes_after_grant_two["last_seq"],
                                                          auth=user_b_session, feed="normal", limit=20)

    # User B shoud have recieved 20 docs due to limit
    assert len(user_b_changes_after_grant_three["results"]) == 10

    for doc in user_b_changes_after_grant_three["results"]:
        # cross off keys found from 'a_docs' dictionary
        del ids_and_revs_from_a_docs[doc["id"]]

    # Make sure all the docs have been crossed out
    assert len(ids_and_revs_from_a_docs) == 0

    #################
    # Changes Req #4
    #################
    user_b_changes_after_grant_four = client.get_changes(url=sg_url, db=sg_db,
                                                         since=user_b_changes_after_grant_three["last_seq"],
                                                         auth=user_b_session, feed="normal", limit=20)

    # Changes should be caught up and there should be no results
    assert len(user_b_changes_after_grant_four["results"]) == 0
def test_attachment_revpos_when_ancestor_unavailable(params_from_base_test_setup, sg_conf_name):
    """
    Creates a document with an attachment, then updates that document so that
    the body of the revision that originally pushed the document is no
    longer available.  Add a new revision that's not a child of the
    active revision, and validate that it's uploaded successfully.
    Example:
       1. Document is created with attachment at rev-1
       2. Document is updated (strip digests and length, only put revpos & stub) multiple times on the server, goes to rev-4
       3. Client attempts to add a new (conflicting) revision 2, with parent rev-1.
       4. If the body of rev-1 is no longer available on the server (temporary backup of revision has expired, and is no longer stored
         in the in-memory rev cache), we were throwing an error to client
         because we couldn't verify based on the _attachments property in rev-1.
       5. In this scenario, before returning error, we are now checking if the active revision has a common ancestor with the incoming revision.
    If so, we can validate any revpos values equal to or earlier than the common ancestor against the active revision
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    cluster_helper.reset_cluster(cluster_config, sg_conf)

    topology = cluster_helper.get_cluster_topology(cluster_config)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]
    sg_db = "db"
    bucket = "data-bucket"

    log_info("Running 'test_attachment_revpos_when_ancestor_unavailable'")
    log_info("Using cbs_url: {}".format(cbs_url))
    log_info("Using sg_url: {}".format(sg_url))
    log_info("Using sg_url_admin: {}".format(sg_url_admin))
    log_info("Using sg_db: {}".format(sg_db))
    log_info("Using bucket: {}".format(bucket))

    channels_list = ["ABC"]

    client = MobileRestClient()
    sg_util = SyncGateway()
    cb_server = CouchbaseServer(cbs_url)

    user1 = client.create_user(url=sg_url_admin, db=sg_db, name="user1", password="******", channels=channels_list)
    doc_with_att = document.create_doc(doc_id="att_doc", content={"sample_key": "sample_val"}, attachment_name="sample_text.txt", channels=channels_list)

    doc_gen_1 = client.add_doc(url=sg_url, db=sg_db, doc=doc_with_att, auth=user1)
    client.update_doc(url=sg_url, db=sg_db, doc_id=doc_gen_1["id"], number_updates=10, auth=user1)

    # Clear cached rev doc bodys from server and cycle sync_gateway
    sg_util.stop_sync_gateway(cluster_config=cluster_config, url=sg_url)

    cb_server.delete_couchbase_server_cached_rev_bodies(bucket=bucket)
    sg_util.start_sync_gateway(cluster_config=cluster_config, url=sg_url, config=sg_conf)

    client.add_conflict(
        url=sg_url, db=sg_db,
        doc_id=doc_gen_1["id"],
        parent_revisions=doc_gen_1["rev"],
        new_revision="2-foo",
        auth=user1
    )
def test_backfill_channels_looping_longpoll_changes(params_from_base_test_setup, sg_conf_name, grant_type):

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    log_info("grant_type: {}".format(grant_type))

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()

    admin_user_info = userinfo.UserInfo("admin", "pass", channels=["A"], roles=[])
    user_b_user_info = userinfo.UserInfo("USER_B", "pass", channels=["B"], roles=[])

    # Create users / sessions
    client.create_user(url=sg_admin_url, db=sg_db,
                       name=admin_user_info.name, password=admin_user_info.password, channels=admin_user_info.channels)

    client.create_user(url=sg_admin_url, db=sg_db,
                       name=user_b_user_info.name, password=user_b_user_info.password, channels=user_b_user_info.channels)

    admin_session = client.create_session(url=sg_admin_url, db=sg_db, name=admin_user_info.name, password=admin_user_info.password)
    user_b_session = client.create_session(url=sg_admin_url, db=sg_db, name=user_b_user_info.name, password=user_b_user_info.password)

    # Create 50 "A" channel docs
    a_docs = client.add_docs(url=sg_url, db=sg_db, number=50, id_prefix=None, auth=admin_session, channels=["A"])
    assert len(a_docs) == 50

    b_docs = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="b_doc", auth=user_b_session, channels=["B"])
    assert len(b_docs) == 1

    user_doc = {"id": "_user/USER_B", "rev": None}
    b_docs.append(user_doc)

    # Loop until user_b sees b_doc_0 doc and _user/USER_B doc
    client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=b_docs, auth=user_b_session, strict=True)

    # Create a dictionary keyed on doc id for all of channel A docs
    ids_and_revs_from_a_docs = {doc["id"]: doc["rev"] for doc in a_docs}
    assert len(ids_and_revs_from_a_docs.keys()) == 50

    # Get last_seq for user_b
    user_b_changes = client.get_changes(url=sg_url, db=sg_db, since=0, auth=user_b_session, feed="normal")

    with concurrent.futures.ProcessPoolExecutor() as ex:

        # Start long poll changes feed.
        changes_task = ex.submit(client.get_changes,
                                 url=sg_url, db=sg_db, since=user_b_changes["last_seq"], auth=user_b_session, timeout=10, limit=20)

        # Grant access to channel "A"
        if grant_type == "CHANNEL-REST":
            log_info("Granting user access to channel A via Admin REST user update")
            # Grant via update to user in Admin API
            client.update_user(url=sg_admin_url, db=sg_db,
                               name=user_b_user_info.name, channels=["A", "B"])

        elif grant_type == "CHANNEL-SYNC":
            log_info("Granting user access to channel A sync function access()")
            # Grant via access() in sync_function, then id 'channel_access' will trigger an access(doc.users, doc.channels)
            access_doc = document.create_doc("channel_access", channels=["A"])
            access_doc["users"] = ["USER_B"]
            client.add_doc(url=sg_url, db=sg_db, doc=access_doc, auth=admin_session)

        elif grant_type == "ROLE-REST":
            log_info("Granting user access to channel A via Admin REST role grant")
            # Create role with channel A
            client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])
            client.update_user(url=sg_admin_url, db=sg_db, name="USER_B", roles=["channel-A-role"])

        elif grant_type == "ROLE-SYNC":
            log_info("Granting user access to channel A via sync function role() grant")
            # Create role with channel A
            client.create_role(url=sg_admin_url, db=sg_db, name="channel-A-role", channels=["A"])

            # Grant via role() in sync_function, then id 'role_access' will trigger an role(doc.users, doc.roles)
            role_access_doc = document.create_doc("role_access")
            role_access_doc["users"] = ["USER_B"]
            role_access_doc["roles"] = ["role:channel-A-role"]
            client.add_doc(sg_url, db=sg_db, doc=role_access_doc, auth=admin_session)

        else:
            pytest.fail("Unsupported grant_type!!!!")

        # Block on return of longpoll changes, feed should wake up and return 20 results
        changes = changes_task.result()

    assert len(changes["results"]) == 20
    num_requests = 1

    # Cross the results off from the 'a_docs' dictionary
    for doc in changes["results"]:
        del ids_and_revs_from_a_docs[doc["id"]]

    # Start looping longpoll changes with limit, cross off changes from dictionary each time one is found
    # Since 20 changes should be crossed off already, this should execute 2x.
    log_info("Starting looping longpoll changes with limit!")
    last_seq = changes["last_seq"]
    while True:

        if len(ids_and_revs_from_a_docs.keys()) == 0:
            log_info("All docs were found! Exiting polling loop")
            break

        changes = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=user_b_session, limit=20, timeout=10)
        num_requests += 1

        # There are more than 2 requests, throw an exception.
        if num_requests == 2:
            assert len(changes["results"]) == 20
        elif num_requests == 3:
            # This will be 10 or 11 depending on if the _user/ doc is returned
            assert 10 <= len(changes["results"]) <= 11
        else:
            raise exceptions.ChangesError("Looping longpoll should only have to perform 3 requests to get all the changes!!")

        # Cross the results off from the 'a_docs' dictionary.
        # This will blow up in docs duplicate docs are sent to changes
        for doc in changes["results"]:
            if doc["id"] != "_user/USER_B":
                del ids_and_revs_from_a_docs[doc["id"]]

        last_seq = changes["last_seq"]

    # Shanges after longpoll
    zero_results = client.get_changes(url=sg_url, db=sg_db,
                                      since=last_seq,
                                      auth=user_b_session, feed="normal")

    # Changes should be caught up and there should be no results
    assert len(zero_results["results"]) == 0
def test_attachment_revpos_when_ancestor_unavailable_active_revision_doesnt_share_ancestor(params_from_base_test_setup, sg_conf_name):
    """
    Creates a document with an attachment, then updates that document so that
    the body of the revision that originally pushed the document is no
    longer available.  Add a new revision that's not a child of the
    active revision, and validate that it's uploaded successfully.
    Example:
       1. Document is created with no attachment at rev-1
       2. Server adds revision with attachment at rev-2 {"hello.txt", revpos=2}
       2. Document is updated multiple times on the server, goes to rev-4
       3. Client attempts to add a new (conflicting) revision 3a, with ancestors rev-2a (with it's own attachment), rev-1.
       4. When client attempts to push rev-3a with attachment stub {"hello.txt", revpos=2}.  Should throw an error, since the revpos
       of the attachment is later than the common ancestor (rev-1)
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    cluster_helper.reset_cluster(cluster_config, sg_conf)

    topology = cluster_helper.get_cluster_topology(cluster_config)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]
    sg_db = "db"
    bucket = "data-bucket"

    log_info("Running 'test_attachment_revpos_when_ancestor_unavailable_active_revision_doesnt_share_ancestor'")
    log_info("Using cbs_url: {}".format(cbs_url))
    log_info("Using sg_url: {}".format(sg_url))
    log_info("Using sg_url_admin: {}".format(sg_url_admin))
    log_info("Using sg_db: {}".format(sg_db))
    log_info("Using bucket: {}".format(bucket))

    sg_user_name = "sg_user"
    sg_user_password = "******"

    sg_user_channels = ["NBC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin, db=sg_db, name=sg_user_name, password=sg_user_password, channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin, db=sg_db, name=sg_user_name)

    doc = document.create_doc(doc_id="doc_1", content={"sample_key": "sample_val"}, channels=sg_user_channels)
    doc_gen_1 = client.add_doc(url=sg_url, db=sg_db, doc=doc, auth=sg_user_session)
    client.update_doc(url=sg_url, db=sg_db, doc_id=doc_gen_1["id"], attachment_name="sample_text.txt", auth=sg_user_session)
    client.update_doc(url=sg_url, db=sg_db, doc_id=doc_gen_1["id"], auth=sg_user_session)
    client.update_doc(url=sg_url, db=sg_db, doc_id=doc_gen_1["id"], auth=sg_user_session)

    parent_rev_list = ["2-foo2", doc_gen_1["rev"]]

    # Sync Gateway should error since it has no references attachment in its ancestors
    with pytest.raises(HTTPError) as he:
        client.add_conflict(
            url=sg_url,
            db=sg_db,
            doc_id=doc_gen_1["id"],
            parent_revisions=parent_rev_list,
            new_revision="3-foo3",
            auth=sg_user_session
        )
    assert he.value[0].startswith("400 Client Error: Bad Request for url: ")
Пример #32
0
def test_string_expiry_as_iso_8601_date(params_from_base_test_setup,
                                        sg_conf_name):
    """
    1. Calculate (server time + 3 seconds) as ISO-8601 date (e.g. 2016-01-01T00:00:00.000+00:00)
    2. PUT /db/doc1 via SG with property "_exp":"[date]"
       PUT /db/doc2 via SG with property "_exp":"2026-01-01T00:00:00.000+00:00"
    3. Wait five seconds
    4. Get /db/doc1.  Assert response is 404
       Get /db/doc2.  Assert response is 20
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]
    xattrs_enabled = params_from_base_test_setup['xattrs_enabled']

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(cluster_config=cluster_config,
                                 sync_gateway_config=sg_conf)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_string_expiry_as_ISO_8601_Date'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]
    bucket_name = "data-bucket"
    cbs_ip = host_for_url(cbs_url)

    sdk_client = Bucket('couchbase://{}/{}'.format(cbs_ip, bucket_name),
                        password='******')

    client = MobileRestClient()

    client.create_user(url=sg_url_admin,
                       db=sg_db,
                       name=sg_user_name,
                       password=sg_user_password,
                       channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin,
                                            db=sg_db,
                                            name=sg_user_name)

    time_util = Time()
    iso_datetime = time_util.get_iso_datetime(delta=3)

    doc_exp_3_body = document.create_doc(doc_id="exp_3",
                                         expiry=iso_datetime,
                                         channels=sg_user_channels)
    doc_exp_years_body = document.create_doc(
        doc_id="exp_years",
        expiry="2026-01-01T00:00:00.000+00:00",
        channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url,
                               db=sg_db,
                               doc=doc_exp_3_body,
                               auth=sg_user_session)
    doc_exp_years = client.add_doc(url=sg_url,
                                   db=sg_db,
                                   doc=doc_exp_years_body,
                                   auth=sg_user_session)

    # Sleep should allow doc_exp_3 to expire
    time.sleep(10)

    # doc_exp_3 should be expired
    with pytest.raises(HTTPError) as he:
        client.get_doc(url=sg_url,
                       db=sg_db,
                       doc_id=doc_exp_3["id"],
                       auth=sg_user_session)

    # In XATTR mode, the expiry results in a tombstone
    # In Doc Meta mode, the expiry results in a purge
    if xattrs_enabled:
        assert he.value[0].startswith("403 Client Error: Forbidden for url:")
    else:
        assert he.value[0].startswith("404 Client Error: Not Found for url:")

    verify_doc_deletion_on_server(doc_id=doc_exp_3["id"],
                                  sdk_client=sdk_client,
                                  sg_client=client,
                                  sg_admin_url=sg_url_admin,
                                  sg_db=sg_db,
                                  xattrs_enabled=xattrs_enabled)

    # doc_exp_years should be available still
    doc_exp_years_result = client.get_doc(url=sg_url,
                                          db=sg_db,
                                          doc_id=doc_exp_years["id"],
                                          auth=sg_user_session)
    assert doc_exp_years_result["_id"] == "exp_years"
Пример #33
0
def test_winning_conflict_branch_revisions(params_from_base_test_setup,
                                           sg_conf_name):
    """ Add winning conflict revisions to the revision tree and ensure
    that the changes feed returns the correct revisions

    Steps:
    - Add a doc ('test_doc')
    - Add 5 revs to 'test_doc'
    - POST _changes, assert rev starts with "6-" from 0, store "last_seq_1"
    - Create a conflict off first revision ("2-foo") (POST docs, new_edits == false)
    - Append 5 revisions to the conflicting branch
        (3-foo with 2-foo as parent, 4-foo with 3-foo as parent ... 7-foo with 6-foo as parent)
    - GET 'test_doc' and verify that the rev is '7-foo'
    - POST _changes, assert returns 7-foo
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]
    no_conflicts_enabled = params_from_base_test_setup["no_conflicts_enabled"]

    if no_conflicts_enabled:
        pytest.skip(
            '--no-conflicts is enabled, this test needs to create conflicts, so skipping the test'
        )

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    c = cluster.Cluster(cluster_config)
    c.reset(sg_conf)

    client = MobileRestClient()

    seth_user_info = userinfo.UserInfo(name="seth",
                                       password="******",
                                       channels=["NATGEO"],
                                       roles=[])

    seth_auth = client.create_user(url=sg_admin_url,
                                   db=sg_db,
                                   name=seth_user_info.name,
                                   password=seth_user_info.password,
                                   channels=seth_user_info.channels)

    test_doc_body = document.create_doc(doc_id="test_doc",
                                        channels=seth_user_info.channels)
    rev_gen_1_doc = client.add_doc(url=sg_url,
                                   db=sg_db,
                                   doc=test_doc_body,
                                   auth=seth_auth)

    rev_gen_6_doc = client.update_doc(url=sg_url,
                                      db=sg_db,
                                      doc_id=rev_gen_1_doc["id"],
                                      number_updates=5,
                                      auth=seth_auth)
    assert rev_gen_6_doc["rev"].startswith("6-")

    # Wait until doc shows up in changes feed
    last_seq = 0
    start = time.time()
    while True:

        if time.time() - start > keywords.constants.CLIENT_REQUEST_TIMEOUT:
            raise keywords.exceptions.TimeoutError(
                "Wait for Replication Status Idle: TIMEOUT")

        changes_1 = client.get_changes(url=sg_url,
                                       db=sg_db,
                                       since=last_seq,
                                       auth=seth_auth,
                                       skip_user_docs=True)
        last_seq = changes_1["last_seq"]
        if len(changes_1["results"]) > 0 and changes_1["results"][0][
                "changes"][0]["rev"].startswith("6-"):
            break

    assert len(changes_1["results"]) == 1
    assert changes_1["results"][0]["id"] == "test_doc"
    assert changes_1["results"][0]["changes"][0]["rev"].startswith("6-")

    # Create a conflict off of rev one
    rev_gen_1_doc_conflict = client.add_conflict(
        url=sg_url,
        db=sg_db,
        doc_id=rev_gen_1_doc["id"],
        parent_revisions=rev_gen_1_doc["rev"],
        new_revision="2-foo",
        auth=seth_auth)

    # Update the conflicting branch 5x
    rev_gen = 3
    for _ in range(5):
        rev_gen_1_doc_conflict = client.add_conflict(
            url=sg_url,
            db=sg_db,
            doc_id=rev_gen_1_doc["id"],
            parent_revisions=rev_gen_1_doc_conflict["rev"],
            new_revision="{}-foo".format(rev_gen),
            auth=seth_auth)
        rev_gen += 1

    # Wait until doc shows up in changes feed from last_seq from where last changes loop from above left off
    start = time.time()
    while True:

        if time.time() - start > keywords.constants.CLIENT_REQUEST_TIMEOUT:
            raise keywords.exceptions.TimeoutError(
                "Wait for Replication Status Idle: TIMEOUT")

        changes_2 = client.get_changes(url=sg_url,
                                       db=sg_db,
                                       since=last_seq,
                                       auth=seth_auth)
        last_seq = changes_2["last_seq"]
        if changes_2["results"][0]["changes"][0]["rev"] == "7-foo" and len(
                changes_2["results"]) > 0:
            break

    # Verify that the the "7-foo" rev is return on the changes feed
    assert len(changes_2["results"]) == 1
    assert changes_2["results"][0]["id"] == "test_doc"
    assert changes_2["results"][0]["changes"][0]["rev"] == "7-foo"
Пример #34
0
def test_removing_expiry(params_from_base_test_setup, sg_conf_name):
    """
    1. PUT /db/doc1 via SG with property "_exp":3
    2. Update /db/doc1 with a new revision with no expiry value
    3. After 10 updates, update /db/doc1 with a revision with no expiry
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    mode = params_from_base_test_setup["mode"]

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster_helper = ClusterKeywords()
    topology = cluster_helper.get_cluster_topology(cluster_config)

    cluster_helper.reset_cluster(cluster_config=cluster_config,
                                 sync_gateway_config=sg_conf)

    cbs_url = topology["couchbase_servers"][0]
    sg_url = topology["sync_gateways"][0]["public"]
    sg_url_admin = topology["sync_gateways"][0]["admin"]

    log_info("Running 'test_removing_expiry'")
    log_info("cbs_url: {}".format(cbs_url))
    log_info("sg_url: {}".format(sg_url))
    log_info("sg_url_admin: {}".format(sg_url_admin))

    sg_db = "db"
    sg_user_name = "sg_user"
    sg_user_password = "******"
    sg_user_channels = ["NBC", "ABC"]

    client = MobileRestClient()

    client.create_user(url=sg_url_admin,
                       db=sg_db,
                       name=sg_user_name,
                       password=sg_user_password,
                       channels=sg_user_channels)
    sg_user_session = client.create_session(url=sg_url_admin,
                                            db=sg_db,
                                            name=sg_user_name)

    doc_exp_3_body = document.create_doc(doc_id="exp_3",
                                         expiry=3,
                                         channels=sg_user_channels)
    doc_exp_10_body = document.create_doc(doc_id="exp_10",
                                          expiry=10,
                                          channels=sg_user_channels)

    doc_exp_3 = client.add_doc(url=sg_url,
                               db=sg_db,
                               doc=doc_exp_3_body,
                               auth=sg_user_session)
    doc_exp_10 = client.add_doc(url=sg_url,
                                db=sg_db,
                                doc=doc_exp_10_body,
                                auth=sg_user_session)

    doc_exp_3_updated = client.update_doc(url=sg_url,
                                          db=sg_db,
                                          doc_id=doc_exp_3["id"],
                                          number_updates=10,
                                          auth=sg_user_session)

    # Sleep should allow an expiry to happen on doc_exp_3 if it had not been removed.
    # Expected behavior is that the doc_exp_3 will still be around due to the removal of the expiry
    time.sleep(5)

    # doc_exp_3 should no longer have an expiry and should not raise an exception
    doc_exp_3_updated_result = client.get_doc(url=sg_url,
                                              db=sg_db,
                                              doc_id=doc_exp_3_updated["id"],
                                              auth=sg_user_session)
    assert doc_exp_3_updated_result["_id"] == "exp_3"

    # doc_exp_10 should be available still and should not raise an exception
    doc_exp_10_result = client.get_doc(url=sg_url,
                                       db=sg_db,
                                       doc_id=doc_exp_10["id"],
                                       auth=sg_user_session)
    assert doc_exp_10_result["_id"] == "exp_10"
Пример #35
0
def test_non_winning_revisions(params_from_base_test_setup, sg_conf_name):
    """ Add non-winning revisions to the revision tree and ensure
    that the changes feed returns the correct revisions

    Steps:
    - Add a doc
    - Add 5 revs
    - changes, assert rev starts with "6-" from 0, store "last_seq_1"
    - Create a conflict off first revision ("2-foo") (POST docs, new_edits == false)
    - changes, assert rev starts with "6-" from "last_seq_1", store "last_seq_2"
    - changes, assert rev starts with "6-" from 0
    - Add a "3-foo" rev with rev "2-foo" as parent
    - changes, assert rev starts with "6-" from "last_seq_2", store "last_seq_3"
    - changes, assert rev starts with "6-" from 0
    - add tombstone rev as child of "6-" i.e. issue delete on ("6-")
    - changes, assert rev starts with "3-foo" from "last_seq_3"
    - changes, assert rev starts with "3-foo" from 0
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]
    no_conflicts_enabled = params_from_base_test_setup["no_conflicts_enabled"]

    if no_conflicts_enabled:
        pytest.skip(
            '--no-conflicts is enabled, this test needs to create conflicts, so skipping the test'
        )

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    c = cluster.Cluster(cluster_config)
    c.reset(sg_conf)

    client = MobileRestClient()

    seth_user_info = userinfo.UserInfo(name="seth",
                                       password="******",
                                       channels=["NATGEO"],
                                       roles=[])

    seth_auth = client.create_user(url=sg_admin_url,
                                   db=sg_db,
                                   name=seth_user_info.name,
                                   password=seth_user_info.password,
                                   channels=seth_user_info.channels)

    test_doc_body = document.create_doc(doc_id="test_doc",
                                        channels=seth_user_info.channels)
    rev_gen_1_doc = client.add_doc(url=sg_url,
                                   db=sg_db,
                                   doc=test_doc_body,
                                   auth=seth_auth)

    rev_gen_6_doc = client.update_doc(url=sg_url,
                                      db=sg_db,
                                      doc_id=rev_gen_1_doc["id"],
                                      number_updates=5,
                                      auth=seth_auth)
    assert rev_gen_6_doc["rev"].startswith("6-")

    # Get changes until rev generation 6 document shows up
    start = time.time()
    last_seq = 0
    while True:
        if time.time() - start > keywords.constants.CLIENT_REQUEST_TIMEOUT:
            raise keywords.exceptions.TimeoutError(
                "Wait for Replication Status Idle: TIMEOUT")

        changes_1 = client.get_changes(url=sg_url,
                                       db=sg_db,
                                       since=last_seq,
                                       auth=seth_auth,
                                       skip_user_docs=True)
        last_seq = changes_1["last_seq"]

        # break when expected rev shows up in changes feed
        if changes_1["results"] and changes_1["results"][0]["changes"][0][
                "rev"].startswith("6-"):
            break

    assert len(changes_1["results"]) == 1
    assert changes_1["results"][0]["id"] == "test_doc"
    assert changes_1["results"][0]["changes"][0]["rev"].startswith("6-")

    # Create a conflict off of rev one
    rev_gen_2_doc_conflict = client.add_conflict(
        url=sg_url,
        db=sg_db,
        doc_id=rev_gen_1_doc["id"],
        parent_revisions=rev_gen_1_doc["rev"],
        new_revision="2-foo",
        auth=seth_auth)
    assert rev_gen_2_doc_conflict["id"] == "test_doc"
    assert rev_gen_2_doc_conflict["rev"] == "2-foo"

    # Issue changes since changes_1 last_seq above
    changes_2 = client.get_changes(url=sg_url,
                                   db=sg_db,
                                   since=changes_1["last_seq"],
                                   auth=seth_auth)
    assert len(changes_2["results"]) == 1
    assert changes_2["results"][0]["id"] == "test_doc"
    assert changes_2["results"][0]["changes"][0]["rev"].startswith("6-")

    # Issue changes since 0, strip user doc and make sure the doc is still the '6-' rev
    changes_from_0_one = client.get_changes(url=sg_url,
                                            db=sg_db,
                                            since=0,
                                            auth=seth_auth,
                                            skip_user_docs=True)
    assert len(changes_from_0_one["results"]) == 1
    assert changes_from_0_one["results"][0]["id"] == "test_doc"
    assert changes_from_0_one["results"][0]["changes"][0]["rev"].startswith(
        "6-")

    # Create a 3-foo rev with 2-foo as the parent
    rev_gen_3_doc_conflict = client.add_conflict(
        url=sg_url,
        db=sg_db,
        doc_id=rev_gen_2_doc_conflict["id"],
        parent_revisions=rev_gen_2_doc_conflict["rev"],
        new_revision="3-foo",
        auth=seth_auth)
    assert rev_gen_3_doc_conflict["id"] == "test_doc"
    assert rev_gen_3_doc_conflict["rev"] == "3-foo"

    # Issue changes since changes_2 last_seq above
    changes_3 = client.get_changes(url=sg_url,
                                   db=sg_db,
                                   since=changes_2["last_seq"],
                                   auth=seth_auth)
    assert len(changes_3["results"]) == 1
    assert changes_3["results"][0]["id"] == "test_doc"
    assert changes_3["results"][0]["changes"][0]["rev"].startswith("6-")

    # Issue changes since 0, strip user doc and make sure the doc is still the '6-' rev
    changes_from_0_two = client.get_changes(url=sg_url,
                                            db=sg_db,
                                            since=0,
                                            auth=seth_auth,
                                            skip_user_docs=True)
    assert len(changes_from_0_two["results"]) == 1
    assert changes_from_0_two["results"][0]["id"] == "test_doc"
    assert changes_from_0_two["results"][0]["changes"][0]["rev"].startswith(
        "6-")

    # Delete test_doc at rev 6-*
    client.delete_doc(url=sg_url,
                      db=sg_db,
                      doc_id=rev_gen_6_doc["id"],
                      rev=rev_gen_6_doc["rev"],
                      auth=seth_auth)

    # Issue changes since changes_3 last_seq above
    changes_4 = client.get_changes(url=sg_url,
                                   db=sg_db,
                                   since=changes_3["last_seq"],
                                   auth=seth_auth)
    assert len(changes_4["results"]) == 1
    assert changes_4["results"][0]["id"] == "test_doc"
    assert changes_4["results"][0]["changes"][0]["rev"] == "3-foo"

    # Issue a oneshot changes since changes_4 last_seq and assert no results are returned
    changes_5 = client.get_changes(url=sg_url,
                                   db=sg_db,
                                   since=changes_4["last_seq"],
                                   feed="normal",
                                   auth=seth_auth)
    assert len(changes_5["results"]) == 0
Пример #36
0
def test_document(doc_id, content, attachments, expiry, channels, expected_doc):
    doc = document.create_doc(doc_id=doc_id, content=content, attachments=attachments, expiry=expiry, channels=channels)
    assert doc == expected_doc
Пример #37
0
def test_winning_conflict_branch_revisions(params_from_base_test_setup, sg_conf_name):
    """ Add winning conflict revisions to the revision tree and ensure
    that the changes feed returns the correct revisions

    Steps:
    - Create a doc ('test_doc')
    - Add 5 revs to 'test_doc'
    - POST _changes, assert rev starts with "6-" from 0, store "last_seq_1"
    - Create a conflict off first revision ("2-foo") (POST docs, new_edits == false)
    - Append 5 revisions to the conflicting branch
        (3-foo with 2-foo as parent, 4-foo with 3-foo as parent ... 7-foo with 6-foo as parent)
    - GET 'test_doc' and verify that the rev is '7-foo'
    - POST _changes, assert returns 7-foo
    """

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    sg_db = "db"

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    c = cluster.Cluster(cluster_config)
    c.reset(sg_conf)

    client = MobileRestClient()

    seth_user_info = userinfo.UserInfo(
        name="seth",
        password="******",
        channels=["NATGEO"],
        roles=[]
    )

    seth_auth = client.create_user(
        url=sg_admin_url,
        db=sg_db,
        name=seth_user_info.name,
        password=seth_user_info.password,
        channels=seth_user_info.channels
    )

    test_doc_body = document.create_doc(doc_id="test_doc", channels=seth_user_info.channels)
    rev_gen_1_doc = client.add_doc(url=sg_url, db=sg_db, doc=test_doc_body, auth=seth_auth)

    rev_gen_6_doc = client.update_doc(url=sg_url, db=sg_db, doc_id=rev_gen_1_doc["id"], number_updates=5, auth=seth_auth)
    assert rev_gen_6_doc["rev"].startswith("6-")

    # Wait until doc shows up in changes feed
    last_seq = 0
    start = time.time()
    while True:

        if time.time() - start > keywords.constants.CLIENT_REQUEST_TIMEOUT:
            raise keywords.exceptions.TimeoutError("Wait for Replication Status Idle: TIMEOUT")

        changes_1 = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=seth_auth, skip_user_docs=True)
        last_seq = changes_1["last_seq"]
        if len(changes_1["results"]) > 0 and changes_1["results"][0]["changes"][0]["rev"].startswith("6-"):
            break

    assert len(changes_1["results"]) == 1
    assert changes_1["results"][0]["id"] == "test_doc"
    assert changes_1["results"][0]["changes"][0]["rev"].startswith("6-")

    # Create a conflict off of rev one
    rev_gen_1_doc_conflict = client.add_conflict(
        url=sg_url,
        db=sg_db,
        doc_id=rev_gen_1_doc["id"],
        parent_revisions=rev_gen_1_doc["rev"],
        new_revision="2-foo",
        auth=seth_auth
    )

    # Update the conflicting branch 5x
    rev_gen = 3
    for _ in range(5):
        rev_gen_1_doc_conflict = client.add_conflict(
            url=sg_url,
            db=sg_db,
            doc_id=rev_gen_1_doc["id"],
            parent_revisions=rev_gen_1_doc_conflict["rev"],
            new_revision="{}-foo".format(rev_gen),
            auth=seth_auth
        )
        rev_gen += 1

    # Wait until doc shows up in changes feed from last_seq from where last changes loop from above left off
    start = time.time()
    while True:

        if time.time() - start > keywords.constants.CLIENT_REQUEST_TIMEOUT:
            raise keywords.exceptions.TimeoutError("Wait for Replication Status Idle: TIMEOUT")

        changes_2 = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=seth_auth)
        last_seq = changes_2["last_seq"]
        if len(changes_2["results"]) > 0:
            break

    # Verify that the the "7-foo" rev is return on the changes feed
    assert len(changes_2["results"]) == 1
    assert changes_2["results"][0]["id"] == "test_doc"
    assert changes_2["results"][0]["changes"][0]["rev"] == "7-foo"
Пример #38
0
def test_document_channels_not_list():
    with pytest.raises(TypeError):
        document.create_doc(None, None, None, None, "B")
Пример #39
0
def test_rollback_server_reset(params_from_base_test_setup, sg_conf_name):
    """
    Test for sync gateway resiliency under Couchbase Server rollback

    Scenario
    1. Create user (seth:pass) and session
    2. Add docs targeting all vbuckets except 66
    3. Add docs to vbucket 66
    4. Verify the docs show up in seth's changes feed
    5. Delete vBucket 66 file on server
    6. Restart server
    7. User should only see docs not in vbucket 66
    """

    num_vbuckets = 1024

    cluster_config = params_from_base_test_setup["cluster_config"]
    topology = params_from_base_test_setup["cluster_topology"]
    mode = params_from_base_test_setup["mode"]

    sg_url = topology["sync_gateways"][0]["public"]
    sg_admin_url = topology["sync_gateways"][0]["admin"]
    cb_server_url = topology["couchbase_servers"][0]
    cb_server = couchbaseserver.CouchbaseServer(cb_server_url)

    sg_db = "db"

    if mode == "cc":
        pytest.skip("Rollback not supported in channel cache mode")

    sg_conf = sync_gateway_config_path_for_mode(sg_conf_name, mode)

    cluster = Cluster(cluster_config)
    cluster.reset(sg_conf)

    client = MobileRestClient()
    seth_user_info = userinfo.UserInfo("seth",
                                       "pass",
                                       channels=["NASA"],
                                       roles=[])

    client.create_user(url=sg_admin_url,
                       db=sg_db,
                       name=seth_user_info.name,
                       password=seth_user_info.password,
                       channels=seth_user_info.channels)

    seth_session = client.create_session(url=sg_admin_url,
                                         db=sg_db,
                                         name=seth_user_info.name,
                                         password=seth_user_info.password)

    # create a doc that will hash to each vbucket in parallel except for vbucket 66
    doc_id_for_every_vbucket_except_66 = []
    with concurrent.futures.ProcessPoolExecutor() as pex:
        futures = [
            pex.submit(document.generate_doc_id_for_vbucket, i)
            for i in range(num_vbuckets) if i != 66
        ]
        for future in concurrent.futures.as_completed(futures):
            doc_id = future.result()
            doc = document.create_doc(doc_id=doc_id,
                                      channels=seth_user_info.channels)
            doc_id_for_every_vbucket_except_66.append(doc)

    vbucket_66_docs = []
    for _ in range(5):
        vbucket_66_docs.append(
            document.create_doc(
                doc_id=document.generate_doc_id_for_vbucket(66),
                channels=seth_user_info.channels))

    seth_docs = client.add_bulk_docs(url=sg_url,
                                     db=sg_db,
                                     docs=doc_id_for_every_vbucket_except_66,
                                     auth=seth_session)
    seth_66_docs = client.add_bulk_docs(url=sg_url,
                                        db=sg_db,
                                        docs=vbucket_66_docs,
                                        auth=seth_session)

    assert len(seth_docs) == num_vbuckets - 1
    assert len(seth_66_docs) == 5

    # Verify the all docs show up in seth's changes feed
    all_docs = seth_docs + seth_66_docs
    assert len(all_docs) == (num_vbuckets - 1) + 5

    client.verify_docs_in_changes(url=sg_url,
                                  db=sg_db,
                                  expected_docs=all_docs,
                                  auth=seth_session)

    # Delete vbucket and restart server
    cb_server.delete_vbucket(66, "data-bucket")
    cb_server.restart()
    max_retries = 50
    count = 0
    while count != max_retries:
        # Try to get changes, sync gateway should be able to recover and return changes
        # A changes since=0 should now be in a rolled back state due to the data loss from the removed vbucket
        # Seth should only see the docs not present in vbucket 66, unlike all the docs as above.
        try:
            changes = client.get_changes(url=sg_url,
                                         db=sg_db,
                                         since=0,
                                         auth=seth_session)
            changes_ids = [
                change["id"] for change in changes["results"]
                if not change["id"].startswith("_user")
            ]
            log_info("length of Changes ids are {} and vbuckets {}".format(
                len(changes_ids), num_vbuckets))
            if len(changes_ids) == (num_vbuckets - 1):
                break
        except HTTPError:
            if changes.status_code == 503:
                log_info('server is still down')
            else:
                raise
        time.sleep(1)
        count += 1

    # Verify that seth 66 doc does not appear in changes as vbucket 66 got deleted
    vbucket_66_docids = [doc1["id"] for doc1 in seth_66_docs]
    for doc_66 in vbucket_66_docids:
        assert doc_66 not in changes_ids, "doc {} in vbucket 66 shows up in changes ".format(
            doc_66)