def test_longpoll_awaken_roles(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) admin_role = "admin_role" admin_channel = "admin_channel" admin_user_info = userinfo.UserInfo(name="admin", password="******", channels=[], roles=[admin_role]) adam_user_info = userinfo.UserInfo(name="adam", password="******", channels=[], roles=[]) traun_user_info = userinfo.UserInfo(name="traun", password="******", channels=[], roles=[]) andy_user_info = userinfo.UserInfo(name="andy", password="******", channels=[], roles=[]) sg_db = "db" client = MobileRestClient() # Create a role on sync_gateway client.create_role(url=sg_admin_url, db=sg_db, name=admin_role, channels=[admin_channel]) # Create users with no channels or roles admin_auth = client.create_user(url=sg_admin_url, db=sg_db, name=admin_user_info.name, password=admin_user_info.password, roles=[admin_role]) adam_auth = client.create_user(url=sg_admin_url, db=sg_db, name=adam_user_info.name, password=adam_user_info.password) traun_auth = client.create_user(url=sg_admin_url, db=sg_db, name=traun_user_info.name, password=traun_user_info.password) andy_auth = client.create_user(url=sg_admin_url, db=sg_db, name=andy_user_info.name, password=andy_user_info.password) ################################ # change feed wakes for role add ################################ # 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) # Add doc with channel associated with the admin role admin_doc = client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="admin_doc", auth=admin_auth, channels=[admin_channel]) client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, 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() adam_auth = client.update_user(url=sg_admin_url, db=sg_db, name=adam_user_info.name, password=adam_user_info.password, roles=[admin_role]) traun_auth = client.update_user(url=sg_admin_url, db=sg_db, name=traun_user_info.name, password=traun_user_info.password, roles=[admin_role]) andy_auth = client.update_user(url=sg_admin_url, db=sg_db, name=andy_user_info.name, password=andy_user_info.password, roles=[admin_role]) adam_changes = adam_changes_task.result() assert 1 <= len(adam_changes["results"]) <= 2 assert adam_changes["results"][0]["id"] == "admin_doc_0" or adam_changes["results"][0]["id"] == "_user/adam" traun_changes = traun_changes_task.result() assert 1 <= len(traun_changes["results"]) <= 2 assert traun_changes["results"][0]["id"] == "admin_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"] == "admin_doc_0" or andy_changes["results"][0]["id"] == "_user/andy" # Check that the user docs all show up in changes feed 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) # Check that the admin doc made it to all the changes feeds client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, auth=adam_auth) client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, auth=traun_auth) client.verify_docs_in_changes(url=sg_url, db=sg_db, expected_docs=admin_doc, auth=andy_auth) # At this point, each user should have a changes feed that is caught up for the next section ########################################### # change feed wakes for channel add to role ########################################### abc_channel = "ABC" abc_pusher_info = userinfo.UserInfo(name="abc_pusher", password="******", channels=[abc_channel], roles=[]) abc_pusher_auth = client.create_user(url=sg_admin_url, db=sg_db, name=abc_pusher_info.name, password=abc_pusher_info.password, channels=abc_pusher_info.channels) # Add doc with ABC channel client.add_docs(url=sg_url, db=sg_db, number=1, id_prefix="abc_doc", auth=abc_pusher_auth, channels=[abc_channel]) # 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() # Update admin role to include ABC channel # Since adam, traun, and andy are assigned to that role, they should wake up and get the 'abc_pusher_0' doc client.update_role(url=sg_admin_url, db=sg_db, name=admin_role, channels=[admin_channel, abc_channel]) adam_changes = adam_changes_task.result() assert len(adam_changes["results"]) == 1 assert adam_changes["results"][0]["id"] == "abc_doc_0" traun_changes = traun_changes_task.result() assert len(traun_changes["results"]) == 1 assert traun_changes["results"][0]["id"] == "abc_doc_0" andy_changes = adam_changes_task.result() assert len(andy_changes["results"]) == 1 assert andy_changes["results"][0]["id"] == "abc_doc_0"
def test_remove_add_channels_to_doc(params_from_base_test_setup, sg_conf_name): 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) cluster = Cluster(cluster_config) cluster.reset(sg_conf) client = MobileRestClient() admin_user_info = userinfo.UserInfo("admin", "pass", channels=["A", "B"], roles=[]) a_user_info = userinfo.UserInfo("a_user", "pass", channels=["A"], roles=[]) admin_user_auth = 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, ) a_user_auth = client.create_user( url=sg_admin_url, db=sg_db, name=a_user_info.name, password=a_user_info.password, channels=a_user_info.channels ) a_docs = client.add_docs( url=sg_url, db=sg_db, number=50, id_prefix="a_doc", auth=admin_user_auth, channels=admin_user_info.channels ) # Build dictionay of a_docs a_docs_id_rev = {doc["id"]: doc["rev"] for doc in a_docs} assert len(a_docs_id_rev) == 50 # Wait for all docs to show up in changes client.verify_doc_id_in_changes(sg_url, sg_db, expected_doc_id="_user/a_user", auth=a_user_auth) client.verify_docs_in_changes(sg_url, sg_db, expected_docs=a_docs, auth=a_user_auth) # Get changes for 'a_user' a_user_changes = client.get_changes(url=sg_url, db=sg_db, since=0, auth=a_user_auth, feed="normal") # 'a_user' should get 50 'a_doc_*' doc and 1 '_user/a_user' doc assert len(a_user_changes["results"]) == 51 ########################### # Remove Channels from doc ########################### # Copy a_docs_id_rev to dictionary to scratch off values remove_docs_scratch_off = a_docs_id_rev.copy() assert len(remove_docs_scratch_off) == 50 # Use admin user to update the docs to remove 'A' from the channels property on the doc and add 'B' client.update_docs(url=sg_url, db=sg_db, docs=a_docs, number_updates=1, auth=admin_user_auth, channels=["B"]) # Longpoll loop requires due to the delay that changes take to permeate to the client changes_timeout = 10 start = time.time() last_seq = a_user_changes["last_seq"] while True: # If take longer than 10 seconds, fail the test if time.time() - start > changes_timeout: raise keywords.exceptions.TimeoutException("Could not find all expected docs in changs feed") # We found everything, exit loop! if remove_docs_scratch_off == {}: log_info("All expected docs found to be removed") break # Get changes for 'a_user' from last_seq a_user_changes = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=a_user_auth, timeout=10) assert len(a_user_changes["results"]) > 0 # Loop over changes found and perform the following # 1. Check that the docs is flagged with 'removed' # 2. Cross off the doc fromt the the 'remove_docs_scratch_off' for change in a_user_changes["results"]: assert change["removed"] == ["A"] assert change["changes"][0]["rev"].startswith("2-") # This will blow up if any change is not found in that dictionary del remove_docs_scratch_off[change["id"]] # Update last_seq last_seq = a_user_changes["last_seq"] # Issue changes request from 'last_seq' and verify that the changes are up to date and returns no results a_user_changes = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=a_user_auth, feed="normal") assert len(a_user_changes["results"]) == 0 ######################### # Add Channels to doc ######################### # Copy the a_docs_id_rev dictionary for scratch ovee add_docs_scratch_off = a_docs_id_rev.copy() assert len(add_docs_scratch_off) == 50 # Use admin user to update the docs to add ['A'] back to document channels client.update_docs(url=sg_url, db=sg_db, docs=a_docs, number_updates=1, auth=admin_user_auth, channels=["A"]) # Longpoll loop requires due to the delay that changes take to permeate to the client changes_timeout = 10 start = time.time() last_seq = a_user_changes["last_seq"] while True: # If take longer than 10 seconds, fail the test if time.time() - start > changes_timeout: raise keywords.exceptions.TimeoutException("Could not find all expected docs in changs feed") # We found everything, exit loop! if add_docs_scratch_off == {}: log_info("All expected docs found to be removed") break # Get changes for 'a_user' from last_seq a_user_changes = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=a_user_auth, timeout=10) assert len(a_user_changes["results"]) > 0 # Loop over changes found and perform the following # 1. Check that the docs has a 3rd gen rev prefix # 2. Cross off the doc fromt the the 'add_docs_scratch_off' for change in a_user_changes["results"]: assert change["changes"][0]["rev"].startswith("3-") # This will blow up if any change is not found in that dictionary del add_docs_scratch_off[change["id"]] # Update last_seq last_seq = a_user_changes["last_seq"] # Issue changes request from 'last_seq' and verify that the changes are up to date and returns no results a_user_changes = client.get_changes(url=sg_url, db=sg_db, since=last_seq, auth=a_user_auth, feed="normal") assert len(a_user_changes["results"]) == 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-")