Skip to content

Commit

Permalink
Fix race when assigning roles in TestFest test (#129)
Browse files Browse the repository at this point in the history
* Assigned user roles using REST API instead of using sync function in the list collection as the user role is required when adding tasks in the tasks collection as well.

* Update todo's sync function.

* Changed syncgateway add_user to require only db_name and name so this function can be used for updating the user.

* Removed create_role  from CouchbaseCloud as the test should use the functions in SyncGateway class directly.
  • Loading branch information
pasin authored Jan 21, 2025
1 parent bb9bc1e commit 235b1ed
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 36 deletions.
5 changes: 0 additions & 5 deletions client/src/cbltest/api/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ def _check_all_indexes_removed(self, bucket: str) -> None:
def _wait_for_all_indexed_removed(self, bucket: str) -> None:
_try_n_times(10, 2, True, self._check_all_indexes_removed, type(None), bucket)

async def create_role(
self, db_name: str, role: str, collection_access: dict
) -> None:
await self.__sync_gateway.add_role(db_name, role, collection_access)

async def configure_dataset(
self,
dataset_path: Path,
Expand Down
17 changes: 11 additions & 6 deletions client/src/cbltest/api/syncgateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,29 +517,34 @@ async def add_user(
self,
db_name: str,
name: str,
password: str,
collection_access: dict,
password: Optional[str] = None,
collection_access: Optional[dict] = None,
admin_roles: Optional[List[str]] = None,
) -> None:
"""
Adds the specified user to a Sync Gateway database with the specified channel access
Adds or updates the specified user to a Sync Gateway database with the specified channel access
:param db_name: The name of the Database to add the user to
:param name: The username to add
:param password: The password for the user that will be added
:param collection_access: The collections that the user will have access to. This needs to
be formatted in the way Sync Gateway expects it, so if you are unsure use
:func:`drop_bucket()<cbltest.api.syncgateway.SyncGateway.create_collection_access_dict>`
:param admin_roles: The admin roles
"""
with self.__tracer.start_as_current_span(
"add_user", attributes={"cbl.user.name": name}
):
body = {
body: Dict[str, Any] = {
"name": name,
"password": password,
"collection_access": collection_access,
}

if password is not None:
body["password"] = password

if collection_access is not None:
body["collection_access"] = collection_access

if admin_roles is not None:
body["admin_roles"] = admin_roles

Expand Down
2 changes: 1 addition & 1 deletion dataset/sg/todo-sg-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"_default": {
"collections": {
"lists": {
"sync": "function foo(doc,oldDoc,meta){var owner=doc._deleted?oldDoc.owner:doc.owner;requireUser(owner);var listChannel='lists.'+owner+'.'+doc._id;var contributorRole='role:'+listChannel+'.contributor';role(owner,contributorRole);access(contributorRole,listChannel);channel(listChannel)}"
"sync": "function foo(doc,oldDoc,meta){var owner=doc._deleted?oldDoc.owner:doc.owner;requireUser(owner);var listChannel='lists.'+owner+'.'+doc._id;var contributorRoleName=listChannel+'.contributor';var contributorRole='role:'+contributorRoleName;requireRole(contributorRoleName);access(contributorRole,listChannel);channel(listChannel)}"
},
"tasks": {
"sync": "function foo(doc,oldDoc,meta){var listId=doc._deleted?oldDoc.taskList.id:doc.taskList.id;var listOwner=doc._deleted?oldDoc.taskList.owner:doc.taskList.owner;var listChannel='lists.'+listOwner+'.'+listId;var contributorRoleName=listChannel+'.contributor';var contributorRole='role:'+contributorRoleName;requireRole(contributorRoleName);var tasksChannel=listChannel+'.tasks';access(contributorRole,tasksChannel);channel(tasksChannel)}"
Expand Down
67 changes: 43 additions & 24 deletions tests/test_fest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from typing import Dict, List, Tuple

import pytest
from cbltest import CBLPyTest
Expand All @@ -17,28 +17,33 @@

class TestFest(CBLTestClass):
async def setup_test_fest_cloud(
self,
cblpytest: CBLPyTest,
dataset_path: Path,
roles: Optional[List[str]] = None,
self, cblpytest: CBLPyTest, dataset_path: Path, roles: Dict[str, List[str]]
) -> CouchbaseCloud:
self.mark_test_step("Reset SG and load todo dataset")
cloud = CouchbaseCloud(
cblpytest.sync_gateways[0], cblpytest.couchbase_servers[0]
)
await cloud.configure_dataset(dataset_path, "todo")

sg_roles = roles if roles is not None else ["lists.user1.db1-list1.contributor"]
self.mark_test_step(f"Create SG roles: {', '.join(sg_roles)}")
collection_access: Dict[str, Any] = {
"_default": {
"lists": {"admin_channels": []},
"tasks": {"admin_channels": []},
"users": {"admin_channels": []},
}
}
for role in sg_roles:
await cloud.create_role("todo", role, collection_access)
for user, user_roles in roles.items():
self.mark_test_step(
f"Assign roles '{', '.join(user_roles)}' to the user '{user}'"
)
for role in user_roles:
await cblpytest.sync_gateways[0].add_role(
"todo",
role,
{
"_default": {
"lists": {"admin_channels": []},
"tasks": {"admin_channels": []},
"users": {"admin_channels": []},
}
},
)
await cblpytest.sync_gateways[0].add_user(
"todo", user, admin_roles=user_roles
)

return cloud

Expand Down Expand Up @@ -111,7 +116,12 @@ async def test_create_tasks(self, cblpytest: CBLPyTest, dataset_path: Path) -> N
await self.setup_test_fest_cloud(
cblpytest,
dataset_path,
["lists.user1.db1-list1.contributor", "lists.user1.db2-list1.contributor"],
{
"user1": [
"lists.user1.db1-list1.contributor",
"lists.user1.db2-list1.contributor",
]
},
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(cblpytest, (db1, db2))
Expand Down Expand Up @@ -252,7 +262,9 @@ async def test_create_tasks(self, cblpytest: CBLPyTest, dataset_path: Path) -> N

@pytest.mark.asyncio(loop_scope="session")
async def test_update_task(self, cblpytest: CBLPyTest, dataset_path: Path) -> None:
await self.setup_test_fest_cloud(cblpytest, dataset_path)
await self.setup_test_fest_cloud(
cblpytest, dataset_path, {"user1": ["lists.user1.db1-list1.contributor"]}
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(cblpytest, (db1, db2))

Expand Down Expand Up @@ -376,7 +388,9 @@ async def test_update_task(self, cblpytest: CBLPyTest, dataset_path: Path) -> No

@pytest.mark.asyncio(loop_scope="session")
async def test_delete_task(self, cblpytest: CBLPyTest, dataset_path: Path) -> None:
await self.setup_test_fest_cloud(cblpytest, dataset_path)
await self.setup_test_fest_cloud(
cblpytest, dataset_path, {"user1": ["lists.user1.db1-list1.contributor"]}
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(cblpytest, (db1, db2))

Expand Down Expand Up @@ -477,7 +491,9 @@ async def test_delete_task(self, cblpytest: CBLPyTest, dataset_path: Path) -> No

@pytest.mark.asyncio(loop_scope="session")
async def test_delete_list(self, cblpytest: CBLPyTest, dataset_path: Path) -> None:
await self.setup_test_fest_cloud(cblpytest, dataset_path)
await self.setup_test_fest_cloud(
cblpytest, dataset_path, {"user1": ["lists.user1.db1-list1.contributor"]}
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(cblpytest, (db1, db2))

Expand Down Expand Up @@ -618,7 +634,10 @@ async def _test_create_tasks_two_users(
await self.setup_test_fest_cloud(
cblpytest,
dataset_path,
["lists.user1.db1-list1.contributor", "lists.user2.db2-list1.contributor"],
{
"user1": ["lists.user1.db1-list1.contributor"],
"user2": ["lists.user2.db2-list1.contributor"],
},
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(
Expand Down Expand Up @@ -746,7 +765,7 @@ async def _test_create_tasks_two_users(
@pytest.mark.asyncio(loop_scope="session")
async def test_share_list(self, cblpytest: CBLPyTest, dataset_path: Path) -> None:
await self.setup_test_fest_cloud(
cblpytest, dataset_path, ["lists.user1.db1-list1.contributor"]
cblpytest, dataset_path, {"user1": ["lists.user1.db1-list1.contributor"]}
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(
Expand Down Expand Up @@ -905,7 +924,7 @@ async def test_update_shared_tasks(
self, cblpytest: CBLPyTest, dataset_path: Path
) -> None:
await self.setup_test_fest_cloud(
cblpytest, dataset_path, ["lists.user1.db1-list1.contributor"]
cblpytest, dataset_path, {"user1": ["lists.user1.db1-list1.contributor"]}
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(
Expand Down Expand Up @@ -1111,7 +1130,7 @@ async def test_update_shared_tasks(
@pytest.mark.asyncio(loop_scope="session")
async def test_unshare_list(self, cblpytest: CBLPyTest, dataset_path: Path) -> None:
await self.setup_test_fest_cloud(
cblpytest, dataset_path, ["lists.user1.db1-list1.contributor"]
cblpytest, dataset_path, {"user1": ["lists.user1.db1-list1.contributor"]}
)
db1, db2 = await self.setup_test_fest_dbs(cblpytest)
repl1, repl2 = await self.setup_test_fest_repls(
Expand Down

0 comments on commit 235b1ed

Please sign in to comment.