diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4549f5c7a..cb66ea960 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,7 @@ jobs: # if changing the below change the run-integration-tests versions and the check-deploy versions # Make sure that we are running the integration tests on the first and last versions of the matrix - python: ['3.9', '3.10', '3.11', '3.12', '3.13'] + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] runs-on: ${{ matrix.os }} @@ -109,7 +109,7 @@ jobs: pytest -sv --cov-append --cov=. --cov-report xml tests/unit - name: Check for Secret availability id: secret-check - if: ${{ contains(fromJSON('["3.9"]'), matrix.python) || contains(fromJSON('["3.13"]'), matrix.python) }} + if: ${{ contains(fromJSON('["3.10"]'), matrix.python) || contains(fromJSON('["3.14"]'), matrix.python) }} # perform secret check & put boolean result as an output shell: bash run: | @@ -126,7 +126,7 @@ jobs: fi - name: Download Failed Tests from Previous Attempt - if: ${{ github.run_attempt > 1 && (contains(fromJSON('["3.9"]'), matrix.python) || contains(fromJSON('["3.13"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true'}} + if: ${{ github.run_attempt > 1 && (contains(fromJSON('["3.10"]'), matrix.python) || contains(fromJSON('["3.14"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true'}} uses: actions/download-artifact@v4 with: name: failed-tests-${{ matrix.os }}-${{ matrix.python }} @@ -142,25 +142,25 @@ jobs: shell: bash # keep versions consistent with the first and last from the strategy matrix - if: ${{ (contains(fromJSON('["3.9"]'), matrix.python) || contains(fromJSON('["3.13"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true'}} + if: ${{ (contains(fromJSON('["3.10"]'), matrix.python) || contains(fromJSON('["3.14"]'), matrix.python)) && steps.secret-check.outputs.secrets_available == 'true'}} run: | # Set SYNAPSE_PROFILE based on OS and Python version if [ "${{ startsWith(matrix.os, 'ubuntu') }}" == "true" ]; then - if [ "${{ matrix.python }}" == "3.9" ]; then + if [ "${{ matrix.python }}" == "3.10" ]; then export SYNAPSE_PROFILE="TestUbuntuMinimumPython" - elif [ "${{ matrix.python }}" == "3.13" ]; then + elif [ "${{ matrix.python }}" == "3.14" ]; then export SYNAPSE_PROFILE="TestUbuntuMaximumPython" fi elif [ "${{ startsWith(matrix.os, 'windows') }}" == "true" ]; then - if [ "${{ matrix.python }}" == "3.9" ]; then + if [ "${{ matrix.python }}" == "3.10" ]; then export SYNAPSE_PROFILE="TestWindowsMinimumPython" - elif [ "${{ matrix.python }}" == "3.13" ]; then + elif [ "${{ matrix.python }}" == "3.14" ]; then export SYNAPSE_PROFILE="TestWindowsMaximumPython" fi elif [ "${{ startsWith(matrix.os, 'macos') }}" == "true" ]; then - if [ "${{ matrix.python }}" == "3.9" ]; then + if [ "${{ matrix.python }}" == "3.10" ]; then export SYNAPSE_PROFILE="TestMacosMinimumPython" - elif [ "${{ matrix.python }}" == "3.13" ]; then + elif [ "${{ matrix.python }}" == "3.14" ]; then export SYNAPSE_PROFILE="TestMacosMaximumPython" fi fi @@ -202,16 +202,6 @@ jobs: # Setup ignore patterns based on Python version IGNORE_FLAGS="--ignore=tests/integration/synapseclient/test_command_line_client.py" - if [ "${{ matrix.python }}" == "3.9" ]; then - # For min Python version, ignore async tests - IGNORE_FLAGS="$IGNORE_FLAGS --ignore=tests/integration/synapseclient/models/async/" - echo "Running integration tests for Min Python version (3.9) - ignoring async tests" - elif [ "${{ matrix.python }}" == "3.13" ]; then - # For max Python version, ignore synchronous tests - IGNORE_FLAGS="$IGNORE_FLAGS --ignore=tests/integration/synapseclient/models/synchronous/" - echo "Running integration tests for Max Python version (3.13) - ignoring synchronous tests" - fi - # Check if we should run only failed tests from previous attempt if [[ -f failed_tests.txt && -s failed_tests.txt ]]; then echo "::notice::Retry attempt ${{ github.run_attempt }} detected - running only previously failed tests" @@ -362,7 +352,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.10 - name: set-release-env shell: bash @@ -459,7 +449,6 @@ jobs: # asset_path: dist/${{ steps.build-package.outputs.bdist-package-name }} # asset_content_type: application/zip - # re-download the built package to the appropriate pypi server. # we upload prereleases to test.pypi.org and releases to pypi.org. deploy: @@ -500,7 +489,7 @@ jobs: os: [ubuntu-24.04, macos-13, windows-2022] # python versions should be consistent with the strategy matrix and the runs-integration-tests versions - python: ['3.9', '3.10', '3.11', '3.12', '3.13'] + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] runs-on: ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index 247cd7d73..fa4e7f520 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ virtualenvs examples_temp .DS_Store deploy.sh +.venv/ junk/ nose.cfg diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cf52875d2..7efe88d6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -93,7 +93,7 @@ copied to forks). #### Installing the Python Client in a virtual environment with pipenv Perform the following one-time steps to set up your local environment. -1. This package uses Python, if you have not already, please install [pyenv](https://github.com/pyenv/pyenv#installation) to manage your Python versions. Versions supported by this package are all versions >=3.9 and <=3.13. If you do not install `pyenv` make sure that Python and `pip` are installed correctly and have been added to your PATH by running `python3 --version` and `pip3 --version`. If your installation was successful, your terminal will return the versions of Python and `pip` that you installed. **Note**: If you have `pyenv` it will install a specific version of Python for you. +1. This package uses Python, if you have not already, please install [pyenv](https://github.com/pyenv/pyenv#installation) to manage your Python versions. Versions supported by this package are all versions >=3.10 and <=3.14. If you do not install `pyenv` make sure that Python and `pip` are installed correctly and have been added to your PATH by running `python3 --version` and `pip3 --version`. If your installation was successful, your terminal will return the versions of Python and `pip` that you installed. **Note**: If you have `pyenv` it will install a specific version of Python for you. 2. Install `pipenv` by running `pip install pipenv`. - If you already have `pipenv` installed, ensure that the version is >=2023.9.8 to avoid compatibility issues. @@ -223,6 +223,64 @@ When integration tests are ran in the Github CI/CD pipeline it will upload the t #### Integration testing for external collaborators As an external collaborator you will not have access to a development account and environment to run the integration tests against. Either request that a Sage Bionetworks staff member run your integration tests via a pull request, or, contact us via the [Service Desk](https://sagebionetworks.jira.com/servicedesk/customer/portal/9) to requisition a development account for integration testing only. +### Managing Python version changes + +When adding support for a new Python version or dropping support for an old version, several files across the codebase and CI/CD pipelines must be updated to ensure consistency and proper testing coverage. + +#### Adding a new Python version + +When adding support for a new Python version (e.g., adding Python 3.15), update the following: + +**Code configuration files:** +1. **`setup.cfg`**: + - Add the new version to the `classifiers` list under `[metadata]` (e.g., `Programming Language :: Python :: 3.15`) + - Update the `python_requires` constraint under `[options]` to include the new version (e.g., `>=3.10, <3.16`) + +2. **`pyproject.toml`**: + - Update the `target-version` list in the `[tool.black]` section to include the new version if needed + +3. **`Dockerfile`**: + - Update the base image to use the new Python version (e.g., `FROM python:3.15-slim`) + +**CI/CD configuration files:** +1. **`.github/workflows/build.yml`**: + - Add the new version to the `python` matrix under the `test` job strategy + - Ensure the new version is included in integration test runs (typically the latest version should be tested) + - Update any Python version comments or documentation within the workflow + +**Testing:** +- Run the full test suite (both unit and integration tests) on the new Python version locally before submitting a PR +- Verify that all CI/CD pipelines pass with the new version included + +#### Dropping an old Python version + +When dropping support for an old Python version (e.g., removing Python 3.10), update the following: + +**Code configuration files:** +1. **`setup.cfg`**: + - Remove the old version from the `classifiers` list under `[metadata]` + - Update the `python_requires` constraint under `[options]` to reflect the new minimum version (e.g., `>=3.11, <3.15`) + +2. **`pyproject.toml`**: + - Update the `target-version` list in the `[tool.black]` section to remove the old version + +3. **`Dockerfile`**: + - Ensure the base image uses a supported Python version + +**CI/CD configuration files:** +1. **`.github/workflows/build.yml`**: + - Remove the old version from the `python` matrix under the `test` job strategy + - Update the cache key version (e.g., increment `v28` to `v29`) to invalidate old caches + +**Documentation:** +- Update the README.md and any getting started documentation to reflect the new supported Python version range +- Update CONTRIBUTING.md (this file) if it mentions specific Python versions in examples + +**Important considerations:** +- Python version changes should be coordinated with a release and clearly communicated in release notes +- Breaking compatibility with a Python version is a significant change and should typically coincide with a major or minor version bump +- Always test thoroughly on the minimum and maximum supported Python versions before release + ### Asynchronous methods [Asyncio](https://docs.python.org/3/library/asyncio.html) is the future of the Synapse Python Client. As such, the expectation is that all future methods that rely on async diff --git a/Dockerfile b/Dockerfile index 2517e28f0..b2a48a351 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9-slim +FROM python:3.14-slim RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections RUN apt-get update \ diff --git a/README.md b/README.md index 6c3c77588..8f5e37472 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ or by sending an email to [python-announce+subscribe@sagebase.org](mailto:python Installation ------------ -The Python Synapse client has been tested on versions 3.9, 3.10, 3.11, 3.12 and 3.13 on Mac OS X, Ubuntu Linux and Windows. +The Python Synapse client has been tested on versions 3.10, 3.11, 3.12, 3.13 and 3.14 on Mac OS X, Ubuntu Linux and Windows. -**Starting from Synapse Python client version 3.0, Synapse Python client requires Python >= 3.9** +**Starting from Synapse Python client version 3.0, Synapse Python client requires Python >= 3.10** ### Install using pip diff --git a/docs/explanations/asyncio_in_python_3_14.md b/docs/explanations/asyncio_in_python_3_14.md new file mode 100644 index 000000000..55100d266 --- /dev/null +++ b/docs/explanations/asyncio_in_python_3_14.md @@ -0,0 +1,130 @@ +# Asyncio and Python 3.14+ Changes + +## Overview + +Starting with Python 3.14, changes to the asyncio implementation affect how the Synapse Python Client works in Jupyter notebooks and other async contexts. This document explains what changed, why, and how it impacts your code. + +**TL;DR:** Python 3.14+ breaks the `nest_asyncio` workaround. In Jupyter notebooks, you must now use async methods with `await` (e.g., `await obj.get_async()` instead of `obj.get()`). Regular scripts are unaffected. + +## Background: How Jupyter Notebooks Run Code + +Jupyter notebooks run all code within an active asyncio event loop. This means: + +- **Async functions** must be called with `await my_async_function()` +- **Synchronous functions** can be called normally with `my_function()` +- **You cannot use** `asyncio.run()` because asyncio intentionally prevents nested event loops (to avoid threading and deadlock issues) + +## What Changed in Python 3.14 + +### Previous Behavior (Python 3.13 and earlier) + +The Synapse Python Client used a library called `nest_asyncio` to work around asyncio's nested event loop restriction. This allowed us to: + +1. Detect whether code was running in a notebook (with an active event loop) or a regular script +2. Automatically call async functions from synchronous wrapper functions +3. Provide a seamless synchronous API that worked in both environments + +**This meant you could write:** +```python +from synapseclient import Synapse +from synapseclient.models import Project + +syn = Synapse() +syn.login() + +# This worked in notebooks AND regular scripts +my_project = Project(name="My Project").get() +``` + +### New Behavior (Python 3.14+) + +Python 3.14 changed the asyncio implementation in ways that break `nest_asyncio`. The library can no longer safely monkey-patch asyncio. + +**Impact:** The automatic async-to-sync conversion no longer works in notebooks on Python 3.14+. + +> **Why the change?** Python 3.14 improved asyncio's safety to prevent deadlocks and race conditions. The `nest_asyncio` workaround bypassed these safety mechanisms and now causes failures in HTTP connection pooling. Rather than risk silent failures, the library raises clear errors when async methods should be used. + +## How This Affects Your Code + +### In Regular Python Scripts + +**No changes needed.** Synchronous methods continue to work as before: + +```python +from synapseclient import Synapse +from synapseclient.models import Project + +syn = Synapse() +syn.login() + +# This still works in regular scripts +my_project = Project(name="My Project").get() +``` + +### In Jupyter Notebooks (Python 3.14+) + +**You must use async methods directly** with `await`: + +```python +from synapseclient import Synapse +from synapseclient.models import Project + +syn = Synapse() +syn.login() + +# Use the async version with await +my_project = await Project(name="My Project").get_async() +``` + +### In Other Async Contexts + +If you're calling Synapse Python Client methods from within an async function, use the async methods: + +```python +import asyncio +from synapseclient import Synapse +from synapseclient.models import Project + +syn = Synapse() +syn.login() + +async def main(): + # Use async methods with await + my_project = await Project(name="My Project").get_async() + +asyncio.run(main()) +``` + +## Error Messages + +If you try to use synchronous methods in an async context on Python 3.14+, you'll see an error like: + +``` +RuntimeError: Python 3.14+ detected an active event loop, which prevents automatic async-to-sync conversion. +This is a limitation of asyncio in Python 3.14+. + +To resolve this, use the async method directly: + • Instead of: result = obj.method_name() + • Use: result = await obj.method_name_async() + +For Jupyter/IPython notebooks: You can use 'await' directly in cells. +For other async contexts: Ensure you're in an async function and use 'await'. +``` + +## Quick Reference + +| Environment | Python 3.13 and earlier | Python 3.14+ | +|-------------|------------------------|--------------| +| Regular Python script | `obj.method()` | `obj.method()` ✓ | +| Jupyter notebook | `obj.method()` | `await obj.method_async()` | +| Inside async function | `await obj.method_async()` | `await obj.method_async()` | + +## Finding Async Methods + +For most synchronous methods, there is a corresponding async version with `_async` suffix: + +- `get()` → `get_async()` +- `store()` → `store_async()` +- `delete()` → `delete_async()` + +Check the API documentation for the complete list of async methods available. diff --git a/docs/tutorials/installation.md b/docs/tutorials/installation.md index e1a401e58..ca4f2e932 100644 --- a/docs/tutorials/installation.md +++ b/docs/tutorials/installation.md @@ -21,7 +21,7 @@ The [synapseclient](https://pypi.python.org/pypi/synapseclient/) package is avai - conda: Please follow instructions [here](https://docs.conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html) to manage environments: ```bash -conda create -n synapseclient python=3.9 +conda create -n synapseclient python=3.14 conda activate synapseclient # Here are a few ways to install the client. Choose the one that fits your use-case @@ -43,8 +43,8 @@ to the `/usr/local/lib` directory. [See here](https://github.com/conda/conda/iss - pyenv: Use [virtualenv](https://virtualenv.pypa.io/en/latest/) to manage your python environment: ```bash -pyenv install -v 3.9.13 -pyenv global 3.9.13 +pyenv install -v 3.14.0 +pyenv global 3.14.0 python -m venv env source env/bin/activate diff --git a/mkdocs.yml b/mkdocs.yml index cca09f73a..fd0e5b679 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -143,6 +143,7 @@ nav: - Manifest TSV: explanations/manifest_tsv.md - Benchmarking: explanations/benchmarking.md - Structuring Your Project: explanations/structuring_your_project.md + - Asyncio Changes in Python 3.14: explanations/asyncio_in_python_3_14.md - News: - news.md - Contact Us: https://sagebionetworks.jira.com/servicedesk/customer/portal/9/group/16/create/206 diff --git a/pylintrc b/pylintrc index e56734f73..8e007b74c 100644 --- a/pylintrc +++ b/pylintrc @@ -93,7 +93,7 @@ prefer-stubs=no # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. -py-version=3.9 +py-version=3.10 # Discover python modules and packages in the file system subtree. recursive=no diff --git a/setup.cfg b/setup.cfg index 8f6074311..65ae4ae49 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,11 +22,11 @@ platforms = any classifiers = Development Status :: 5 - Production/Stable Programming Language :: Python - Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.13 + Programming Language :: Python :: 3.14 Operating System :: MacOS Operating System :: Microsoft :: Windows Operating System :: Unix @@ -42,7 +42,7 @@ classifiers = zip_safe = False include_package_data = True packages = find: -python_requires = >=3.9, <3.14 +python_requires = >=3.10, <3.15 install_requires = # "requests>=2.22.0,<2.30.0; python_version<'3.10'", requests>=2.22.0,<3.0 @@ -129,6 +129,14 @@ docs = mkdocs-open-in-new-tab~=1.0.3 markdown-include~=0.8.1 +all = + %(dev)s + %(pandas)s + %(curator)s + %(pysftp)s + %(boto3)s + %(docs)s + [options.entry_points] console_scripts = diff --git a/synapseclient/api/entity_services.py b/synapseclient/api/entity_services.py index 759be644a..4afa5084e 100644 --- a/synapseclient/api/entity_services.py +++ b/synapseclient/api/entity_services.py @@ -6,8 +6,6 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Optional, Union -from async_lru import alru_cache - from synapseclient.api.api_client import rest_post_paginated_async from synapseclient.core.exceptions import SynapseHTTPError from synapseclient.core.utils import get_synid_and_version @@ -180,7 +178,6 @@ async def get_entity( ) -@alru_cache(ttl=60) async def get_upload_destination( entity_id: str, *, synapse_client: Optional["Synapse"] = None ) -> Dict[str, Union[str, int]]: diff --git a/synapseclient/client.py b/synapseclient/client.py index 584562507..df562c16c 100644 --- a/synapseclient/client.py +++ b/synapseclient/client.py @@ -2315,6 +2315,106 @@ def store( test_entity = File('/path/to/data/file.xyz', description='Fancy new data', parent=project) test_entity = syn.store(test_entity, activity=activity) + """ + return wrap_async_to_sync( + coroutine=self.store_async( + obj, + createOrUpdate=createOrUpdate, + forceVersion=forceVersion, + versionLabel=versionLabel, + isRestricted=isRestricted, + activity=activity, + used=used, + executed=executed, + activityName=activityName, + activityDescription=activityDescription, + set_annotations=set_annotations, + ) + ) + + async def store_async( + self, + obj, + *, + createOrUpdate=True, + forceVersion=True, + versionLabel=None, + isRestricted=False, + activity=None, + used=None, + executed=None, + activityName=None, + activityDescription=None, + set_annotations=True, + ): + """ + Creates a new Entity or updates an existing Entity, uploading any files in the process. + + Arguments: + obj: A Synapse Entity, Evaluation, or Wiki + used: The Entity, Synapse ID, or URL used to create the object (can also be a list of these) + executed: The Entity, Synapse ID, or URL representing code executed to create the object + (can also be a list of these) + activity: Activity object specifying the user's provenance. + activityName: Activity name to be used in conjunction with *used* and *executed*. + activityDescription: Activity description to be used in conjunction with *used* and *executed*. + createOrUpdate: Indicates whether the method should automatically perform an update if the 'obj' + conflicts with an existing Synapse object. + forceVersion: Indicates whether the method should increment the version of the object even if nothing + has changed. + versionLabel: Arbitrary string used to label the version. + isRestricted: If set to true, an email will be sent to the Synapse access control team to start the + process of adding terms-of-use or review board approval for this entity. + You will be contacted with regards to the specific data being restricted and the + requirements of access. + set_annotations: If True, set the annotations on the entity. If False, do not set the annotations. + + Returns: + A Synapse Entity, Evaluation, or Wiki + + Example: Using this function + Creating a new Project: + + ```python + import asyncio + from synapseclient import Project, Synapse + + syn = Synapse() + syn.login() + + async def main(): + project = Project('My uniquely named project') + project = await syn.store_async(project) + + asyncio.run(main()) + ``` + + Adding files with [provenance (aka: Activity)][synapseclient.Activity]: + + A synapse entity *syn1906480* contains data and an entity *syn1917825* contains code + + ```python + import asyncio + from synapseclient import File, Activity, Synapse + + syn = Synapse() + syn.login() + + + activity = Activity( + 'Fancy Processing', + description='No seriously, really fancy processing', + used=['syn1906480', 'http://data_r_us.com/fancy/data.txt'], + executed='syn1917825') + + test_entity = File('/path/to/data/file.xyz', description='Fancy new data', parent=project) + + async def main(): + test_entity = await syn.store_async(test_entity, activity=activity) + + asyncio.run(main()) + ``` + """ trace.get_current_span().set_attributes({"thread.id": threading.get_ident()}) # SYNPY-1031: activity must be Activity object or code will fail later @@ -2328,13 +2428,15 @@ def store( # _synapse_store hook # for objects that know how to store themselves - if hasattr(obj, "_synapse_store"): + if hasattr(obj, "_synapse_store_async"): + return await obj._synapse_store_async(self) + elif hasattr(obj, "_synapse_store"): return obj._synapse_store(self) # Handle all non-Entity objects if not (isinstance(obj, Entity) or type(obj) == dict): if isinstance(obj, Wiki): - return self._storeWiki(obj, createOrUpdate) + return await self._storeWiki_async(obj, createOrUpdate) if "id" in obj: # If ID is present, update trace.get_current_span().set_attributes({"synapse.id": obj["id"]}) @@ -2446,18 +2548,16 @@ def store( "Entities of type File must have a parentId." ) - fileHandle = wrap_async_to_sync( - upload_file_handle_async( - self, - parent_id_for_upload, - local_state["path"] - if (synapseStore or local_state_fh.get("externalURL") is None) - else local_state_fh.get("externalURL"), - synapse_store=synapseStore, - md5=local_file_md5_hex or local_state_fh.get("contentMd5"), - file_size=local_state_fh.get("contentSize"), - mimetype=local_state_fh.get("contentType"), - ) + fileHandle = await upload_file_handle_async( + self, + parent_id_for_upload, + local_state["path"] + if (synapseStore or local_state_fh.get("externalURL") is None) + else local_state_fh.get("externalURL"), + synapse_store=synapseStore, + md5=local_file_md5_hex or local_state_fh.get("contentMd5"), + file_size=local_state_fh.get("contentSize"), + mimetype=local_state_fh.get("contentType"), ) properties["dataFileHandleId"] = fileHandle["id"] local_state["_file_handle"] = fileHandle @@ -5622,13 +5722,38 @@ def create_s3_storage_location( Returns: A 3-tuple of the synapse Folder, a the storage location setting, and the project setting dictionaries. """ + return wrap_async_to_sync( + self.create_s3_storage_location_async( + parent=parent, + folder_name=folder_name, + folder=folder, + bucket_name=bucket_name, + base_key=base_key, + sts_enabled=sts_enabled, + ) + ) + + # TODO: Deprecate method in https://sagebionetworks.jira.com/browse/SYNPY-1441 + async def create_s3_storage_location_async( + self, + *, + parent=None, + folder_name=None, + folder=None, + bucket_name=None, + base_key=None, + sts_enabled=False, + ) -> Tuple[Folder, Dict[str, str], Dict[str, str]]: + """ + async version of create_s3_storage_location + """ if folder_name and parent: if folder: raise ValueError( "folder and folder_name are mutually exclusive, only one should be passed" ) - folder = self.store(Folder(name=folder_name, parent=parent)) + folder = await self.store_async(Folder(name=folder_name, parent=parent)) elif not folder: raise ValueError("either folder or folder_name should be required") @@ -6768,6 +6893,22 @@ def getWiki(self, owner, subpageId=None, version=None): """ Get a [synapseclient.wiki.Wiki][] object from Synapse. Uses wiki2 API which supports versioning. + Arguments: + owner: The entity to which the Wiki is attached + subpageId: The id of the specific sub-page or None to get the root Wiki page + version: The version of the page to retrieve or None to retrieve the latest + + Returns: + A [synapseclient.wiki.Wiki][] object + """ + return wrap_async_to_sync( + self.getWiki_async(owner, subpageId=subpageId, version=version) + ) + + async def getWiki_async(self, owner, subpageId=None, version=None): + """ + Get a [synapseclient.wiki.Wiki][] object from Synapse. Uses wiki2 API which supports versioning. + Arguments: owner: The entity to which the Wiki is attached subpageId: The id of the specific sub-page or None to get the root Wiki page @@ -6791,16 +6932,14 @@ def getWiki(self, owner, subpageId=None, version=None): cache_dir = self.cache.get_cache_dir(wiki.markdownFileHandleId) if not os.path.exists(cache_dir): os.makedirs(cache_dir) - path = wrap_async_to_sync( - coroutine=download_by_file_handle( - file_handle_id=wiki["markdownFileHandleId"], - synapse_id=wiki["id"], - entity_type="WikiMarkdown", - destination=os.path.join( - cache_dir, str(wiki.markdownFileHandleId) + ".md" - ), - synapse_client=self, - ) + path = await download_by_file_handle( + file_handle_id=wiki["markdownFileHandleId"], + synapse_id=wiki["id"], + entity_type="WikiMarkdown", + destination=os.path.join( + cache_dir, str(wiki.markdownFileHandleId) + ".md" + ), + synapse_client=self, ) try: import gzip @@ -6836,6 +6975,21 @@ def _storeWiki(self, wiki: Wiki, createOrUpdate: bool) -> Wiki: """ Stores or updates the given Wiki. + Arguments: + wiki: A Wiki object + createOrUpdate: Indicates whether the method should automatically perform an update if the 'obj' + conflicts with an existing Synapse object. + + Returns: + An updated Wiki object + """ + return wrap_async_to_sync(self._storeWiki_async(wiki, createOrUpdate)) + + # TODO: Deprecate method in https://sagebionetworks.jira.com/browse/SYNPY-1351 + async def _storeWiki_async(self, wiki: Wiki, createOrUpdate: bool) -> Wiki: + """ + Stores or updates the given Wiki. + Arguments: wiki: A Wiki object createOrUpdate: Indicates whether the method should automatically perform an update if the 'obj' @@ -6851,7 +7005,7 @@ def _storeWiki(self, wiki: Wiki, createOrUpdate: bool) -> Wiki: # Convert all attachments into file handles if wiki.get("attachments") is not None: for attachment in wiki["attachments"]: - fileHandle = wrap_async_to_sync(upload_synapse_s3(self, attachment)) + fileHandle = await upload_synapse_s3(self, attachment) wiki["attachmentFileHandleIds"].append(fileHandle["id"]) del wiki["attachments"] @@ -6876,7 +7030,7 @@ def _storeWiki(self, wiki: Wiki, createOrUpdate: bool) -> Wiki: ) or err.response.status_code == 409 ): - existing_wiki = self.getWiki(wiki.ownerId) + existing_wiki = await self.getWiki_async(wiki.ownerId) # overwrite everything except for the etag (this will keep unmodified fields in the existing wiki) etag = existing_wiki["etag"] @@ -7710,12 +7864,6 @@ async def async_query_example(): uri = "/entity/{id}/table/query/nextPage/async".format(id=tableId) return self._waitForAsync(uri=uri, request=nextPageToken) - @deprecated( - version="4.9.0", - reason="To be removed in 5.0.0. " - "Use the `_chunk_and_upload_csv` method on the `from synapseclient.models import Table` class instead. " - "Check the docstring for the replacement function example.", - ) def _uploadCsv( self, filepath: str, @@ -7798,9 +7946,44 @@ async def upload_csv_with_chunk_method(table_id, path): asyncio.run(upload_csv_with_chunk_method(table_id=TABLE_ID, path=PATH)) ``` """ + return wrap_async_to_sync( + self._uploadCsv_async( + filepath, + schema, + updateEtag, + quoteCharacter, + escapeCharacter, + lineEnd, + separator, + header, + linesToSkip, + ) + ) - fileHandleId = wrap_async_to_sync( - multipart_upload_file_async(self, filepath, content_type="text/csv") + @deprecated( + version="4.9.0", + reason="To be removed in 5.0.0. " + "Use the `_chunk_and_upload_csv` method on the `from synapseclient.models import Table` class instead. " + "Check the docstring for the replacement function example.", + ) + async def _uploadCsv_async( + self, + filepath: str, + schema: Union[Entity, str], + updateEtag: str = None, + quoteCharacter: str = '"', + escapeCharacter: str = "\\", + lineEnd: str = os.linesep, + separator: str = ",", + header: bool = True, + linesToSkip: int = 0, + ) -> dict: + """ + Async version of [_uploadCsv][] + """ + + fileHandleId = await multipart_upload_file_async( + self, filepath, content_type="text/csv" ) uploadRequest = { @@ -8242,7 +8425,15 @@ def downloadTableColumns(self, table, columns, downloadLocation=None, **kwargs): data[file_handle_id] = f.read() """ + return wrap_async_to_sync( + self.downloadTableColumns_async(table, columns, downloadLocation, **kwargs) + ) + # TODO: Deprecate method in https://sagebionetworks.jira.com/browse/SYNPY-1632 + async def downloadTableColumns_async( + self, table, columns, downloadLocation=None, **kwargs + ): + """Async version of downloadTableColumns.""" RETRIABLE_FAILURE_CODES = ["EXCEEDS_SIZE_LIMIT"] MAX_DOWNLOAD_TRIES = 100 max_files_per_request = kwargs.get("max_files_per_request", 2500) @@ -8303,13 +8494,11 @@ def downloadTableColumns(self, table, columns, downloadLocation=None, **kwargs): temp_dir = tempfile.mkdtemp() zipfilepath = os.path.join(temp_dir, "table_file_download.zip") try: - zipfilepath = wrap_async_to_sync( - download_by_file_handle( - file_handle_id=response["resultZipFileHandleId"], - synapse_id=table.tableId, - entity_type="TableEntity", - destination=zipfilepath, - ) + zipfilepath = await download_by_file_handle( + file_handle_id=response["resultZipFileHandleId"], + synapse_id=table.tableId, + entity_type="TableEntity", + destination=zipfilepath, ) # TODO handle case when no zip file is returned # TODO test case when we give it partial or all bad file handles @@ -8574,9 +8763,22 @@ def sendMessage( Returns: The metadata of the created message """ + return wrap_async_to_sync( + self.sendMessage_async( + userIds=userIds, + messageSubject=messageSubject, + messageBody=messageBody, + contentType=contentType, + ) + ) + + async def sendMessage_async( + self, userIds, messageSubject, messageBody, contentType="text/plain" + ): + """Async version of sendMessage.""" - fileHandleId = wrap_async_to_sync( - multipart_upload_string_async(self, messageBody, content_type=contentType) + fileHandleId = await multipart_upload_string_async( + self, messageBody, content_type=contentType ) message = dict( recipients=userIds, subject=messageSubject, fileHandleId=fileHandleId diff --git a/synapseclient/core/async_utils.py b/synapseclient/core/async_utils.py index 074c4697f..e774e367a 100644 --- a/synapseclient/core/async_utils.py +++ b/synapseclient/core/async_utils.py @@ -2,6 +2,7 @@ import asyncio import functools +import sys from typing import Any, Callable, Coroutine, Union import nest_asyncio @@ -83,7 +84,17 @@ def wrap_async_to_sync(coroutine: Coroutine[Any, Any, Any]) -> Any: except RuntimeError: pass - if loop: + if loop and sys.version_info >= (3, 14, 0): + raise RuntimeError( + f"Python 3.14+ detected an active event loop, which prevents automatic async-to-sync conversion.\n" + f"This is a limitation of asyncio in Python 3.14+.\n\n" + f"To resolve this, use the async function directly:\n" + f" • Instead of: result = your_function()\n" + f" • Use: result = await {coroutine.__name__}()\n\n" + f"For Jupyter/IPython notebooks: You can use 'await' directly in cells.\n" + f"For other async contexts: Ensure you're in an async function and use 'await'." + ) + elif loop: nest_asyncio.apply(loop=loop) return loop.run_until_complete(coroutine) else: @@ -112,7 +123,17 @@ def wrap_async_generator_to_sync_generator(async_gen_func: Callable, *args, **kw except RuntimeError: pass - if loop: + if loop and sys.version_info >= (3, 14, 0): + raise RuntimeError( + f"Python 3.14+ detected an active event loop, which prevents automatic async-to-sync conversion.\n" + f"This is a limitation of asyncio in Python 3.14+.\n\n" + f"To resolve this, use the async generator directly:\n" + f" • Instead of: for item in your_generator():\n" + f" • Use: async for item in {async_gen_func.__name__}():\n\n" + f"For Jupyter/IPython notebooks: You can use 'async for' directly in cells.\n" + f"For other async contexts: Ensure you're in an async function and use 'async for'." + ) + elif loop: nest_asyncio.apply(loop=loop) # Create the async generator @@ -181,7 +202,17 @@ async def wrapper(*args, **kwargs): except RuntimeError: pass - if loop: + if loop and sys.version_info >= (3, 14, 0): + raise RuntimeError( + f"Python 3.14+ detected an active event loop, which prevents automatic async-to-sync conversion.\n" + f"This is a limitation of asyncio in Python 3.14+.\n\n" + f"To resolve this, call the async method directly:\n" + f" • Instead of: result = obj.method_name()\n" + f" • Use: result = await obj.{async_method_name}()\n\n" + f"For Jupyter/IPython notebooks: You can use 'await' directly in cells.\n" + f"For other async contexts: Ensure you're in an async function and use 'await'." + ) + elif loop: nest_asyncio.apply(loop=loop) return loop.run_until_complete(wrapper(*args, **kwargs)) else: diff --git a/synapseclient/core/upload/upload_functions_async.py b/synapseclient/core/upload/upload_functions_async.py index 1b5af3f22..528273a35 100644 --- a/synapseclient/core/upload/upload_functions_async.py +++ b/synapseclient/core/upload/upload_functions_async.py @@ -240,7 +240,11 @@ async def create_external_file_handle( # Create the file handle because there is nothing to upload file_handle = await post_external_filehandle( - external_url=url, mimetype=mimetype, md5=md5, file_size=file_size + external_url=url, + mimetype=mimetype, + md5=md5, + file_size=file_size, + synapse_client=syn, ) span.set_attribute("synapse.file_handle_id", file_handle.get("id")) @@ -280,7 +284,11 @@ async def upload_external_file_handle_sftp( file_md5 = md5 or utils.md5_for_file_hex(filename=file_path) file_handle = await post_external_filehandle( - external_url=uploaded_url, mimetype=mimetype, md5=file_md5, file_size=file_size + external_url=uploaded_url, + mimetype=mimetype, + md5=file_md5, + file_size=file_size, + synapse_client=syn, ) span.set_attribute("synapse.file_handle_id", file_handle["id"]) diff --git a/synapseclient/models/dataset.py b/synapseclient/models/dataset.py index 86f15ad76..646b226b2 100644 --- a/synapseclient/models/dataset.py +++ b/synapseclient/models/dataset.py @@ -9,7 +9,7 @@ from synapseclient import Synapse from synapseclient.api.table_services import ViewEntityType, ViewTypeMask -from synapseclient.core.async_utils import async_to_sync, wrap_async_to_sync +from synapseclient.core.async_utils import async_to_sync from synapseclient.core.constants import concrete_types from synapseclient.core.utils import MB, delete_none_keys from synapseclient.models import Activity, Annotations @@ -379,6 +379,141 @@ def snapshot( """ return TableUpdateTransaction + def add_item( + self, + item: Union[EntityRef, "File", "Folder"], + *, + synapse_client: Optional[Synapse] = None, + ) -> None: + """Adds an item in the form of an EntityRef to the dataset. + For Folders, children are added recursively. Effect is not seen + until the dataset is stored. + + Arguments: + item: Entity to add to the dataset. Must be an EntityRef, File, or Folder. + synapse_client: If not passed in and caching was not disabled by + `Synapse.allow_client_caching(False)` this will use the last created + instance from the Synapse class constructor. + + Raises: + ValueError: If the item is not an EntityRef, File, or Folder + + Example: Add a file to a dataset. +   + + ```python + from synapseclient import Synapse + from synapseclient.models import Dataset, File + + syn = Synapse() + syn.login() + + my_dataset = Dataset(id="syn1234").get() + my_dataset.add_item(File(id="syn1235")) + my_dataset.store() + ``` + + Example: Add a folder to a dataset. + All child files are recursively added to the dataset. + + ```python + from synapseclient import Synapse + from synapseclient.models import Dataset, Folder + + syn = Synapse() + syn.login() + + my_dataset = Dataset(id="syn1234").get() + my_dataset.add_item(Folder(id="syn1236")) + my_dataset.store() + ``` + + Example: Add an entity reference to a dataset. +   + + ```python + from synapseclient import Synapse + from synapseclient.models import Dataset, EntityRef + + syn = Synapse() + syn.login() + + my_dataset = Dataset(id="syn1234").get() + my_dataset.add_item(EntityRef(id="syn1237", version=1)) + my_dataset.store() + ``` + """ + return None + + def remove_item( + self, + item: Union[EntityRef, "File", "Folder"], + *, + synapse_client: Optional[Synapse] = None, + ) -> None: + """ + Removes an item from the dataset. For Folders, all + children of the folder are removed recursively. + Effect is not seen until the dataset is stored. + + Arguments: + item: The Synapse ID or Entity to remove from the dataset + synapse_client: If not passed in and caching was not disabled by + `Synapse.allow_client_caching(False)` this will use the last created + instance from the Synapse class constructor. + + Returns: + None + + Raises: + ValueError: If the item is not a valid type + + Example: Remove a file from a dataset. +   + + ```python + from synapseclient import Synapse + from synapseclient.models import Dataset, File + + syn = Synapse() + syn.login() + + my_dataset = Dataset(id="syn1234").get() + my_dataset.remove_item(File(id="syn1235")) + my_dataset.store() + ``` + + Example: Remove a folder from a dataset. + All child files are recursively removed from the dataset. + + ```python + from synapseclient import Synapse + from synapseclient.models import Dataset, Folder + + syn = Synapse() + syn.login() + + my_dataset = Dataset(id="syn1234").get() + my_dataset.remove_item(Folder(id="syn1236")) + my_dataset.store() + ``` + + Example: Remove an entity reference from a dataset. +   + ```python + from synapseclient import Synapse + from synapseclient.models import Dataset, EntityRef + + syn = Synapse() + syn.login() + + my_dataset = Dataset(id="syn1234").get() + my_dataset.remove_item(EntityRef(id="syn1237", version=1)) + my_dataset.store() + ``` + """ + return None + @dataclass @async_to_sync @@ -946,7 +1081,7 @@ def _append_entity_ref(self, entity_ref: EntityRef) -> None: if entity_ref not in self.items: self.items.append(entity_ref) - def add_item( + async def add_item_async( self, item: Union[EntityRef, "File", "Folder"], *, @@ -969,15 +1104,19 @@ def add_item(   ```python + import asyncio from synapseclient import Synapse from synapseclient.models import Dataset, File syn = Synapse() syn.login() - my_dataset = Dataset(id="syn1234").get() - my_dataset.add_item(File(id="syn1235")) - my_dataset.store() + async def main(): + my_dataset = await Dataset(id="syn1234").get_async() + await my_dataset.add_item_async(File(id="syn1235")) + await my_dataset.store_async() + + asyncio.run(main()) ``` Example: Add a folder to a dataset. @@ -990,9 +1129,12 @@ def add_item( syn = Synapse() syn.login() - my_dataset = Dataset(id="syn1234").get() - my_dataset.add_item(Folder(id="syn1236")) - my_dataset.store() + async def main(): + my_dataset = await Dataset(id="syn1234").get_async() + await my_dataset.add_item_async(Folder(id="syn1236")) + await my_dataset.store_async() + + asyncio.run(main()) ``` Example: Add an entity reference to a dataset. @@ -1005,9 +1147,12 @@ def add_item( syn = Synapse() syn.login() - my_dataset = Dataset(id="syn1234").get() - my_dataset.add_item(EntityRef(id="syn1237", version=1)) - my_dataset.store() + async def main(): + my_dataset = await Dataset(id="syn1234").get_async() + await my_dataset.add_item_async(EntityRef(id="syn1237", version=1)) + await my_dataset.store_async() + + asyncio.run(main()) ``` """ from synapseclient.models import File, Folder @@ -1020,12 +1165,14 @@ def add_item( if not item.version_number: item = File( id=item.id, version_number=item.version_number, download_file=False - ).get() + ).get(synapse_client=client) self._append_entity_ref( entity_ref=EntityRef(id=item.id, version=item.version_number) ) elif isinstance(item, Folder): - children = wrap_async_to_sync(item._retrieve_children(follow_link=True)) + children = await item._retrieve_children( + follow_link=True, synapse_client=client + ) for child in children: if child["type"] == concrete_types.FILE_ENTITY: self._append_entity_ref( @@ -1034,7 +1181,9 @@ def add_item( ) ) else: - self.add_item(item=Folder(id=child["id"]), synapse_client=client) + await self.add_item_async( + item=Folder(id=child["id"]), synapse_client=client + ) else: raise ValueError( f"item must be one of EntityRef, File, or Folder. {item} is a {type(item)}" @@ -1050,7 +1199,7 @@ def _remove_entity_ref(self, entity_ref: EntityRef) -> None: raise ValueError(f"Entity {entity_ref.id} not found in items list") self.items.remove(entity_ref) - def remove_item( + async def remove_item_async( self, item: Union[EntityRef, "File", "Folder"], *, @@ -1077,44 +1226,56 @@ def remove_item(   ```python + import asyncio from synapseclient import Synapse from synapseclient.models import Dataset, File syn = Synapse() syn.login() - my_dataset = Dataset(id="syn1234").get() - my_dataset.remove_item(File(id="syn1235")) - my_dataset.store() + async def main(): + my_dataset = await Dataset(id="syn1234").get_async() + await my_dataset.remove_item_async(File(id="syn1235")) + await my_dataset.store_async() + + asyncio.run(main()) ``` Example: Remove a folder from a dataset. All child files are recursively removed from the dataset. ```python + import asyncio from synapseclient import Synapse from synapseclient.models import Dataset, Folder syn = Synapse() syn.login() - my_dataset = Dataset(id="syn1234").get() - my_dataset.remove_item(Folder(id="syn1236")) - my_dataset.store() + async def main(): + my_dataset = await Dataset(id="syn1234").get_async() + await my_dataset.remove_item_async(Folder(id="syn1236")) + await my_dataset.store_async() + + asyncio.run(main()) ``` Example: Remove an entity reference from a dataset.   ```python + import asyncio from synapseclient import Synapse from synapseclient.models import Dataset, EntityRef syn = Synapse() syn.login() - my_dataset = Dataset(id="syn1234").get() - my_dataset.remove_item(EntityRef(id="syn1237", version=1)) - my_dataset.store() + async def main(): + my_dataset = await Dataset(id="syn1234").get_async() + await my_dataset.remove_item_async(EntityRef(id="syn1237", version=1)) + await my_dataset.store_async() + + asyncio.run(main()) ``` """ from synapseclient.models import File, Folder @@ -1130,7 +1291,7 @@ def remove_item( ).get() self._remove_entity_ref(EntityRef(id=item.id, version=item.version_number)) elif isinstance(item, Folder): - children = wrap_async_to_sync(item._retrieve_children(follow_link=True)) + children = await item._retrieve_children(follow_link=True) for child in children: if child["type"] == concrete_types.FILE_ENTITY: self._remove_entity_ref( diff --git a/synapseclient/models/mixins/table_components.py b/synapseclient/models/mixins/table_components.py index c0f810c62..ab6a4774e 100644 --- a/synapseclient/models/mixins/table_components.py +++ b/synapseclient/models/mixins/table_components.py @@ -159,7 +159,7 @@ def row_labels_from_rows(rows: List[Row]) -> List[Row]: async def _query_table_csv( query: str, - synapse: Synapse, + synapse_client: Synapse, header: bool = True, include_row_id_and_row_version: bool = True, # for csvTableDescriptor @@ -186,7 +186,7 @@ async def _query_table_csv( Arguments: query: The SQL query string to execute against the table. - synapse: An authenticated Synapse client instance used for making the API call. + synapse_client: An authenticated Synapse client instance used for making the API call. header: Should the first line contain the column names as a header in the resulting file? Set to True to include the headers, False otherwise. The default value is True. @@ -224,7 +224,7 @@ async def _query_table_csv( A tuple containing the download result (QueryJob object) and the path to the downloaded CSV file. The download result is a dictionary containing information about the download. """ - + client = Synapse.get_client(synapse_client=synapse_client) csv_descriptor = CsvTableDescriptor( separator=separator, escape_character=escape_character, @@ -251,11 +251,11 @@ async def _query_table_csv( ) download_from_table_result = await query_job_request.send_job_and_wait_async( - synapse_client=synapse, timeout=timeout + synapse_client=client, timeout=timeout ) file_handle_id = download_from_table_result.results_file_handle_id - cached_file_path = synapse.cache.get( + cached_file_path = client.cache.get( file_handle_id=file_handle_id, path=download_location ) if cached_file_path is not None: @@ -266,7 +266,7 @@ async def _query_table_csv( download_location=download_location ) else: - download_dir = synapse.cache.get_cache_dir(file_handle_id=file_handle_id) + download_dir = client.cache.get_cache_dir(file_handle_id=file_handle_id) os.makedirs(download_dir, exist_ok=True) filename = f"SYNAPSE_TABLE_QUERY_{file_handle_id}.csv" @@ -275,13 +275,13 @@ async def _query_table_csv( synapse_id=extract_synapse_id_from_query(query), entity_type="TableEntity", destination=os.path.join(download_dir, filename), - synapse_client=synapse, + synapse_client=client, ) return download_from_table_result, path def _query_table_next_page( - next_page_token: "QueryNextPageToken", table_id: str, synapse: Synapse + next_page_token: "QueryNextPageToken", table_id: str, synapse_client: Synapse ) -> "QueryResultBundle": """ Retrieve following pages if the result contains a *nextPageToken* @@ -289,12 +289,13 @@ def _query_table_next_page( Arguments: next_page_token: Forward this token to get the next page of results. table_id: The Synapse ID of the table - synapse: An authenticated Synapse client instance used for making the API call. + synapse_client: An authenticated Synapse client instance used for making the API call. Returns: The following page of results as a QueryResultBundle """ + synapse = Synapse.get_client(synapse_client=synapse_client) uri = "/entity/{id}/table/query/nextPage/async".format(id=table_id) result = synapse._waitForAsync(uri=uri, request=next_page_token.token) return QueryResultBundle.fill_from_dict(data=result) @@ -302,7 +303,7 @@ def _query_table_next_page( async def _query_table_row_set( query: str, - synapse: Synapse, + synapse_client: Synapse, limit: int = None, offset: int = None, part_mask=None, @@ -333,7 +334,7 @@ async def _query_table_row_set( ) completed_request = await query_bundle_request.send_job_and_wait_async( - synapse_client=synapse, timeout=timeout + synapse_client=synapse_client, timeout=timeout ) return QueryResultBundle( @@ -352,7 +353,7 @@ async def _query_table_row_set( async def _table_query( query: str, - synapse: Optional[Synapse] = None, + synapse_client: Optional[Synapse] = None, results_as: str = "csv", timeout: int = 250, **kwargs, @@ -389,17 +390,15 @@ async def _table_query( If `results_as` is "rowset", returns a QueryResultBundle object. If `results_as` is "csv", returns a tuple of (QueryJob, csv_path). """ - client = Synapse.get_client(synapse_client=synapse) - if results_as.lower() == "rowset": return await _query_table_row_set( - query=query, synapse=client, timeout=timeout, **kwargs + query=query, synapse_client=synapse_client, timeout=timeout, **kwargs ) elif results_as.lower() == "csv": result, csv_path = await _query_table_csv( query=query, - synapse=client, + synapse_client=synapse_client, quote_character=kwargs.get("quote_character", DEFAULT_QUOTE_CHARACTER), escape_character=kwargs.get("escape_character", DEFAULT_ESCAPSE_CHAR), line_end=kwargs.get("line_end", str(os.linesep)), @@ -425,7 +424,7 @@ async def _table_query( def _rowset_to_pandas_df( query_result_bundle: QueryResultBundle, - synapse: Synapse, + synapse_client: Synapse, row_id_and_version_in_index: bool = True, **kwargs, ) -> "DATA_FRAME_TYPE": @@ -437,7 +436,7 @@ def _rowset_to_pandas_df( table query. This is typically the response from a table query operation that includes query results, headers, and pagination information. see here: https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/QueryResultBundle.html - synapse: An authenticated Synapse client instance used for making additional + synapse_client: An authenticated Synapse client instance used for making additional API calls when pagination is required to fetch subsequent pages of results. row_id_and_version_in_index: If True, uses ROW_ID, ROW_VERSION (and ROW_ETAG if present) as the DataFrame index. @@ -510,7 +509,9 @@ def construct_rownames(query_result_bundle, offset=0): # see QueryResult: https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/QueryResult.html # see RowSet: https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/RowSet.html result = _query_table_next_page( - next_page_token=next_page_token, table_id=rowset.table_id, synapse=synapse + next_page_token=next_page_token, + table_id=rowset.table_id, + synapse_client=synapse_client, ) rowset = result.query_result.query_results next_page_token = result.query_result.next_page_token @@ -2788,6 +2789,7 @@ async def main(): header=header, download_location=download_location, timeout=timeout, + synapse_client=synapse_client, ) if download_location: @@ -2924,13 +2926,14 @@ async def main(): limit=limit, offset=offset, timeout=timeout, + synapse_client=synapse_client, ) as_df = await loop.run_in_executor( None, lambda: _rowset_to_pandas_df( query_result_bundle=results, - synapse=client, + synapse_client=client, row_id_and_version_in_index=False, ), ) diff --git a/synapseclient/models/schema_organization.py b/synapseclient/models/schema_organization.py index 2bc6d4a63..a6fc10566 100644 --- a/synapseclient/models/schema_organization.py +++ b/synapseclient/models/schema_organization.py @@ -29,8 +29,6 @@ from synapseclient.models.mixins.asynchronous_job import AsynchronousCommunicator from synapseclient.models.mixins.json_schema import JSONSchemaVersionInfo -SYNAPSE_SCHEMA_URL = f"{Synapse().repoEndpoint}/schema/type/registered/" - class SchemaOrganizationProtocol(Protocol): """ @@ -38,7 +36,9 @@ class SchemaOrganizationProtocol(Protocol): have a synchronous counterpart that may also be called. """ - def get(self, synapse_client: Optional["Synapse"] = None) -> "SchemaOrganization": + def get( + self, *, synapse_client: Optional["Synapse"] = None + ) -> "SchemaOrganization": """ Gets the metadata from Synapse for this organization @@ -68,7 +68,9 @@ def get(self, synapse_client: Optional["Synapse"] = None) -> "SchemaOrganization """ return self - def store(self, synapse_client: Optional["Synapse"] = None) -> "SchemaOrganization": + def store( + self, *, synapse_client: Optional["Synapse"] = None + ) -> "SchemaOrganization": """ Stores this organization in Synapse @@ -98,7 +100,7 @@ def store(self, synapse_client: Optional["Synapse"] = None) -> "SchemaOrganizati """ return self - def delete(self, synapse_client: Optional["Synapse"] = None) -> None: + def delete(self, *, synapse_client: Optional["Synapse"] = None) -> None: """ Deletes this organization in Synapse @@ -124,7 +126,7 @@ def delete(self, synapse_client: Optional["Synapse"] = None) -> None: return None def get_json_schemas( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> Generator["JSONSchema", None, None]: """ Gets the JSON Schemas that are part of this organization @@ -157,7 +159,7 @@ def get_json_schemas( synapse_client=synapse_client, ) - def get_acl(self, synapse_client: Optional["Synapse"] = None) -> dict[str, Any]: + def get_acl(self, *, synapse_client: Optional["Synapse"] = None) -> dict[str, Any]: """ Gets the ACL for this organization @@ -190,6 +192,7 @@ def update_acl( self, principal_id: int, access_type: list[str], + *, synapse_client: Optional["Synapse"] = None, ) -> None: """ @@ -254,7 +257,7 @@ def __post_init__(self) -> None: _check_name(self.name) async def get_async( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> "SchemaOrganization": """ Gets the metadata from Synapse for this organization @@ -300,7 +303,7 @@ async def get_org(): return self async def store_async( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> "SchemaOrganization": """ Stores this organization in Synapse @@ -344,7 +347,7 @@ async def store_org(): self.fill_from_dict(response) return self - async def delete_async(self, synapse_client: Optional["Synapse"] = None) -> None: + async def delete_async(self, *, synapse_client: Optional["Synapse"] = None) -> None: """ Deletes this organization in Synapse @@ -393,11 +396,13 @@ async def delete_org(): """ if not self.id: await self.get_async(synapse_client=synapse_client) - await delete_organization(self.id, synapse_client=synapse_client) + await delete_organization( + organization_id=self.id, synapse_client=synapse_client + ) @skip_async_to_sync async def get_json_schemas_async( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> AsyncGenerator["JSONSchema", None]: """ Gets the JSONSchemas that are part of this organization @@ -441,7 +446,7 @@ async def get_schemas(): yield JSONSchema().fill_from_dict(item) async def get_acl_async( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> dict[str, Any]: """ Gets the ACL for this organization @@ -492,6 +497,7 @@ async def update_acl_async( self, principal_id: int, access_type: list[str], + *, synapse_client: Optional["Synapse"] = None, ) -> None: """ @@ -575,7 +581,7 @@ class JSONSchemaProtocol(Protocol): have a synchronous counterpart that may also be called. """ - def get(self, synapse_client: Optional["Synapse"] = None) -> "JSONSchema": + def get(self, *, synapse_client: Optional["Synapse"] = None) -> "JSONSchema": """ Gets this JSONSchemas metadata @@ -612,6 +618,7 @@ def store( schema_body: dict[str, Any], version: Optional[str] = None, dry_run: bool = False, + *, synapse_client: Optional["Synapse"] = None, ) -> "JSONSchema": """ @@ -658,7 +665,10 @@ def store( return self def delete( - self, version: Optional[str] = None, synapse_client: Optional["Synapse"] = None + self, + version: Optional[str] = None, + *, + synapse_client: Optional["Synapse"] = None, ) -> None: """ Deletes this JSONSchema @@ -702,7 +712,7 @@ def delete( return None def get_versions( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> Generator["JSONSchemaVersionInfo", None, None]: """ Gets all versions of this JSONSchema @@ -736,7 +746,10 @@ def get_versions( ) def get_body( - self, version: Optional[str] = None, synapse_client: Optional["Synapse"] = None + self, + version: Optional[str] = None, + *, + synapse_client: Optional["Synapse"] = None, ) -> dict[str, Any]: """ Gets the body of this JSONSchema. @@ -822,7 +835,7 @@ def __post_init__(self) -> None: self.uri = None async def get_async( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> "JSONSchema": """ Gets the metadata for this JSONSchema from Synapse @@ -890,6 +903,7 @@ async def store_async( schema_body: dict[str, Any], version: Optional[str] = None, dry_run: bool = False, + *, synapse_client: Optional["Synapse"] = None, ) -> "JSONSchema": """ @@ -943,12 +957,15 @@ async def store_schema(): if not self.organization_name: raise ValueError("JSONSchema must have a organization_name") + client = Synapse.get_client(synapse_client=synapse_client) + request = CreateSchemaRequest( schema=schema_body, name=self.name, organization_name=self.organization_name, version=version, dry_run=dry_run, + synapse_schema_url=f"{client.repoEndpoint}/schema/type/registered/", ) completed_request: CreateSchemaRequest = await request.send_job_and_wait_async( synapse_client=synapse_client @@ -960,7 +977,10 @@ async def store_schema(): return self async def delete_async( - self, version: Optional[str] = None, synapse_client: Optional["Synapse"] = None + self, + version: Optional[str] = None, + *, + synapse_client: Optional["Synapse"] = None, ) -> None: """ If a version is supplied the specific version is deleted from Synapse. @@ -1026,7 +1046,7 @@ async def delete_schema(): @skip_async_to_sync async def get_versions_async( - self, synapse_client: Optional["Synapse"] = None + self, *, synapse_client: Optional["Synapse"] = None ) -> AsyncGenerator[JSONSchemaVersionInfo, None]: """ Gets all versions of this JSONSchema @@ -1071,7 +1091,10 @@ async def get_versions(): yield self._create_json_schema_version_from_response(schema) async def get_body_async( - self, version: Optional[str] = None, synapse_client: Optional["Synapse"] = None + self, + version: Optional[str] = None, + *, + synapse_client: Optional["Synapse"] = None, ) -> dict[str, Any]: """ Gets the body of this JSONSchema @@ -1273,6 +1296,9 @@ class CreateSchemaRequest(AsynchronousCommunicator): dry_run: bool = False """Whether or not to do the request as a dry-run""" + synapse_schema_url: str = field(repr=False, default=None) + """The base URL for Synapse schemas""" + concrete_type: str = field(init=False) """The concrete type of the request""" @@ -1294,7 +1320,7 @@ def __post_init__(self) -> None: self._check_semantic_version(self.version) uri = f"{uri}-{self.version}" self.uri = uri - self.id = f"{SYNAPSE_SCHEMA_URL}{uri}" + self.id = f"{self.synapse_schema_url}{uri}" self.schema["$id"] = self.id def to_synapse_request(self) -> dict[str, Any]: diff --git a/synapseclient/table.py b/synapseclient/table.py index 9956de0a6..6b8bb79c0 100644 --- a/synapseclient/table.py +++ b/synapseclient/table.py @@ -1978,8 +1978,8 @@ def __init__(self, schema, rowset): super(RowSetTable, self).__init__(schema, etag=rowset.get("etag", None)) self.rowset = rowset - def _synapse_store(self, syn): - row_reference_set = syn.store(self.rowset) + async def _synapse_store_async(self, syn): + row_reference_set = await syn.store_async(self.rowset) return RowSetTable(self.schema, row_reference_set) def asDataFrame(self): @@ -2544,17 +2544,17 @@ def __init__( self.setColumnHeaders(headers) - def _synapse_store(self, syn): + async def _synapse_store_async(self, syn: "Synapse"): copied_self = copy.copy(self) - return copied_self._update_self(syn) + return await copied_self._update_self_async(syn) - def _update_self(self, syn): + async def _update_self_async(self, syn: "Synapse"): if isinstance(self.schema, Schema) and self.schema.get("id", None) is None: # store schema - self.schema = syn.store(self.schema) + self.schema = await syn.store_async(self.schema) self.tableId = self.schema.id - result = syn._uploadCsv( + result = await syn._uploadCsv_async( self.filepath, self.schema if self.schema else self.tableId, updateEtag=self.etag, diff --git a/synapseutils/sync.py b/synapseutils/sync.py index 8ead132de..ad5b6f9a2 100644 --- a/synapseutils/sync.py +++ b/synapseutils/sync.py @@ -166,6 +166,33 @@ def syncFromSynapse( for f in entities: print(f.path) """ + return wrap_async_to_sync( + coroutine=syncFromSynapse_async( + syn=syn, + entity=entity, + path=path, + ifcollision=ifcollision, + allFiles=allFiles, + followLink=followLink, + manifest=manifest, + downloadFile=downloadFile, + ) + ) + + +async def syncFromSynapse_async( + syn: Synapse, + entity: Union[str, SynapseFile, SynapseProject, SynapseFolder], + path: str = None, + ifcollision: str = "overwrite.local", + allFiles=None, + followLink: bool = False, + manifest: str = "all", + downloadFile: bool = True, +): + """ + Asynchronous version of syncFromSynapse. + """ if manifest not in ("all", "root", "suppress"): raise ValueError( @@ -182,16 +209,14 @@ def syncFromSynapse( # their parts downloaded in additional threads in the same Executor with shared_download_progress_bar(file_size=1, synapse_client=syn): - root_entity = wrap_async_to_sync( - coroutine=_sync( - syn=syn, - entity=entity, - path=path, - if_collision=ifcollision, - follow_link=followLink, - download_file=downloadFile, - manifest=manifest, - ) + root_entity = await _sync( + syn=syn, + entity=entity, + path=path, + if_collision=ifcollision, + follow_link=followLink, + download_file=downloadFile, + manifest=manifest, ) files = [] @@ -589,7 +614,7 @@ async def _upload_item_async( used=used_activity, executed=executed_activity, ) - await item.store_async() + await item.store_async(synapse_client=self.syn) return item @@ -1132,6 +1157,29 @@ def syncToSynapse( Returns: None """ + wrap_async_to_sync( + coroutine=syncToSynapse_async( + syn=syn, + manifestFile=manifestFile, + dryRun=dryRun, + sendMessages=sendMessages, + retries=retries, + merge_existing_annotations=merge_existing_annotations, + associate_activity_to_new_version=associate_activity_to_new_version, + ) + ) + + +async def syncToSynapse_async( + syn: Synapse, + manifestFile, + dryRun: bool = False, + sendMessages: bool = True, + retries: int = MAX_RETRIES, + merge_existing_annotations: bool = True, + associate_activity_to_new_version: bool = False, +) -> None: + """Async version of syncToSynapse.""" df = readManifestFile(syn, manifestFile) sizes = [ @@ -1164,22 +1212,18 @@ def syncToSynapse( syn, "Upload of %s" % manifestFile, retries=retries ) upload = notify_decorator(_manifest_upload) - wrap_async_to_sync( - upload( - syn, - df, - merge_existing_annotations, - associate_activity_to_new_version, - ) + await upload( + syn, + df, + merge_existing_annotations, + associate_activity_to_new_version, ) else: - wrap_async_to_sync( - _manifest_upload( - syn, - df, - merge_existing_annotations, - associate_activity_to_new_version, - ) + await _manifest_upload( + syn, + df, + merge_existing_annotations, + associate_activity_to_new_version, ) progress_bar.update(total_upload_size - progress_bar.n) progress_bar.close() diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index d80b2d5e6..4331e41d5 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -21,11 +21,11 @@ from synapseclient.core.async_utils import wrap_async_to_sync from synapseclient.core.logging_setup import DEFAULT_LOGGER_NAME, SILENT_LOGGER_NAME from synapseclient.models import Project as Project_Model -from synapseclient.models import Team +from synapseclient.models import SubmissionView, Team tracer = trace.get_tracer("synapseclient") working_directory = tempfile.mkdtemp(prefix="someTestFolder") -Synapse.allow_client_caching = False +Synapse.allow_client_caching(False) def pytest_collection_modifyitems(items) -> None: @@ -34,7 +34,7 @@ def pytest_collection_modifyitems(items) -> None: I want to run all tests, even if they are not explictly async, within the same event loop. This will allow our async_to_sync wrapper logic use the same event loop - for all tests. This implictly allows us to re-use the HTTP connection pooling for + for all tests. This implicitly allows us to re-use the HTTP connection pooling for all tests. """ pytest_asyncio_tests = (item for item in items if is_async_test(item)) @@ -51,7 +51,7 @@ def syn(request) -> Synapse: """ print("Python version:", sys.version) - syn = Synapse(debug=False, skip_checks=True) + syn = Synapse(debug=False, skip_checks=True, cache_client=False) print("Testing against endpoints:") print(" " + syn.repoEndpoint) print(" " + syn.authEndpoint) @@ -80,7 +80,7 @@ def syn_with_logger(request) -> Synapse: """ print("Python version:", sys.version) - syn = Synapse(debug=False, skip_checks=True) + syn = Synapse(debug=False, skip_checks=True, cache_client=False) print("Testing against endpoints:") print(" " + syn.repoEndpoint) print(" " + syn.authEndpoint) @@ -103,7 +103,7 @@ async def project_model(request, syn: Synapse) -> Project_Model: # Make one project for all the tests to use proj = await Project_Model( name="integration_test_project" + str(uuid.uuid4()) - ).store_async() + ).store_async(synapse_client=syn) def project_teardown() -> None: wrap_async_to_sync(_cleanup(syn, [working_directory, proj.id])) @@ -114,7 +114,7 @@ def project_teardown() -> None: @pytest_asyncio.fixture(loop_scope="session", scope="session") -async def project(request, syn: Synapse) -> Project: +def project(request, syn: Synapse) -> Project: """ Create a project to be shared by all tests in the session. If xdist is being used a project is created for each worker node. @@ -131,22 +131,6 @@ def project_teardown(): return proj -@pytest.fixture(scope="function", autouse=True) -def clear_cache() -> None: - """ - Clear all LRU caches before each test to avoid any side effects. - """ - from synapseclient.api.entity_services import get_upload_destination - - # Clear the cache before each test - get_upload_destination.cache_clear() - - yield - - # Clear the cache after each test - get_upload_destination.cache_clear() - - @pytest_asyncio.fixture(loop_scope="session", scope="session") async def schedule_for_cleanup(request, syn: Synapse): """Returns a closure that takes an item that should be scheduled for cleanup. @@ -192,7 +176,15 @@ async def _cleanup(syn: Synapse, items): print(ex) elif isinstance(item, Team): try: - item.delete() + await item.delete_async(synapse_client=syn) + except Exception as ex: + if hasattr(ex, "response") and ex.response.status_code in [404, 403]: + pass + else: + print("Error cleaning up entity: " + str(ex)) + elif isinstance(item, SubmissionView): + try: + await item.delete_async(synapse_client=syn) except Exception as ex: if hasattr(ex, "response") and ex.response.status_code in [404, 403]: pass diff --git a/tests/integration/synapseclient/core/test_REST_request_helpers.py b/tests/integration/synapseclient/core/test_REST_request_helpers.py index 03e3b16f4..3de297f65 100644 --- a/tests/integration/synapseclient/core/test_REST_request_helpers.py +++ b/tests/integration/synapseclient/core/test_REST_request_helpers.py @@ -3,12 +3,12 @@ requests to the Synapse backend """ -from unittest import skip +# from unittest import skip from synapseclient import Column -@skip("Skip integration tests for soon to be removed code") +# @skip("Skip integration tests for soon to be removed code") async def test_createColumns(syn): columns_to_create = [ Column(name="FirstTestColumn", columnType="INTEGER"), diff --git a/tests/integration/synapseclient/core/test_download.py b/tests/integration/synapseclient/core/test_download.py index 2463a65a7..9dce6680e 100644 --- a/tests/integration/synapseclient/core/test_download.py +++ b/tests/integration/synapseclient/core/test_download.py @@ -259,6 +259,7 @@ async def test_download_check_md5( file_handle_associate_type="FileEntity", file_handle_id=entity_bad_md5.data_file_handle_id, expected_md5="2345a", + synapse_client=syn, ) async def test_download_from_url_resume_partial_download( @@ -340,6 +341,7 @@ async def test_download_expired_url( file_handle_associate_type="FileEntity", file_handle_id=file.data_file_handle_id, expected_md5=file.file_handle.content_md5, + synapse_client=syn, ) # THEN the expired URL is refreshed @@ -379,7 +381,7 @@ async def test_download_expired_presigned_url( SynapseError, match="The provided pre-signed URL has expired. Please provide a new pre-signed URL.", ): - path = download_from_url( + download_from_url( url=expired_url, destination=tempfile.gettempdir(), entity_id=None, @@ -387,6 +389,7 @@ async def test_download_expired_presigned_url( file_handle_id=None, expected_md5=file.file_handle.content_md5, url_is_presigned=True, + synapse_client=syn, ) # THEN the expired URL is refreshed @@ -425,6 +428,7 @@ async def test_download_via_presigned_url_md5_matches( file_handle_id=None, expected_md5=file.file_handle.content_md5, url_is_presigned=True, + synapse_client=syn, ) # THEN the expired URL is refreshed @@ -459,7 +463,7 @@ async def test_download_via_presigned_url_md5_mismatches( spy_file_handle = mocker.spy(download_functions, "get_file_handle_for_download") with pytest.raises(SynapseMd5MismatchError): - path = download_from_url( + download_from_url( url=url, destination=tempfile.gettempdir(), entity_id=None, @@ -467,6 +471,7 @@ async def test_download_via_presigned_url_md5_mismatches( file_handle_id=None, expected_md5="mismatchedmd5hash", url_is_presigned=True, + synapse_client=syn, ) # THEN the expired URL is refreshed @@ -507,6 +512,7 @@ async def test_download_via_presigned_url_no_md5( file_handle_id=None, expected_md5=None, url_is_presigned=True, + synapse_client=syn, ) # THEN the expired URL is refreshed @@ -881,10 +887,11 @@ async def test_download_from_url_multi_threaded_via_expired_presigned_url( SynapseError, match="The provided pre-signed URL has expired. Please provide a new pre-signed URL.", ): - path = await download_from_url_multi_threaded( + await download_from_url_multi_threaded( destination=file_path, presigned_url=presigned_url_info, expected_md5=file_md5, + synapse_client=syn, ) async def test_download_from_url_multi_threaded_via_presigned_url_no_file_name( @@ -946,10 +953,11 @@ async def test_download_from_url_multi_threaded_via_presigned_url_no_file_name( SynapseError, match="The provided pre-signed URL is missing the file name.", ): - path = await download_from_url_multi_threaded( + await download_from_url_multi_threaded( destination=file_path, presigned_url=presigned_url_info, expected_md5=file_md5, + synapse_client=syn, ) async def test_download_from_url_multi_threaded_via_presigned_url_md5_matches( @@ -1007,10 +1015,11 @@ async def test_download_from_url_multi_threaded_via_presigned_url_md5_matches( ), ): # WHEN I attempt to download the file with multiple parts, it should raise an error due to the missing file name - path = await download_from_url_multi_threaded( + await download_from_url_multi_threaded( destination=file_path, presigned_url=presigned_url_info, expected_md5=file_md5, + synapse_client=syn, ) # THEN the file is downloaded to the given location AND matches the original @@ -1073,10 +1082,11 @@ async def test_download_from_url_multi_threaded_via_presigned_url_md5_mismatches ): # WHEN I attempt to download the file with multiple parts, it should raise an error due to mismatched md5 with pytest.raises(SynapseMd5MismatchError): - path = await download_from_url_multi_threaded( + await download_from_url_multi_threaded( destination=file_path, presigned_url=presigned_url_info, expected_md5="mismatchedmd5hash", + synapse_client=syn, ) async def test_download_from_url_multi_threaded_via_presigned_url_no_md5( @@ -1138,10 +1148,11 @@ async def test_download_from_url_multi_threaded_via_presigned_url_no_md5( ), ): # WHEN I attempt to download the file with multiple parts, it should raise an error due to mismatched md5 - path = await download_from_url_multi_threaded( + await download_from_url_multi_threaded( destination=file_path, presigned_url=presigned_url_info, expected_md5=None, + synapse_client=syn, ) # THEN the file is downloaded to the given location AND no MD5-related operations diff --git a/tests/integration/synapseclient/core/test_external_storage.py b/tests/integration/synapseclient/core/test_external_storage.py index 7448d2585..a214eb2ae 100644 --- a/tests/integration/synapseclient/core/test_external_storage.py +++ b/tests/integration/synapseclient/core/test_external_storage.py @@ -208,7 +208,7 @@ async def _configure_storage_location( folder, storage_location_setting, _, - ) = self.syn.create_s3_storage_location( + ) = await self.syn.create_s3_storage_location_async( parent=self.project.id, folder_name=folder_name, bucket_name=bucket_name, diff --git a/tests/integration/synapseclient/core/upload/test_multipart_upload.py b/tests/integration/synapseclient/core/upload/test_multipart_upload.py index 03516b1a1..189471eff 100644 --- a/tests/integration/synapseclient/core/upload/test_multipart_upload.py +++ b/tests/integration/synapseclient/core/upload/test_multipart_upload.py @@ -31,7 +31,7 @@ async def test_round_trip(syn: Synapse, project: Project, schedule_for_cleanup): # Download the file and compare it with the original junk = File(parent=project, dataFileHandleId=fhid) junk.properties.update(syn._createEntity(junk.properties)) - (tmp_f, tmp_path) = tempfile.mkstemp() + (_, tmp_path) = tempfile.mkstemp() schedule_for_cleanup(tmp_path) junk["path"] = await download_by_file_handle( @@ -39,6 +39,7 @@ async def test_round_trip(syn: Synapse, project: Project, schedule_for_cleanup): synapse_id=junk["id"], entity_type="FileEntity", destination=tmp_path, + synapse_client=syn, ) assert filecmp.cmp(filepath, junk.path) @@ -114,6 +115,7 @@ def _put_chunk_or_fail_randomly(url, *args, **kwargs): synapse_id=junk["id"], entity_type="FileEntity", destination=tmp_path, + synapse_client=syn, ) assert filecmp.cmp(filepath, junk.path) @@ -179,7 +181,7 @@ async def test_multipart_upload_big_string( # Download the file and compare it with the original junk = File(parent=project, dataFileHandleId=fhid) junk.properties.update(syn._createEntity(junk.properties)) - (tmp_f, tmp_path) = tempfile.mkstemp() + (_, tmp_path) = tempfile.mkstemp() schedule_for_cleanup(tmp_path) junk["path"] = await download_by_file_handle( @@ -187,6 +189,7 @@ async def test_multipart_upload_big_string( synapse_id=junk["id"], entity_type="FileEntity", destination=tmp_path, + synapse_client=syn, ) with open(junk.path, encoding="utf-8") as f: @@ -280,13 +283,13 @@ def _multipart_copy_test( assert file_content == dest_file_content -async def test_multipart_copy(syn: Synapse, project: Project, schedule_for_cleanup): +def test_multipart_copy(syn: Synapse, project: Project, schedule_for_cleanup): """Test multi part copy using the minimum part size.""" _multipart_copy_test(syn, project, schedule_for_cleanup, MIN_PART_SIZE) @skip("Skip in normal testing because the large size makes it slow") -async def test_multipart_copy__big_parts( +def test_multipart_copy__big_parts( syn: Synapse, project: Project, schedule_for_cleanup ): _multipart_copy_test(syn, project, schedule_for_cleanup, 100 * utils.MB) diff --git a/tests/integration/synapseclient/core/upload/test_multipart_upload_async.py b/tests/integration/synapseclient/core/upload/test_multipart_upload_async.py index 5399fe310..9a4351fd1 100644 --- a/tests/integration/synapseclient/core/upload/test_multipart_upload_async.py +++ b/tests/integration/synapseclient/core/upload/test_multipart_upload_async.py @@ -152,7 +152,7 @@ def _put_chunk_or_fail_randomly(self, url, *args, **kwargs): finally: try: if "junk" in locals(): - await junk.delete_async() + await junk.delete_async(synapse_client=syn) except Exception: syn.logger.exception("Failed to cleanup") try: @@ -213,7 +213,7 @@ async def test_multipart_upload_big_string( # AND I store a reference to the String in an Entity junk = await File( parent_id=project_model.id, data_file_handle_id=file_handle_id - ).store_async() + ).store_async(synapse_client=syn) (_, tmp_path) = tempfile.mkstemp() schedule_for_cleanup(tmp_path) @@ -246,7 +246,11 @@ async def _multipart_copy_test( dest_folder_name = "test_multipart_copy_{}".format(uuid.uuid4()) # GIVEN A new folder with an S3 storage location that we can copy to - dest_folder, storage_location_setting, _ = syn.create_s3_storage_location( + ( + dest_folder, + storage_location_setting, + _, + ) = await syn.create_s3_storage_location_async( parent=project_model.id, folder_name=dest_folder_name ) diff --git a/tests/integration/synapseclient/integration_test.py b/tests/integration/synapseclient/integration_test.py index f25e7a51f..4bdce1d5f 100644 --- a/tests/integration/synapseclient/integration_test.py +++ b/tests/integration/synapseclient/integration_test.py @@ -7,7 +7,8 @@ import uuid from datetime import datetime from typing import Callable -from unittest import skip + +# from unittest import skip from unittest.mock import patch import pytest @@ -35,7 +36,7 @@ NEW_DESCRIPTION = "new description" -async def test_login(syn): +def test_login(syn): try: config = configparser.RawConfigParser() config.read(client.CONFIG_FILE) @@ -84,7 +85,7 @@ async def test_login(syn): syn.login(profile=profile, silent=True) -async def testCustomConfigFile(schedule_for_cleanup): +def testCustomConfigFile(schedule_for_cleanup): if os.path.isfile(client.CONFIG_FILE): configPath = "./CONFIGFILE" shutil.copyfile(client.CONFIG_FILE, configPath) @@ -100,8 +101,8 @@ async def testCustomConfigFile(schedule_for_cleanup): ) -@skip("Skip integration tests for soon to be removed code") -async def test_entity_version(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_entity_version(syn, project, schedule_for_cleanup): # Make an Entity and make sure the version is one entity = File(parent=project["id"]) entity["path"] = utils.make_bogus_data_file() @@ -155,8 +156,8 @@ async def test_entity_version(syn, project, schedule_for_cleanup): assert returnEntity.versionNumber == 1 -@skip("Skip integration tests for soon to be removed code") -async def test_md5_query(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_md5_query(syn, project, schedule_for_cleanup): # Add the same Entity several times path = utils.make_bogus_data_file() schedule_for_cleanup(path) @@ -177,8 +178,8 @@ async def test_md5_query(syn, project, schedule_for_cleanup): assert len(results) == num -@skip("Skip integration tests for soon to be removed code") -async def test_uploadFile_given_dictionary(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_uploadFile_given_dictionary(syn, project, schedule_for_cleanup): # Make a Folder Entity the old fashioned way folder = { "concreteType": Folder._synapse_entity_type, @@ -209,8 +210,8 @@ async def test_uploadFile_given_dictionary(syn, project, schedule_for_cleanup): syn.get(entity["id"]) -@skip("Skip integration tests for soon to be removed code") -async def test_upload_file_with_force_version_false( +# @skip("Skip integration tests for soon to be removed code") +def test_upload_file_with_force_version_false( syn: Synapse, project: Project, schedule_for_cleanup ) -> None: # GIVEN A bogus file to upload to synapse @@ -243,8 +244,8 @@ async def test_upload_file_with_force_version_false( assert not mocked_file_handle_upload.called -@skip("Skip integration tests for soon to be removed code") -async def test_upload_file_changed_with_force_version_false( +# @skip("Skip integration tests for soon to be removed code") +def test_upload_file_changed_with_force_version_false( syn: Synapse, project: Project, schedule_for_cleanup ) -> None: # GIVEN A bogus file to upload to synapse @@ -279,8 +280,8 @@ async def test_upload_file_changed_with_force_version_false( assert before_file_handle_id != entity["dataFileHandleId"] -@skip("Skip integration tests for soon to be removed code") -async def test_uploadFileEntity(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_uploadFileEntity(syn, project, schedule_for_cleanup): # Create a FileEntity # Dictionaries default to FileEntity as a type fname = utils.make_bogus_data_file() @@ -317,8 +318,8 @@ async def test_uploadFileEntity(syn, project, schedule_for_cleanup): assert filecmp.cmp(fname, entity["path"]) -@skip("Skip integration tests for soon to be removed code") -async def test_download_multithreaded(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_download_multithreaded(syn, project, schedule_for_cleanup): # Create a FileEntity # Dictionaries default to FileEntity as a type fname = utils.make_bogus_data_file() @@ -339,8 +340,8 @@ async def test_download_multithreaded(syn, project, schedule_for_cleanup): syn.multi_threaded = False -@skip("Skip integration tests for soon to be removed code") -async def test_downloadFile(schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_downloadFile(schedule_for_cleanup): # See if the a "wget" works filename = utils.download_file( "http://dev-versions.synapse.sagebase.org/sage_bionetworks_logo_274x128.png" @@ -349,8 +350,8 @@ async def test_downloadFile(schedule_for_cleanup): assert os.path.exists(filename) -@skip("Skip integration tests for soon to be removed code") -async def test_provenance(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_provenance(syn, project, schedule_for_cleanup): # Create a File Entity fname = utils.make_bogus_data_file() schedule_for_cleanup(fname) @@ -398,8 +399,8 @@ async def test_provenance(syn, project, schedule_for_cleanup): pytest.raises(SynapseHTTPError, syn.getProvenance, data_entity["id"]) -@skip("Skip integration tests for soon to be removed code") -async def test_annotations(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_annotations(syn, project, schedule_for_cleanup): # Get the annotations of an Entity entity = syn.store(Folder(parent=project["id"])) anno = syn.get_annotations(entity) @@ -447,8 +448,8 @@ async def test_annotations(syn, project, schedule_for_cleanup): assert annotation["maybe"] == [True, False] -@skip("Skip integration tests for soon to be removed code") -async def test_annotations_on_file_during_create_no_annotations( +# @skip("Skip integration tests for soon to be removed code") +def test_annotations_on_file_during_create_no_annotations( syn: Synapse, project: Project, schedule_for_cleanup ): # GIVEN a bogus file @@ -475,8 +476,8 @@ async def test_annotations_on_file_during_create_no_annotations( assert not mock_set_annotations.called -@skip("Skip integration tests for soon to be removed code") -async def test_annotations_on_file_during_create_with_annotations( +# @skip("Skip integration tests for soon to be removed code") +def test_annotations_on_file_during_create_with_annotations( syn: Synapse, project: Project, schedule_for_cleanup ): # GIVEN a bogus file @@ -504,8 +505,8 @@ async def test_annotations_on_file_during_create_with_annotations( assert len(annotations) == 2 -@skip("Skip integration tests for soon to be removed code") -async def test_get_user_profile(syn): +# @skip("Skip integration tests for soon to be removed code") +def test_get_user_profile(syn): p1 = syn.getUserProfile() # get by name @@ -517,16 +518,16 @@ async def test_get_user_profile(syn): assert p2.userName == p1.userName -@skip("Skip integration tests for soon to be removed code") -async def test_findEntityIdByNameAndParent(syn, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_findEntityIdByNameAndParent(syn, schedule_for_cleanup): project_name = str(uuid.uuid1()) project_id = syn.store(Project(name=project_name))["id"] assert project_id == syn.findEntityId(project_name) schedule_for_cleanup(project_id) -@skip("Skip integration tests for soon to be removed code") -async def test_getChildren(syn, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_getChildren(syn, schedule_for_cleanup): # setup a hierarchy for folders # PROJECT # | \ @@ -593,8 +594,8 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - @skip("Skip integration tests for soon to be removed code") - async def test_get_acl_default(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_acl_default(self) -> None: # GIVEN a project created with default permissions of administrator project_with_default_permissions: Entity = self.syn.store( Project(name=str(uuid.uuid4()) + "test_get_acl_default_permissions") @@ -620,8 +621,8 @@ async def test_get_acl_default(self) -> None: ] assert set(expected_permissions) == set(permissions) - @skip("Skip integration tests for soon to be removed code") - async def test_get_acl_read_only_permissions_on_entity(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_acl_read_only_permissions_on_entity(self) -> None: # GIVEN a project created with default permissions of administrator project_with_read_only_permissions: Entity = self.syn.store( Project(name=str(uuid.uuid4()) + "test_get_acl_read_permissions_on_project") @@ -645,8 +646,8 @@ async def test_get_acl_read_only_permissions_on_entity(self) -> None: expected_permissions = ["READ"] assert set(expected_permissions) == set(permissions) - @skip("Skip integration tests for soon to be removed code") - async def test_get_acl_through_team_assigned_to_user(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_acl_through_team_assigned_to_user(self) -> None: # GIVEN a project created with default permissions of administrator project_with_permissions_through_single_team: Entity = self.syn.store( Project( @@ -709,8 +710,8 @@ async def test_get_acl_through_team_assigned_to_user(self) -> None: ] assert set(expected_permissions) == set(permissions) - @skip("Skip integration tests for soon to be removed code") - async def test_get_acl_through_multiple_teams_assigned_to_user(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_acl_through_multiple_teams_assigned_to_user(self) -> None: # GIVEN a project created with default permissions of administrator project_with_permissions_through_multiple_teams: Entity = self.syn.store( Project( @@ -791,8 +792,8 @@ async def test_get_acl_through_multiple_teams_assigned_to_user(self) -> None: ] assert set(expected_permissions) == set(permissions) - @skip("Skip integration tests for soon to be removed code") - async def test_get_acl_for_project_with_public_and_registered_user(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_acl_for_project_with_public_and_registered_user(self) -> None: # GIVEN a project created with default permissions of administrator project_with_permissions_for_public_and_authenticated_users: Entity = ( self.syn.store( @@ -876,8 +877,8 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - @skip("Skip integration tests for soon to be removed code") - async def test_get_permissions_default(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_permissions_default(self) -> None: # GIVEN a project created with default permissions of administrator project_with_default_permissions: Entity = self.syn.store( Project(name=str(uuid.uuid4()) + "test_get_permissions_default_permissions") @@ -900,8 +901,8 @@ async def test_get_permissions_default(self) -> None: ] assert set(expected_permissions) == set(permissions.access_types) - @skip("Skip integration tests for soon to be removed code") - async def test_get_permissions_read_only_permissions_on_entity(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_permissions_read_only_permissions_on_entity(self) -> None: # GIVEN a project created with default permissions of administrator project_with_read_only_permissions: Entity = self.syn.store( Project( @@ -928,8 +929,8 @@ async def test_get_permissions_read_only_permissions_on_entity(self) -> None: assert set(expected_permissions) == set(permissions.access_types) - @skip("Skip integration tests for soon to be removed code") - async def test_get_permissions_through_team_assigned_to_user(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_permissions_through_team_assigned_to_user(self) -> None: # GIVEN a project created with default permissions of administrator project_with_permissions_through_single_team: Entity = self.syn.store( Project( @@ -991,8 +992,8 @@ async def test_get_permissions_through_team_assigned_to_user(self) -> None: ] assert set(expected_permissions) == set(permissions.access_types) - @skip("Skip integration tests for soon to be removed code") - async def test_get_permissions_through_multiple_teams_assigned_to_user( + # @skip("Skip integration tests for soon to be removed code") + def test_get_permissions_through_multiple_teams_assigned_to_user( self, ) -> None: # GIVEN a project created with default permissions of administrator @@ -1074,8 +1075,8 @@ async def test_get_permissions_through_multiple_teams_assigned_to_user( ] assert set(expected_permissions) == set(permissions.access_types) - @skip("Skip integration tests for soon to be removed code") - async def test_get_permissions_for_project_with_registered_user(self) -> None: + # @skip("Skip integration tests for soon to be removed code") + def test_get_permissions_for_project_with_registered_user(self) -> None: # GIVEN a project created with default permissions of administrator project_with_permissions_for_authenticated_users: Entity = self.syn.store( Project( @@ -1129,8 +1130,8 @@ async def test_get_permissions_for_project_with_registered_user(self) -> None: assert set(expected_permissions) == set(permissions.access_types) -@skip("Skip integration tests for soon to be removed code") -async def test_create_delete_team(syn: Synapse) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_create_delete_team(syn: Synapse) -> None: # GIVEN information about a team I want to create name = "python_client_integration_test_team_" + str(uuid.uuid4()) description = "test description" @@ -1162,7 +1163,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - @skip("Skip integration tests for soon to be removed code") + # @skip("Skip integration tests for soon to be removed code") async def test_rest_get_async(self, project: Project) -> None: # GIVEN a project stored in synapse @@ -1174,7 +1175,7 @@ async def test_rest_get_async(self, project: Project) -> None: assert new_project_instance["name"] == project["name"] assert new_project_instance["etag"] == project["etag"] - @skip("Skip integration tests for soon to be removed code") + # @skip("Skip integration tests for soon to be removed code") async def test_rest_post_async(self, project: Project) -> None: # GIVEN A bogus file to upload to synapse path = utils.make_bogus_uuid_file() @@ -1187,7 +1188,7 @@ async def test_rest_post_async(self, project: Project) -> None: ) # AND I store the file in Synapse - entity = self.syn.store(entity) + entity = await self.syn.store_async(entity) self.schedule_for_cleanup(entity.id) # WHEN I used the rest_post_async method to get the bundle @@ -1204,7 +1205,7 @@ async def test_rest_post_async(self, project: Project) -> None: assert entity_bundle["entity"]["name"] == entity.name assert entity_bundle["entity"]["etag"] == entity.etag - @skip("Skip integration tests for soon to be removed code") + # @skip("Skip integration tests for soon to be removed code") async def test_rest_put_async(self, project: Project) -> None: # GIVEN A bogus file to upload to synapse path = utils.make_bogus_uuid_file() @@ -1217,7 +1218,7 @@ async def test_rest_put_async(self, project: Project) -> None: ) # AND I store the file in Synapse - entity = self.syn.store(entity) + entity = await self.syn.store_async(entity) self.schedule_for_cleanup(entity.id) # AND I used the rest_post_async method to get the bundle @@ -1253,7 +1254,7 @@ async def test_rest_put_async(self, project: Project) -> None: ) assert entity_bundle_copy["entity"]["description"] == NEW_DESCRIPTION - @skip("Skip integration tests for soon to be removed code") + # @skip("Skip integration tests for soon to be removed code") async def test_rest_delete_async(self, project: Project) -> None: # GIVEN A bogus file to upload to synapse path = utils.make_bogus_uuid_file() @@ -1266,7 +1267,7 @@ async def test_rest_delete_async(self, project: Project) -> None: ) # AND I store the file in Synapse - entity = self.syn.store(entity) + entity = await self.syn.store_async(entity) self.schedule_for_cleanup(entity.id) # WHEN I use rest_delete_async to delete the entity diff --git a/tests/integration/synapseclient/integration_test_Entity.py b/tests/integration/synapseclient/integration_test_Entity.py index 5f5b4d874..9ab0c9902 100644 --- a/tests/integration/synapseclient/integration_test_Entity.py +++ b/tests/integration/synapseclient/integration_test_Entity.py @@ -7,7 +7,8 @@ import uuid from datetime import datetime as Datetime from typing import Callable -from unittest import skip + +# from unittest import skip from unittest.mock import patch import pytest @@ -28,8 +29,8 @@ from synapseclient.core.upload.upload_functions import create_external_file_handle -@skip("Skip integration tests for soon to be removed code") -async def test_entity(syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_entity(syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: # Update the project project_name = str(uuid.uuid4()) project = Project(name=project_name) @@ -172,8 +173,8 @@ async def test_entity(syn: Synapse, schedule_for_cleanup: Callable[..., None]) - assert os.path.basename(a_file_cached.path) == os.path.basename(a_file.path) -@skip("Skip integration tests for soon to be removed code") -async def test_special_characters(syn: Synapse, project: Project) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_special_characters(syn: Synapse, project: Project) -> None: folder = syn.store( Folder( "Special Characters Here", @@ -195,8 +196,8 @@ async def test_special_characters(syn: Synapse, project: Project) -> None: assert folder.russian_annotation[0] == "Обезьяна прикладом" -@skip("Skip integration tests for soon to be removed code") -async def test_get_local_file( +# @skip("Skip integration tests for soon to be removed code") +def test_get_local_file( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: new_path = utils.make_bogus_data_file() @@ -228,8 +229,8 @@ async def test_get_local_file( pytest.raises(SynapseError, syn.get, new_path, limitSearch="syn1") -@skip("Skip integration tests for soon to be removed code") -async def test_store_with_flags( +# @skip("Skip integration tests for soon to be removed code") +def test_store_with_flags( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: # -- CreateOrUpdate flag for Projects -- @@ -315,8 +316,8 @@ async def test_store_with_flags( assert ephemeral_bogus.shoe_size == [11.5] -@skip("Skip integration tests for soon to be removed code") -async def test_get_with_download_location_and_ifcollision( +# @skip("Skip integration tests for soon to be removed code") +def test_get_with_download_location_and_ifcollision( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: # Store a File and delete it locally @@ -370,8 +371,8 @@ async def test_get_with_download_location_and_ifcollision( os.remove(renamed_bogus.path) -@skip("Skip integration tests for soon to be removed code") -async def test_get_with_cache_hit_and_miss_with_ifcollision( +# @skip("Skip integration tests for soon to be removed code") +def test_get_with_cache_hit_and_miss_with_ifcollision( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None], @@ -435,8 +436,8 @@ async def test_get_with_cache_hit_and_miss_with_ifcollision( os.remove(filepath) -@skip("Skip integration tests for soon to be removed code") -async def test_store_activity( +# @skip("Skip integration tests for soon to be removed code") +def test_store_activity( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: # Create a File and an Activity @@ -490,8 +491,8 @@ async def test_store_activity( assert honking["id"] == honking2["id"] -@skip("Skip integration tests for soon to be removed code") -async def test_store_is_restricted_flag( +# @skip("Skip integration tests for soon to be removed code") +def test_store_is_restricted_flag( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: # Store a file with access requirements @@ -507,8 +508,8 @@ async def test_store_is_restricted_flag( assert intercepted.called -@skip("Skip integration tests for soon to be removed code") -async def test_external_file_handle(syn: Synapse, project: Project) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_external_file_handle(syn: Synapse, project: Project) -> None: # Tests shouldn't have external dependencies, but this is a pretty picture of Singapore singapore_url = "http://upload.wikimedia.org/wikipedia/commons/thumb/3/3e/1_singapore_city_skyline_dusk_panorama_2011.jpg/1280px-1_singapore_city_skyline_dusk_panorama_2011.jpg" # noqa singapore = File(singapore_url, parent=project, synapseStore=False) @@ -544,8 +545,8 @@ async def test_external_file_handle(syn: Synapse, project: Project) -> None: assert s2.externalURL == singapore_2_url -@skip("Skip integration tests for soon to be removed code") -async def test_synapse_store_flag( +# @skip("Skip integration tests for soon to be removed code") +def test_synapse_store_flag( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: # Store a path to a local file @@ -615,8 +616,8 @@ async def test_synapse_store_flag( ) -@skip("Skip integration tests for soon to be removed code") -async def test_create_or_update_project( +# @skip("Skip integration tests for soon to be removed code") +def test_create_or_update_project( syn: Synapse, schedule_for_cleanup: Callable[..., None] ) -> None: name = str(uuid.uuid4()) @@ -642,8 +643,8 @@ async def test_create_or_update_project( pytest.raises(Exception, syn.store, project, createOrUpdate=False) -@skip("Skip integration tests for soon to be removed code") -async def test_download_file_false( +# @skip("Skip integration tests for soon to be removed code") +def test_download_file_false( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: rename_suffix = "blah" + str(uuid.uuid4()) @@ -671,8 +672,8 @@ async def test_download_file_false( assert reupload.name == file.name -@skip("Skip integration tests for soon to be removed code") -async def test_download_file_URL_false(syn: Synapse, project: Project) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_download_file_URL_false(syn: Synapse, project: Project) -> None: # Upload an external file handle file_that_exists = "http://dev-versions.synapse.sagebase.org/synapsePythonClient" reupload = File(file_that_exists, synapseStore=False, parent=project) @@ -703,8 +704,8 @@ async def test_download_file_URL_false(syn: Synapse, project: Project) -> None: # SYNPY-366 -@skip("Skip integration tests for soon to be removed code") -async def test_download_local_file_url_path( +# @skip("Skip integration tests for soon to be removed code") +def test_download_local_file_url_path( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: path = utils.make_bogus_data_file() @@ -718,8 +719,8 @@ async def test_download_local_file_url_path( # SYNPY-424 -@skip("Skip integration tests for soon to be removed code") -async def test_store_file_handle_update_metadata( +# @skip("Skip integration tests for soon to be removed code") +def test_store_file_handle_update_metadata( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: original_file_path = utils.make_bogus_data_file() @@ -748,8 +749,8 @@ async def test_store_file_handle_update_metadata( assert [os.path.basename(replacement_file_path)] == new_entity.files -@skip("Skip integration tests for soon to be removed code") -async def test_store_docker_repository(syn: Synapse, project: Project) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_store_docker_repository(syn: Synapse, project: Project) -> None: repo_name = "some/repository/path" docker_repo = syn.store(DockerRepository(repo_name, parent=project)) assert isinstance(docker_repo, DockerRepository) @@ -757,8 +758,8 @@ async def test_store_docker_repository(syn: Synapse, project: Project) -> None: assert repo_name == docker_repo.repositoryName -@skip("Skip integration tests for soon to be removed code") -async def test_store_changing_external_url_by_changing_path( +# @skip("Skip integration tests for soon to be removed code") +def test_store_changing_external_url_by_changing_path( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: url = "https://www.synapse.org/Portal/clear.cache.gif" @@ -786,8 +787,8 @@ async def test_store_changing_external_url_by_changing_path( assert not ext.synapseStore -@skip("Skip integration tests for soon to be removed code") -async def test_store_changing_from_synapse_to_external_url_by_changing_path( +# @skip("Skip integration tests for soon to be removed code") +def test_store_changing_from_synapse_to_external_url_by_changing_path( syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: # create a temp file diff --git a/tests/integration/synapseclient/models/async/test_activity_async.py b/tests/integration/synapseclient/models/async/test_activity_async.py index cfde00331..2ca65b38d 100644 --- a/tests/integration/synapseclient/models/async/test_activity_async.py +++ b/tests/integration/synapseclient/models/async/test_activity_async.py @@ -39,7 +39,7 @@ async def create_file_with_activity( self.schedule_for_cleanup(file.path) if store_file: - await file.store_async() + await file.store_async(synapse_client=self.syn) self.schedule_for_cleanup(file.id) return file @@ -97,7 +97,7 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: ) # WHEN I store the activity - result = await activity.store_async(parent=file) + result = await activity.store_async(parent=file, synapse_client=self.syn) self.schedule_for_cleanup(result.id) # THEN I expect the activity to be stored correctly @@ -110,7 +110,7 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: modified_name = f"modified_name_{str(uuid.uuid4())}" result.name = modified_name result.description = "modified_description" - modified_result = await result.store_async() + modified_result = await result.store_async(synapse_client=self.syn) # THEN I expect the modified activity to be stored await self.verify_activity_properties( @@ -121,7 +121,9 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: ) # WHEN I get the activity from the file - retrieved_activity = await Activity.from_parent_async(parent=file) + retrieved_activity = await Activity.from_parent_async( + parent=file, synapse_client=self.syn + ) # THEN I expect the retrieved activity to match the modified one assert retrieved_activity.name == modified_name @@ -134,10 +136,12 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: ) # WHEN I delete the activity - await result.delete_async(parent=file) + await result.delete_async(parent=file, synapse_client=self.syn) # THEN I expect no activity to be associated with the file - activity_after_delete = await Activity.from_parent_async(parent=file) + activity_after_delete = await Activity.from_parent_async( + parent=file, synapse_client=self.syn + ) assert activity_after_delete is None async def test_store_activity_with_no_references( @@ -162,7 +166,7 @@ async def test_store_activity_with_no_references( ) # Clean up - await file.activity.delete_async(parent=file) + await file.activity.delete_async(parent=file, synapse_client=self.syn) async def test_store_activity_via_file_creation( self, project: Synapse_Project @@ -194,7 +198,7 @@ async def test_store_activity_via_file_creation( ) # Clean up - await file.activity.delete_async(parent=file) + await file.activity.delete_async(parent=file, synapse_client=self.syn) async def test_get_by_activity_id(self, project: Synapse_Project) -> None: """Test retrieving an activity by its ID using async method""" @@ -211,7 +215,9 @@ async def test_get_by_activity_id(self, project: Synapse_Project) -> None: stored_activity = file.activity # WHEN I retrieve the activity by its ID - retrieved_activity = await Activity.get_async(activity_id=stored_activity.id) + retrieved_activity = await Activity.get_async( + activity_id=stored_activity.id, synapse_client=self.syn + ) # THEN I expect to get the same activity assert retrieved_activity is not None @@ -226,7 +232,7 @@ async def test_get_by_activity_id(self, project: Synapse_Project) -> None: ) # Clean up - await stored_activity.delete_async(parent=file) + await stored_activity.delete_async(parent=file, synapse_client=self.syn) async def test_get_by_parent_id(self, project: Synapse_Project) -> None: """Test retrieving an activity by parent entity ID using async method""" @@ -241,7 +247,9 @@ async def test_get_by_parent_id(self, project: Synapse_Project) -> None: await asyncio.sleep(2) # WHEN I retrieve the activity by parent ID - retrieved_activity = await Activity.get_async(parent_id=file.id) + retrieved_activity = await Activity.get_async( + parent_id=file.id, synapse_client=self.syn + ) # THEN I expect to get the same activity assert retrieved_activity is not None @@ -256,7 +264,7 @@ async def test_get_by_parent_id(self, project: Synapse_Project) -> None: ) # Clean up - await stored_activity.delete_async(parent=file) + await stored_activity.delete_async(parent=file, synapse_client=self.syn) async def test_get_by_parent_id_with_version( self, project: Synapse_Project @@ -273,7 +281,9 @@ async def test_get_by_parent_id_with_version( # WHEN I retrieve the activity by parent ID with version retrieved_activity = await Activity.get_async( - parent_id=file.id, parent_version_number=file.version_number + parent_id=file.id, + parent_version_number=file.version_number, + synapse_client=self.syn, ) # THEN I expect to get the same activity @@ -286,18 +296,22 @@ async def test_get_by_parent_id_with_version( ) # Clean up - await stored_activity.delete_async(parent=file) + await stored_activity.delete_async(parent=file, synapse_client=self.syn) async def test_get_nonexistent_activity(self) -> None: """Test retrieving a nonexistent activity returns None using async method""" # WHEN I try to retrieve a nonexistent activity by ID - retrieved_activity = await Activity.get_async(activity_id="syn999999999") + retrieved_activity = await Activity.get_async( + activity_id="syn999999999", synapse_client=self.syn + ) # THEN I expect to get None assert retrieved_activity is None # AND when I try to retrieve by nonexistent parent ID - retrieved_activity = await Activity.get_async(parent_id="syn999999999") + retrieved_activity = await Activity.get_async( + parent_id="syn999999999", synapse_client=self.syn + ) # THEN I expect to get None assert retrieved_activity is None @@ -325,7 +339,7 @@ async def test_get_activity_id_takes_precedence( # WHEN I retrieve using activity_id from first activity and parent_id from second retrieved_activity = await Activity.get_async( - activity_id=stored_activity1.id, parent_id=file2.id + activity_id=stored_activity1.id, parent_id=file2.id, synapse_client=self.syn ) # THEN I expect to get the first activity (activity_id takes precedence) @@ -335,8 +349,8 @@ async def test_get_activity_id_takes_precedence( assert retrieved_activity.description == "first activity async" # Clean up - await stored_activity1.delete_async(parent=file1) - await stored_activity2.delete_async(parent=file2) + await stored_activity1.delete_async(parent=file1, synapse_client=self.syn) + await stored_activity2.delete_async(parent=file2, synapse_client=self.syn) async def test_get_no_parameters_raises_error(self) -> None: """Test that calling get_async() without parameters raises ValueError""" @@ -365,7 +379,7 @@ async def test_store_activity_with_string_parent( ) # WHEN I store the activity using a string parent ID - result = await activity.store_async(parent=file.id) + result = await activity.store_async(parent=file.id, synapse_client=self.syn) self.schedule_for_cleanup(result.id) # THEN I expect the activity to be stored correctly @@ -375,12 +389,14 @@ async def test_store_activity_with_string_parent( ) # AND when I retrieve it from the file - retrieved_activity = await Activity.from_parent_async(parent=file) + retrieved_activity = await Activity.from_parent_async( + parent=file, synapse_client=self.syn + ) assert retrieved_activity.id == result.id assert retrieved_activity.name == activity.name # Clean up - await result.delete_async(parent=file.id) + await result.delete_async(parent=file.id, synapse_client=self.syn) async def test_from_parent_with_string_parent( self, project: Synapse_Project @@ -396,7 +412,9 @@ async def test_from_parent_with_string_parent( stored_activity = file.activity # WHEN I retrieve the activity using a string parent ID - retrieved_activity = await Activity.from_parent_async(parent=file.id) + retrieved_activity = await Activity.from_parent_async( + parent=file.id, synapse_client=self.syn + ) # THEN I expect to get the same activity assert retrieved_activity is not None @@ -405,7 +423,7 @@ async def test_from_parent_with_string_parent( assert retrieved_activity.description == "testing from_parent with string" # Clean up - await stored_activity.delete_async(parent=file) + await stored_activity.delete_async(parent=file, synapse_client=self.syn) async def test_from_parent_with_string_parent_and_version( self, project: Synapse_Project @@ -421,7 +439,9 @@ async def test_from_parent_with_string_parent_and_version( # WHEN I retrieve the activity using a string parent ID with version parameter retrieved_activity = await Activity.from_parent_async( - parent=file.id, parent_version_number=file.version_number + parent=file.id, + parent_version_number=file.version_number, + synapse_client=self.syn, ) # THEN I expect to get the same activity @@ -430,7 +450,7 @@ async def test_from_parent_with_string_parent_and_version( assert retrieved_activity.name == activity.name # Clean up - await stored_activity.delete_async(parent=file) + await stored_activity.delete_async(parent=file, synapse_client=self.syn) async def test_from_parent_with_string_parent_with_embedded_version( self, project: Synapse_Project @@ -447,7 +467,7 @@ async def test_from_parent_with_string_parent_with_embedded_version( # WHEN I retrieve the activity using a string parent ID with embedded version parent_with_version = f"{file.id}.{file.version_number}" retrieved_activity = await Activity.from_parent_async( - parent=parent_with_version + parent=parent_with_version, synapse_client=self.syn ) # THEN I expect to get the same activity @@ -456,7 +476,7 @@ async def test_from_parent_with_string_parent_with_embedded_version( assert retrieved_activity.name == activity.name # Clean up - await stored_activity.delete_async(parent=file) + await stored_activity.delete_async(parent=file, synapse_client=self.syn) async def test_from_parent_version_precedence( self, project: Synapse_Project @@ -475,7 +495,9 @@ async def test_from_parent_version_precedence( parent_with_version = f"{file.id}.{file.version_number}" wrong_version = file.version_number + 1 if file.version_number > 1 else 999 retrieved_activity = await Activity.from_parent_async( - parent=parent_with_version, parent_version_number=wrong_version + parent=parent_with_version, + parent_version_number=wrong_version, + synapse_client=self.syn, ) # THEN I expect to get the activity (embedded version should take precedence) @@ -484,7 +506,7 @@ async def test_from_parent_version_precedence( assert retrieved_activity.name == activity.name # Clean up - await stored_activity.delete_async(parent=file) + await stored_activity.delete_async(parent=file, synapse_client=self.syn) async def test_delete_with_string_parent(self, project: Synapse_Project) -> None: """Test deleting an activity using a string parent ID""" @@ -496,10 +518,12 @@ async def test_delete_with_string_parent(self, project: Synapse_Project) -> None file = await self.create_file_with_activity(project, activity=activity) # WHEN I delete the activity using a string parent ID - await Activity.delete_async(parent=file.id) + await Activity.delete_async(parent=file.id, synapse_client=self.syn) # THEN I expect no activity to be associated with the file - activity_after_delete = await Activity.from_parent_async(parent=file) + activity_after_delete = await Activity.from_parent_async( + parent=file, synapse_client=self.syn + ) assert activity_after_delete is None async def test_disassociate_with_string_parent( @@ -514,8 +538,12 @@ async def test_disassociate_with_string_parent( file = await self.create_file_with_activity(project, activity=activity) # WHEN I disassociate the activity using a string parent ID - await Activity.disassociate_from_entity_async(parent=file.id) + await Activity.disassociate_from_entity_async( + parent=file.id, synapse_client=self.syn + ) # THEN I expect no activity to be associated with the file - activity_after_disassociate = await Activity.from_parent_async(parent=file) + activity_after_disassociate = await Activity.from_parent_async( + parent=file, synapse_client=self.syn + ) assert activity_after_disassociate is None diff --git a/tests/integration/synapseclient/models/async/test_agent_async.py b/tests/integration/synapseclient/models/async/test_agent_async.py index edef44a1c..d95203f3d 100644 --- a/tests/integration/synapseclient/models/async/test_agent_async.py +++ b/tests/integration/synapseclient/models/async/test_agent_async.py @@ -126,6 +126,7 @@ async def test_prompt(self) -> None: prompt="hello", enable_trace=True, timeout=ASYNC_JOB_TIMEOUT_SEC, + synapse_client=self.syn, ) # AND I expect the chat history to be updated with the prompt and response assert len(agent_session.chat_history) == 1 @@ -200,7 +201,7 @@ async def test_get_session(self) -> None: await agent.start_session_async(synapse_client=self.syn) # THEN I expect to be able to get the session with its id existing_session = await agent.get_session_async( - session_id=agent.current_session.id + session_id=agent.current_session.id, synapse_client=self.syn ) # AND I expect those sessions to be the same assert existing_session == agent.current_session @@ -220,6 +221,7 @@ async def test_prompt_with_session(self) -> None: enable_trace=True, session=session, timeout=ASYNC_JOB_TIMEOUT_SEC, + synapse_client=self.syn, ) test_session = agent.sessions[session.id] # THEN I expect the chat history to be updated with the prompt and response @@ -238,7 +240,10 @@ async def test_prompt_no_session(self) -> None: # WHEN I prompt the agent without a current session set # and no session provided await agent.prompt_async( - prompt="hello", enable_trace=True, timeout=ASYNC_JOB_TIMEOUT_SEC + prompt="hello", + enable_trace=True, + timeout=ASYNC_JOB_TIMEOUT_SEC, + synapse_client=self.syn, ) # THEN I expect a new session to be started and set as the current session assert agent.current_session is not None diff --git a/tests/integration/synapseclient/models/async/test_dataset_async.py b/tests/integration/synapseclient/models/async/test_dataset_async.py index 635fcb425..44f90896b 100644 --- a/tests/integration/synapseclient/models/async/test_dataset_async.py +++ b/tests/integration/synapseclient/models/async/test_dataset_async.py @@ -87,14 +87,18 @@ async def create_dataset_with_items( # Add files if provided if files: for file in files: - stored_file = await file.store_async(parent=project_model) - dataset.add_item(stored_file) + stored_file = await file.store_async( + parent=project_model, synapse_client=self.syn + ) + await dataset.add_item_async(stored_file, synapse_client=self.syn) # Add folders if provided if folders: for folder in folders: - stored_folder = await folder.store_async(parent=project_model) - dataset.add_item(stored_folder) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) + await dataset.add_item_async(stored_folder, synapse_client=self.syn) # Store the dataset dataset = await dataset.store_async(synapse_client=self.syn) @@ -166,11 +170,15 @@ async def test_dataset_with_items(self, project_model: Project) -> None: # WHEN I store the files and folder stored_files = [] for file in files: - stored_file = await file.store_async(parent=project_model) + stored_file = await file.store_async( + parent=project_model, synapse_client=self.syn + ) stored_files.append(stored_file) folder.files = folder_files - stored_folder = await folder.store_async(parent=project_model) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) # AND create a dataset with these items dataset = Dataset( @@ -181,10 +189,10 @@ async def test_dataset_with_items(self, project_model: Project) -> None: # Add individual files for file in stored_files: - dataset.add_item(file) + await dataset.add_item_async(file, synapse_client=self.syn) # Add folder - dataset.add_item(stored_folder) + await dataset.add_item_async(stored_folder, synapse_client=self.syn) # Store the dataset dataset = await dataset.store_async(synapse_client=self.syn) @@ -208,7 +216,7 @@ async def test_dataset_with_items(self, project_model: Project) -> None: assert item in retrieved_dataset.items # WHEN I remove one file from the dataset - dataset.remove_item(stored_files[0]) + await dataset.remove_item_async(stored_files[0], synapse_client=self.syn) await dataset.store_async(synapse_client=self.syn) # THEN that file should no longer be in the dataset @@ -225,7 +233,9 @@ async def test_dataset_query_operations(self, project_model: Project) -> None: """Test querying a dataset and different query modes""" # GIVEN a dataset with a file and custom column file = self.create_file_instance() - stored_file = await file.store_async(parent=project_model) + stored_file = await file.store_async( + parent=project_model, synapse_client=self.syn + ) dataset = Dataset( name=str(uuid.uuid4()), @@ -233,7 +243,7 @@ async def test_dataset_query_operations(self, project_model: Project) -> None: parent_id=project_model.id, columns=[Column(name="my_annotation", column_type=ColumnType.STRING)], ) - dataset.add_item(stored_file) + await dataset.add_item_async(stored_file, synapse_client=self.syn) dataset = await dataset.store_async(synapse_client=self.syn) self.schedule_for_cleanup(dataset.id) @@ -249,12 +259,14 @@ async def test_dataset_query_operations(self, project_model: Project) -> None: primary_keys=["id"], wait_for_eventually_consistent_view=True, dry_run=False, + synapse_client=self.syn, ) # THEN I can query the data row = await Dataset.query_async( query=f"SELECT * FROM {dataset.id} WHERE id = '{stored_file.id}'", timeout=QUERY_TIMEOUT_SEC, + synapse_client=self.syn, ) # AND the query results should match the expected values @@ -363,8 +375,8 @@ async def test_dataset_versioning(self, project_model: Project) -> None: file1 = self.create_file_instance() file2 = self.create_file_instance() - file1 = await file1.store_async(parent=project_model) - file2 = await file2.store_async(parent=project_model) + file1 = await file1.store_async(parent=project_model, synapse_client=self.syn) + file2 = await file2.store_async(parent=project_model, synapse_client=self.syn) dataset = Dataset( name=str(uuid.uuid4()), @@ -375,14 +387,14 @@ async def test_dataset_versioning(self, project_model: Project) -> None: self.schedule_for_cleanup(dataset.id) # WHEN I add the first file and create a snapshot - dataset.add_item(file1) + await dataset.add_item_async(file1, synapse_client=self.syn) await dataset.store_async(synapse_client=self.syn) await dataset.snapshot_async( timeout=ASYNC_JOB_TIMEOUT_SEC, synapse_client=self.syn ) # AND I add the second file and create another snapshot - dataset.add_item(file2) + await dataset.add_item_async(file2, synapse_client=self.syn) await dataset.store_async(synapse_client=self.syn) await dataset.snapshot_async( timeout=ASYNC_JOB_TIMEOUT_SEC, synapse_client=self.syn @@ -436,8 +448,10 @@ async def create_dataset( if has_file: file = self.create_file_instance() - stored_file = await file.store_async(parent=project_model) - dataset.add_item(stored_file) + stored_file = await file.store_async( + parent=project_model, synapse_client=self.syn + ) + await dataset.add_item_async(stored_file, synapse_client=self.syn) dataset = await dataset.store_async(synapse_client=self.syn) self.schedule_for_cleanup(dataset.id) @@ -511,7 +525,7 @@ async def test_dataset_collection_lifecycle(self, project_model: Project) -> Non async def test_dataset_collection_queries(self, project_model: Project) -> None: """Test querying DatasetCollections with various part masks""" # GIVEN a dataset and a collection with that dataset - dataset = await self.create_dataset(project_model, has_file=True) + dataset = await self.create_dataset(project_model=project_model, has_file=True) collection = DatasetCollection( name=str(uuid.uuid4()), @@ -535,12 +549,14 @@ async def test_dataset_collection_queries(self, project_model: Project) -> None: primary_keys=["id"], wait_for_eventually_consistent_view=True, dry_run=False, + synapse_client=self.syn, ) # THEN I can query and get the updated data row = await DatasetCollection.query_async( query=f"SELECT * FROM {collection.id} WHERE id = '{dataset.id}'", timeout=QUERY_TIMEOUT_SEC, + synapse_client=self.syn, ) assert row["id"][0] == dataset.id assert row["name"][0] == dataset.name @@ -577,6 +593,7 @@ async def test_dataset_collection_queries(self, project_model: Project) -> None: query=f"SELECT * FROM {collection.id}", part_mask=QUERY_RESULTS, timeout=QUERY_TIMEOUT_SEC, + synapse_client=self.syn, ) # THEN the data in the columns should match assert results_only.result["id"][0] == dataset.id diff --git a/tests/integration/synapseclient/models/async/test_file_async.py b/tests/integration/synapseclient/models/async/test_file_async.py index 29bcb2f43..76d936c5e 100644 --- a/tests/integration/synapseclient/models/async/test_file_async.py +++ b/tests/integration/synapseclient/models/async/test_file_async.py @@ -1439,7 +1439,9 @@ async def test_get_overwrite_local_for_matching_filenames( self.schedule_for_cleanup(file_2.id) # AND I change the download name of the second file to the first file - await file_2.change_metadata_async(download_as=file.name) + await file_2.change_metadata_async( + download_as=file.name, synapse_client=self.syn + ) # WHEN I get the file with the default collision of `overwrite.local` file_2 = await File( @@ -1481,7 +1483,9 @@ async def test_get_keep_local_for_matching_filenames( self.schedule_for_cleanup(file_2.id) # AND I change the download name of the second file to the first file - await file_2.change_metadata_async(download_as=file.name) + await file_2.change_metadata_async( + download_as=file.name, synapse_client=self.syn + ) # WHEN I get the file with the default collision of `keep.local` file_2 = await File( diff --git a/tests/integration/synapseclient/models/async/test_folder_async.py b/tests/integration/synapseclient/models/async/test_folder_async.py index 62441661f..fc6075594 100644 --- a/tests/integration/synapseclient/models/async/test_folder_async.py +++ b/tests/integration/synapseclient/models/async/test_folder_async.py @@ -117,7 +117,9 @@ async def test_store_folder_variations( # GIVEN a Folder object and a Project object # WHEN I store the Folder on Synapse - stored_folder = await folder.store_async(parent=project_model) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(folder.id) # THEN I expect the stored Folder to have the expected properties @@ -140,7 +142,7 @@ async def test_store_folder_variations( # WHEN I store the Folder on Synapse stored_folder_with_annotations = await folder_with_annotations.store_async( - parent=project_model + parent=project_model, synapse_client=self.syn ) self.schedule_for_cleanup(folder_with_annotations.id) @@ -161,7 +163,9 @@ async def test_store_folder_with_files( folder.files.append(file) # WHEN I store the Folder on Synapse - stored_folder = await folder.store_async(parent=project_model) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(folder.id) # THEN I expect the stored Folder to have the expected properties and files @@ -179,7 +183,9 @@ async def test_store_folder_with_files( # WHEN I store the Folder on Synapse stored_folder_with_multiple_files = ( - await folder_with_multiple_files.store_async(parent=project_model) + await folder_with_multiple_files.store_async( + parent=project_model, synapse_client=self.syn + ) ) self.schedule_for_cleanup(folder_with_multiple_files.id) @@ -204,7 +210,9 @@ async def test_store_folder_with_files_and_folders( folder.folders = folders # WHEN I store the Folder on Synapse - stored_folder = await folder.store_async(parent=project_model) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(folder.id) # THEN I expect the stored Folder to have the expected properties, files, and folders @@ -233,7 +241,9 @@ async def test_get_folder_methods( self, project_model: Project, folder: Folder ) -> None: # GIVEN a Folder object stored in Synapse - stored_folder = await folder.store_async(parent=project_model) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(folder.id) # Test Case 1: Get folder by ID @@ -280,15 +290,17 @@ def verify_folder_properties(self, folder: Folder, parent_id: str): async def test_delete_folder(self, project_model: Project, folder: Folder) -> None: # GIVEN a Folder object stored in Synapse - stored_folder = await folder.store_async(parent=project_model) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(folder.id) # WHEN I delete the Folder from Synapse - await stored_folder.delete_async() + await stored_folder.delete_async(synapse_client=self.syn) # THEN I expect the folder to have been deleted with pytest.raises(SynapseHTTPError) as e: - await stored_folder.get() + await stored_folder.get_async(synapse_client=self.syn) assert f"404 Client Error: Entity {stored_folder.id} is in trash can." in str( e.value @@ -388,24 +400,26 @@ async def test_copy_folder_with_files_and_folders( # GIVEN a nested folder structure with files and folders source_folder = self.create_nested_folder() source_folder.annotations = {"test": ["test"]} - stored_source_folder = await source_folder.store_async(parent=project_model) + stored_source_folder = await source_folder.store_async( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(stored_source_folder.id) # Test Case 1: Copy folder with all contents # Create first destination folder destination_folder_1 = await Folder( name=str(uuid.uuid4()), description="Destination for folder copy 1" - ).store_async(parent=project_model) + ).store_async(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(destination_folder_1.id) # WHEN I copy the folder to the destination folder copied_folder = await stored_source_folder.copy_async( - parent_id=destination_folder_1.id + parent_id=destination_folder_1.id, synapse_client=self.syn ) # AND I sync the destination folder from Synapse await destination_folder_1.sync_from_synapse_async( - recursive=False, download_file=False + recursive=False, download_file=False, synapse_client=self.syn ) # THEN I expect the copied Folder to have the expected properties @@ -417,17 +431,19 @@ async def test_copy_folder_with_files_and_folders( # Create a second destination folder for the second test case destination_folder_2 = await Folder( name=str(uuid.uuid4()), description="Destination for folder copy 2" - ).store_async(parent=project_model) + ).store_async(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(destination_folder_2.id) # WHEN I copy the folder to the destination folder excluding files copied_folder_no_files = await stored_source_folder.copy_async( - parent_id=destination_folder_2.id, exclude_types=["file"] + parent_id=destination_folder_2.id, + exclude_types=["file"], + synapse_client=self.syn, ) # AND I sync the destination folder from Synapse await destination_folder_2.sync_from_synapse_async( - recursive=False, download_file=False + recursive=False, download_file=False, synapse_client=self.syn ) # THEN I expect the copied Folder to have the expected properties but no files @@ -488,12 +504,14 @@ async def test_sync_from_synapse( folder.folders = sub_folders # WHEN I store the Folder on Synapse - stored_folder = await folder.store_async(parent=project_model) + stored_folder = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(folder.id) # AND I sync the folder from Synapse copied_folder = await stored_folder.sync_from_synapse_async( - path=root_directory_path + path=root_directory_path, synapse_client=self.syn ) # THEN I expect that the folder and its contents are synced from Synapse to disk @@ -617,11 +635,11 @@ async def test_sync_all_entity_types(self, project_model: Project) -> None: scope_ids=[folder.id], ) submission_view = await submission_view.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submission_view.id) + self.schedule_for_cleanup(submission_view) # WHEN I sync the folder from Synapse synced_folder = await folder.sync_from_synapse_async( - recursive=False, download_file=False + recursive=False, download_file=False, synapse_client=self.syn ) # THEN all entity types should be present @@ -687,30 +705,30 @@ async def create_test_hierarchy(self, project_model: Project) -> dict: folder = Folder( name=f"test_walk_folder_{str(uuid.uuid4())}", parent_id=project_model.id ) - folder = await folder.store_async() + folder = await folder.store_async(synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # Create a file in the root folder root_file = self.create_file_instance(self.schedule_for_cleanup) root_file.parent_id = folder.id - root_file = await root_file.store_async() + root_file = await root_file.store_async(synapse_client=self.syn) self.schedule_for_cleanup(root_file.id) # Create nested folder and file nested_folder = Folder(name=f"nested_folder_{str(uuid.uuid4())[:8]}") nested_folder.parent_id = folder.id - nested_folder = await nested_folder.store_async() + nested_folder = await nested_folder.store_async(synapse_client=self.syn) self.schedule_for_cleanup(nested_folder.id) nested_file = self.create_file_instance(self.schedule_for_cleanup) nested_file.parent_id = nested_folder.id - nested_file = await nested_file.store_async() + nested_file = await nested_file.store_async(synapse_client=self.syn) self.schedule_for_cleanup(nested_file.id) # Create another nested folder with no files empty_folder = Folder(name=f"empty_folder_{str(uuid.uuid4())[:8]}") empty_folder.parent_id = folder.id - empty_folder = await empty_folder.store_async() + empty_folder = await empty_folder.store_async(synapse_client=self.syn) self.schedule_for_cleanup(empty_folder.id) return { @@ -728,7 +746,9 @@ async def test_walk_async_recursive_true(self, project_model: Project) -> None: # WHEN: Walking through the folder asynchronously with recursive=True results = [] - async for result in hierarchy["folder"].walk_async(recursive=True): + async for result in hierarchy["folder"].walk_async( + recursive=True, synapse_client=self.syn + ): results.append(result) # THEN: Should get 3 results (folder root, nested_folder, empty_folder) @@ -769,7 +789,9 @@ async def test_walk_async_recursive_false(self, project_model: Project) -> None: # WHEN: Walking through the folder asynchronously with recursive=False results = [] - async for result in hierarchy["folder"].walk_async(recursive=False): + async for result in hierarchy["folder"].walk_async( + recursive=False, synapse_client=self.syn + ): results.append(result) # THEN: Should get only 1 result (folder root only) diff --git a/tests/integration/synapseclient/models/async/test_json_schema_async.py b/tests/integration/synapseclient/models/async/test_json_schema_async.py index 506075c8f..dfbf8e186 100644 --- a/tests/integration/synapseclient/models/async/test_json_schema_async.py +++ b/tests/integration/synapseclient/models/async/test_json_schema_async.py @@ -48,18 +48,24 @@ async def create_entity( entity_name = str(uuid.uuid4()) + name_suffix if entity_type == Project: - entity = await Project(name=entity_name).store_async() + entity = await Project(name=entity_name).store_async( + synapse_client=self.syn + ) elif entity_type == Folder: folder = Folder(name=entity_name) - entity = await folder.store_async(parent=project_model) + entity = await folder.store_async( + parent=project_model, synapse_client=self.syn + ) elif entity_type == File: file_fixture.name = entity_name - entity = await file_fixture.store_async(parent=project_model) + entity = await file_fixture.store_async( + parent=project_model, synapse_client=self.syn + ) elif entity_type == Table: table_fixture.name = entity_name - entity = await table_fixture.store_async() + entity = await table_fixture.store_async(synapse_client=self.syn) elif entity_type == EntityView: - entity = await entity_view_fixture.store_async() + entity = await entity_view_fixture.store_async(synapse_client=self.syn) else: raise ValueError(f"Unsupported entity type: {entity_type}") @@ -282,7 +288,7 @@ async def test_get_schema_derived_keys_async( "productPrice": 100, } - await created_entity.store_async() + await created_entity.store_async(synapse_client=self.syn) response = await created_entity.get_schema_async(synapse_client=self.syn) assert response.enable_derived_annotations == True @@ -333,7 +339,7 @@ async def test_validate_schema_async_invalid_annos( "productDescription": 1000, "productQuantity": "invalid string", } - await created_entity.store_async() + await created_entity.store_async(synapse_client=self.syn) # Ensure annotations are stored await asyncio.sleep(2) @@ -391,7 +397,7 @@ async def test_validate_schema_async_valid_annos( "productDescription": "This is a test product.", "productQuantity": 100, } - await created_entity.store_async() + await created_entity.store_async(synapse_client=self.syn) # Ensure annotations are stored await asyncio.sleep(2) response = await created_entity.validate_schema_async( @@ -463,8 +469,8 @@ async def test_get_validation_statistics_async( "productQuantity": "invalid string", } - await file_1.store_async(parent=created_entity) - await file_2.store_async(parent=created_entity) + await file_1.store_async(parent=created_entity, synapse_client=self.syn) + await file_2.store_async(parent=created_entity, synapse_client=self.syn) # Ensure annotations are stored await asyncio.sleep(2) @@ -538,8 +544,8 @@ async def test_get_invalid_validation_async( "productQuantity": "invalid string", } - await file_1.store_async(parent=created_entity) - await file_2.store_async(parent=created_entity) + await file_1.store_async(parent=created_entity, synapse_client=self.syn) + await file_2.store_async(parent=created_entity, synapse_client=self.syn) # Ensure annotations are stored await asyncio.sleep(2) diff --git a/tests/integration/synapseclient/models/async/test_permissions_async.py b/tests/integration/synapseclient/models/async/test_permissions_async.py index c82269828..60346acf8 100644 --- a/tests/integration/synapseclient/models/async/test_permissions_async.py +++ b/tests/integration/synapseclient/models/async/test_permissions_async.py @@ -74,15 +74,21 @@ async def create_entity( entity_name = str(uuid.uuid4()) + name_suffix if entity_type == Project: - entity = await Project(name=entity_name).store_async() + entity = await Project(name=entity_name).store_async( + synapse_client=self.syn + ) elif entity_type == Folder: - entity = await Folder(name=entity_name).store_async(parent=project_model) + entity = await Folder(name=entity_name).store_async( + parent=project_model, synapse_client=self.syn + ) elif entity_type == File: file_fixture.name = entity_name - entity = await file_fixture.store_async(parent=project_model) + entity = await file_fixture.store_async( + parent=project_model, synapse_client=self.syn + ) elif entity_type == Table: table_fixture.name = entity_name - entity = await table_fixture.store_async() + entity = await table_fixture.store_async(synapse_client=self.syn) else: raise ValueError(f"Unsupported entity type: {entity_type}") @@ -92,7 +98,9 @@ async def create_entity( async def create_team(self, description: str = DESCRIPTION_FAKE_TEAM) -> Team: """Helper to create a team with cleanup handling""" name = TEAM_PREFIX + str(uuid.uuid4()) - team = await Team(name=name, description=description).create_async() + team = await Team(name=name, description=description).create_async( + synapse_client=self.syn + ) self.schedule_for_cleanup(team) return team @@ -109,7 +117,9 @@ async def test_get_acl_default( user = await UserProfile().get_async(synapse_client=self.syn) # WHEN getting the permissions for the user on the entity - permissions = await entity.get_acl_async(principal_id=user.id) + permissions = await entity.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) # THEN the expected default admin permissions should be present expected_permissions = [ @@ -147,10 +157,13 @@ async def test_get_acl_limited_permissions( await entity.set_permissions_async( principal_id=user.id, access_type=limited_permissions, + synapse_client=self.syn, ) # WHEN getting the permissions for the user on the entity - permissions = await entity.get_acl_async(principal_id=user.id) + permissions = await entity.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) # THEN only the limited permissions should be present assert set(limited_permissions) == set(permissions) @@ -183,13 +196,18 @@ async def test_get_acl_through_team( await entity.set_permissions_async( principal_id=team.id, access_type=team_permissions, + synapse_client=self.syn, ) # AND the user has no direct permissions - await entity.set_permissions_async(principal_id=user.id, access_type=[]) + await entity.set_permissions_async( + principal_id=user.id, access_type=[], synapse_client=self.syn + ) # WHEN getting the permissions for the user on the entity - permissions = await entity.get_acl_async(principal_id=user.id) + permissions = await entity.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) # THEN the permissions should match the team's permissions assert set(team_permissions) == set(permissions) @@ -227,19 +245,26 @@ async def test_get_acl_through_multiple_teams( await entity.set_permissions_async( principal_id=team_1.id, access_type=team_1_permissions, + synapse_client=self.syn, ) # AND the second team has only READ and DOWNLOAD permissions team_2_permissions = ["READ", "DOWNLOAD"] await entity.set_permissions_async( - principal_id=team_2.id, access_type=team_2_permissions + principal_id=team_2.id, + access_type=team_2_permissions, + synapse_client=self.syn, ) # AND the user has no direct permissions - await entity.set_permissions_async(principal_id=user.id, access_type=[]) + await entity.set_permissions_async( + principal_id=user.id, access_type=[], synapse_client=self.syn + ) # WHEN getting the permissions for the user on the entity - permissions = await entity.get_acl_async(principal_id=user.id) + permissions = await entity.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) # THEN the permissions should be the combined set from both teams expected_permissions = [ @@ -271,11 +296,15 @@ async def test_get_acl_with_public_and_authenticated_users( user = await UserProfile().get_async(synapse_client=self.syn) # AND public users have READ permission - await entity.set_permissions_async(principal_id=PUBLIC, access_type=["READ"]) + await entity.set_permissions_async( + principal_id=PUBLIC, access_type=["READ"], synapse_client=self.syn + ) # AND authenticated users have READ and DOWNLOAD permissions await entity.set_permissions_async( - principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"] + principal_id=AUTHENTICATED_USERS, + access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # AND the user has specific permissions (excluding DOWNLOAD) @@ -291,16 +320,19 @@ async def test_get_acl_with_public_and_authenticated_users( await entity.set_permissions_async( principal_id=user.id, access_type=user_permissions, + synapse_client=self.syn, ) # WHEN getting public permissions (no principal_id) - public_permissions = await entity.get_acl_async() + public_permissions = await entity.get_acl_async(synapse_client=self.syn) # THEN only public permissions should be present assert set(["READ"]) == set(public_permissions) # WHEN getting the permissions for the authenticated user - user_permissions = await entity.get_acl_async(principal_id=user.id) + user_permissions = await entity.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) # THEN the permissions should include direct user permissions plus # permissions from authenticated users and public users @@ -322,13 +354,13 @@ async def test_get_acl_for_subfolder_with_different_permissions( # GIVEN a parent folder with default permissions parent_folder = await Folder( name=str(uuid.uuid4()) + "_parent_folder_test" - ).store_async(parent=project_model) + ).store_async(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(parent_folder.id) # AND a subfolder created inside the parent folder subfolder = await Folder( name=str(uuid.uuid4()) + "_subfolder_test" - ).store_async(parent=parent_folder) + ).store_async(parent=parent_folder, synapse_client=self.syn) self.schedule_for_cleanup(subfolder.id) # AND the user that created the folders @@ -345,13 +377,18 @@ async def test_get_acl_for_subfolder_with_different_permissions( await subfolder.set_permissions_async( principal_id=user.id, access_type=limited_permissions, + synapse_client=self.syn, ) # WHEN getting permissions for the subfolder - subfolder_permissions = await subfolder.get_acl_async(principal_id=user.id) + subfolder_permissions = await subfolder.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) # AND getting permissions for the parent folder - parent_permissions = await parent_folder.get_acl_async(principal_id=user.id) + parent_permissions = await parent_folder.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) # THEN the subfolder should have the limited permissions assert set(limited_permissions) == set(subfolder_permissions) @@ -390,24 +427,30 @@ async def test_remove_team_permissions_with_empty_access_type( await entity.set_permissions_async( principal_id=team.id, access_type=initial_team_permissions, + synapse_client=self.syn, ) # WHEN verifying the team has the initial permissions - team_acl_before = await entity.get_acl_async(principal_id=team.id) + team_acl_before = await entity.get_acl_async( + principal_id=team.id, synapse_client=self.syn + ) assert set(initial_team_permissions) == set(team_acl_before) # AND WHEN removing the team's permissions by setting access_type to empty list await entity.set_permissions_async( principal_id=team.id, access_type=[], # Empty list to remove permissions + synapse_client=self.syn, ) # THEN the team should have no permissions - team_acl_after = await entity.get_acl_async(principal_id=team.id) + team_acl_after = await entity.get_acl_async( + principal_id=team.id, synapse_client=self.syn + ) assert team_acl_after == [] # AND the team should not appear in the full ACL list with any permissions - all_acls = await entity.list_acl_async() + all_acls = await entity.list_acl_async(synapse_client=self.syn) team_acl_entries = [ acl for acl in all_acls.entity_acl @@ -419,7 +462,9 @@ async def test_remove_team_permissions_with_empty_access_type( # AND other entities should still maintain their permissions (verify no side effects) user = await UserProfile().get_async(synapse_client=self.syn) - user_acl = await entity.get_acl_async(principal_id=user.id) + user_acl = await entity.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) assert len(user_acl) > 0, "User permissions should remain intact" async def test_table_permissions(self, project_model: Project) -> None: @@ -434,7 +479,7 @@ async def test_table_permissions(self, project_model: Project) -> None: columns=columns, parent_id=project_model.id, ) - table = await table.store_async() + table = await table.store_async(synapse_client=self.syn) self.schedule_for_cleanup(table.id) # AND a test team @@ -447,27 +492,35 @@ async def test_table_permissions(self, project_model: Project) -> None: await table.set_permissions_async( principal_id=team.id, access_type=team_permissions, + synapse_client=self.syn, ) # Set authenticated users permissions await table.set_permissions_async( principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) await asyncio.sleep(random.randint(1, 5)) # THEN listing permissions should show all set permissions # Check team permissions - team_acl = await table.get_acl_async(principal_id=team.id) + team_acl = await table.get_acl_async( + principal_id=team.id, synapse_client=self.syn + ) assert set(team_permissions) == set(team_acl) # Check authenticated users permissions - auth_acl = await table.get_acl_async(principal_id=AUTHENTICATED_USERS) + auth_acl = await table.get_acl_async( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert set(["READ", "DOWNLOAD"]) == set(auth_acl) # Check user effective permissions (should include permissions from all sources) - user_effective_acl = await table.get_acl_async(principal_id=user.id) + user_effective_acl = await table.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) expected_user_permissions = { "READ", "DELETE", @@ -481,21 +534,27 @@ async def test_table_permissions(self, project_model: Project) -> None: assert expected_user_permissions.issubset(set(user_effective_acl)) # AND listing all ACLs should return complete ACL information - all_acls = await table.list_acl_async() + all_acls = await table.list_acl_async(synapse_client=self.syn) assert isinstance(all_acls, AclListResult) assert len(all_acls.entity_acl) >= 3 # User, team, authenticated_users # WHEN deleting specific permissions for the team - await table.set_permissions_async(principal_id=team.id, access_type=[]) + await table.set_permissions_async( + principal_id=team.id, access_type=[], synapse_client=self.syn + ) await asyncio.sleep(random.randint(1, 5)) # THEN team should no longer have permissions - team_acl_after_delete = await table.get_acl_async(principal_id=team.id) + team_acl_after_delete = await table.get_acl_async( + principal_id=team.id, synapse_client=self.syn + ) assert team_acl_after_delete == [] # BUT other permissions should remain - auth_acl_after = await table.get_acl_async(principal_id=AUTHENTICATED_USERS) + auth_acl_after = await table.get_acl_async( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert set(["READ", "DOWNLOAD"]) == set(auth_acl_after) async def test_entity_view_permissions(self, project_model: Project) -> None: @@ -507,7 +566,7 @@ async def test_entity_view_permissions(self, project_model: Project) -> None: scope_ids=[project_model.id], view_type_mask=0x01, # File view ) - entity_view = await entity_view.store_async() + entity_view = await entity_view.store_async(synapse_client=self.syn) self.schedule_for_cleanup(entity_view.id) # AND test subjects @@ -520,12 +579,14 @@ async def test_entity_view_permissions(self, project_model: Project) -> None: await entity_view.set_permissions_async( principal_id=team.id, access_type=team_permissions, + synapse_client=self.syn, ) # Set authenticated users permissions await entity_view.set_permissions_async( principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # Set limited user permissions (remove some admin permissions) @@ -540,50 +601,61 @@ async def test_entity_view_permissions(self, project_model: Project) -> None: await entity_view.set_permissions_async( principal_id=user.id, access_type=limited_user_permissions, + synapse_client=self.syn, ) await asyncio.sleep(random.randint(1, 5)) # THEN listing permissions should reflect all changes # Verify team permissions - team_acl = await entity_view.get_acl_async(principal_id=team.id) + team_acl = await entity_view.get_acl_async( + principal_id=team.id, synapse_client=self.syn + ) assert set(team_permissions) == set(team_acl) # Verify authenticated users permissions - auth_acl = await entity_view.get_acl_async(principal_id=AUTHENTICATED_USERS) + auth_acl = await entity_view.get_acl_async( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert set(["READ", "DOWNLOAD"]) == set(auth_acl) # Verify user permissions include both direct and inherited permissions - user_acl = await entity_view.get_acl_async(principal_id=user.id) + user_acl = await entity_view.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) expected_user_permissions = set( limited_user_permissions + ["DOWNLOAD"] ) # Includes auth users perm assert expected_user_permissions == set(user_acl) # Verify complete ACL listing - all_acls = await entity_view.list_acl_async() + all_acls = await entity_view.list_acl_async(synapse_client=self.syn) assert isinstance(all_acls, AclListResult) assert len(all_acls.entity_acl) >= 3 # User, team, authenticated_users # WHEN deleting authenticated users permissions await entity_view.set_permissions_async( - principal_id=AUTHENTICATED_USERS, access_type=[] + principal_id=AUTHENTICATED_USERS, access_type=[], synapse_client=self.syn ) await asyncio.sleep(random.randint(1, 5)) # THEN authenticated users should lose permissions auth_acl_after = await entity_view.get_acl_async( - principal_id=AUTHENTICATED_USERS + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn ) assert auth_acl_after == [] # AND user permissions should no longer include DOWNLOAD - user_acl_after = await entity_view.get_acl_async(principal_id=user.id) + user_acl_after = await entity_view.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) assert set(limited_user_permissions) == set(user_acl_after) # BUT team permissions should remain - team_acl_after = await entity_view.get_acl_async(principal_id=team.id) + team_acl_after = await entity_view.get_acl_async( + principal_id=team.id, synapse_client=self.syn + ) assert set(team_permissions) == set(team_acl_after) async def test_submission_view_permissions(self, project_model: Project) -> None: @@ -594,8 +666,8 @@ async def test_submission_view_permissions(self, project_model: Project) -> None parent_id=project_model.id, scope_ids=[project_model.id], ) - submission_view = await submission_view.store_async() - self.schedule_for_cleanup(submission_view.id) + submission_view = await submission_view.store_async(synapse_client=self.syn) + self.schedule_for_cleanup(submission_view) # AND test subjects team1 = await self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - team1") @@ -608,6 +680,7 @@ async def test_submission_view_permissions(self, project_model: Project) -> None await submission_view.set_permissions_async( principal_id=team1.id, access_type=team1_permissions, + synapse_client=self.syn, ) # Team2 gets read-only access with download @@ -615,12 +688,14 @@ async def test_submission_view_permissions(self, project_model: Project) -> None await submission_view.set_permissions_async( principal_id=team2.id, access_type=team2_permissions, + synapse_client=self.syn, ) # Public gets read access await submission_view.set_permissions_async( principal_id=PUBLIC, access_type=["READ"], + synapse_client=self.syn, ) # User gets minimal direct permissions @@ -628,58 +703,75 @@ async def test_submission_view_permissions(self, project_model: Project) -> None await submission_view.set_permissions_async( principal_id=user.id, access_type=user_direct_permissions, + synapse_client=self.syn, ) await asyncio.sleep(random.randint(1, 5)) # THEN listing permissions should show proper aggregation # Check individual team permissions - team1_acl = await submission_view.get_acl_async(principal_id=team1.id) + team1_acl = await submission_view.get_acl_async( + principal_id=team1.id, synapse_client=self.syn + ) assert set(team1_permissions) == set(team1_acl) - team2_acl = await submission_view.get_acl_async(principal_id=team2.id) + team2_acl = await submission_view.get_acl_async( + principal_id=team2.id, synapse_client=self.syn + ) assert set(team2_permissions) == set(team2_acl) # Check public permissions - public_acl = await submission_view.get_acl_async() + public_acl = await submission_view.get_acl_async(synapse_client=self.syn) assert set(["READ"]) == set(public_acl) # Check user effective permissions (should aggregate from all teams) - user_effective_acl = await submission_view.get_acl_async(principal_id=user.id) + user_effective_acl = await submission_view.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) expected_effective = set( user_direct_permissions + team1_permissions + team2_permissions ) assert expected_effective == set(user_effective_acl) # Verify complete ACL structure - all_acls = await submission_view.list_acl_async() + all_acls = await submission_view.list_acl_async(synapse_client=self.syn) assert isinstance(all_acls, AclListResult) assert len(all_acls.entity_acl) >= 4 # User, team1, team2, public # WHEN selectively deleting permissions # Remove PUBLIC and team permissions - await submission_view.set_permissions_async(principal_id=PUBLIC, access_type=[]) await submission_view.set_permissions_async( - principal_id=team1.id, access_type=[] + principal_id=PUBLIC, access_type=[], synapse_client=self.syn + ) + await submission_view.set_permissions_async( + principal_id=team1.id, access_type=[], synapse_client=self.syn ) await asyncio.sleep(random.randint(1, 5)) # THEN PUBLIC should lose all permissions - public_acl_after = await submission_view.get_acl_async(principal_id=PUBLIC) + public_acl_after = await submission_view.get_acl_async( + principal_id=PUBLIC, synapse_client=self.syn + ) assert public_acl_after == [] # AND team should lose all permissions - team_acl_after = await submission_view.get_acl_async(principal_id=team1.id) + team_acl_after = await submission_view.get_acl_async( + principal_id=team1.id, synapse_client=self.syn + ) assert team_acl_after == [] # AND user effective permissions should no longer include team1 permissions - user_effective_after = await submission_view.get_acl_async(principal_id=user.id) + user_effective_after = await submission_view.get_acl_async( + principal_id=user.id, synapse_client=self.syn + ) expected_after = set(user_direct_permissions + team2_permissions) assert expected_after == set(user_effective_after) # BUT other permissions should remain unchanged - team2_acl_after = await submission_view.get_acl_async(principal_id=team2.id) + team2_acl_after = await submission_view.get_acl_async( + principal_id=team2.id, synapse_client=self.syn + ) assert set(team2_permissions) == set(team2_acl_after) @@ -694,7 +786,9 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: async def create_team(self, description: str = DESCRIPTION_FAKE_TEAM) -> Team: """Helper to create a team with cleanup handling""" name = TEAM_PREFIX + str(uuid.uuid4()) - team = await Team(name=name, description=description).create_async() + team = await Team(name=name, description=description).create_async( + synapse_client=self.syn + ) self.schedule_for_cleanup(team) return team @@ -702,11 +796,11 @@ async def test_get_permissions_default(self) -> None: # GIVEN a project created with default permissions project = await Project( name=str(uuid.uuid4()) + "_test_get_permissions_default" - ).store_async() + ).store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # WHEN getting the permissions for the current user - permissions = await project.get_permissions_async() + permissions = await project.get_permissions_async(synapse_client=self.syn) # THEN all default permissions should be present expected_permissions = [ @@ -725,17 +819,19 @@ async def test_get_permissions_with_limited_access(self) -> None: # GIVEN a project created with default permissions project = await Project( name=str(uuid.uuid4()) + "_test_get_permissions_limited" - ).store_async() + ).store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND the current user that created the project user = await UserProfile().get_async(synapse_client=self.syn) # AND the permissions for the user are set to READ only - await project.set_permissions_async(principal_id=user.id, access_type=["READ"]) + await project.set_permissions_async( + principal_id=user.id, access_type=["READ"], synapse_client=self.syn + ) # WHEN getting the permissions for the current user - permissions = await project.get_permissions_async() + permissions = await project.get_permissions_async(synapse_client=self.syn) # THEN READ and CHANGE_SETTINGS permissions should be present # Note: CHANGE_SETTINGS is bound to ownerId and can't be removed @@ -750,7 +846,7 @@ async def test_get_permissions_through_teams(self) -> None: # AND a project created with default permissions project = await Project( name=str(uuid.uuid4()) + "_test_get_permissions_through_teams" - ).store_async() + ).store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND the current user that created the project @@ -769,18 +865,23 @@ async def test_get_permissions_through_teams(self) -> None: await project.set_permissions_async( principal_id=team_1.id, access_type=team_1_permissions, + synapse_client=self.syn, ) # AND team 2 has only READ and DOWNLOAD permissions await project.set_permissions_async( - principal_id=team_2.id, access_type=["READ", "DOWNLOAD"] + principal_id=team_2.id, + access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # AND the user has no direct permissions - await project.set_permissions_async(principal_id=user.id, access_type=[]) + await project.set_permissions_async( + principal_id=user.id, access_type=[], synapse_client=self.syn + ) # WHEN getting the permissions for the current user - permissions = await project.get_permissions_async() + permissions = await project.get_permissions_async(synapse_client=self.syn) # THEN the effective permissions should be the combined permissions from both teams expected_permissions = [ @@ -799,7 +900,7 @@ async def test_get_permissions_with_authenticated_users(self) -> None: # GIVEN a project created with default permissions project = await Project( name=str(uuid.uuid4()) + "_test_get_permissions_authenticated" - ).store_async() + ).store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND the current user that created the project @@ -807,7 +908,9 @@ async def test_get_permissions_with_authenticated_users(self) -> None: # AND authenticated users have READ and DOWNLOAD permissions await project.set_permissions_async( - principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"] + principal_id=AUTHENTICATED_USERS, + access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # AND the current user has specific permissions (without DOWNLOAD) @@ -823,10 +926,11 @@ async def test_get_permissions_with_authenticated_users(self) -> None: await project.set_permissions_async( principal_id=user.id, access_type=user_permissions, + synapse_client=self.syn, ) # WHEN getting the permissions for the current user - permissions = await project.get_permissions_async() + permissions = await project.get_permissions_async(synapse_client=self.syn) # THEN the permissions should include user permissions plus # the DOWNLOAD permission from authenticated users @@ -874,11 +978,15 @@ async def _set_custom_permissions( """Helper to set custom permissions on an entity so we can verify deletion.""" # Set custom permissions for authenticated users await entity.set_permissions_async( - principal_id=AUTHENTICATED_USERS, access_type=["READ"] + principal_id=AUTHENTICATED_USERS, + access_type=["READ"], + synapse_client=self.syn, ) # Verify permissions were set - acl = await entity.get_acl_async(principal_id=AUTHENTICATED_USERS) + acl = await entity.get_acl_async( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert "READ" in acl return acl @@ -891,7 +999,9 @@ async def _verify_permissions_deleted( await asyncio.sleep(random.randint(1, 5)) acl = await entity.get_acl_async( - principal_id=AUTHENTICATED_USERS, check_benefactor=False + principal_id=AUTHENTICATED_USERS, + check_benefactor=False, + synapse_client=self.syn, ) if not acl: @@ -910,7 +1020,9 @@ async def _verify_permissions_not_deleted( for attempt in range(self.verification_attempts): await asyncio.sleep(random.randint(1, 5)) acl = await entity.get_acl_async( - principal_id=AUTHENTICATED_USERS, check_benefactor=False + principal_id=AUTHENTICATED_USERS, + check_benefactor=False, + synapse_client=self.syn, ) if "READ" in acl: return True @@ -1297,7 +1409,9 @@ async def test_delete_permissions_on_new_project( caplog.set_level(logging.DEBUG) # GIVEN a newly created project with custom permissions - project = await Project(name=f"test_project_{uuid.uuid4()}").store_async() + project = await Project(name=f"test_project_{uuid.uuid4()}").store_async( + synapse_client=self.syn + ) self.schedule_for_cleanup(project.id) # AND custom permissions are set for authenticated users @@ -1305,7 +1419,7 @@ async def test_delete_permissions_on_new_project( await asyncio.sleep(random.randint(1, 5)) # WHEN I delete permissions on the project - await project.delete_permissions_async() + await project.delete_permissions_async(synapse_client=self.syn) # THEN the permissions should not be deleted # Check either for the log message or verify the permissions still exist diff --git a/tests/integration/synapseclient/models/async/test_project_async.py b/tests/integration/synapseclient/models/async/test_project_async.py index fccaf57bf..9a8be2574 100644 --- a/tests/integration/synapseclient/models/async/test_project_async.py +++ b/tests/integration/synapseclient/models/async/test_project_async.py @@ -121,7 +121,7 @@ async def test_store_project_basic(self, project: Project) -> None: # GIVEN a Project object # WHEN I store the Project on Synapse - stored_project = await project.store_async() + stored_project = await project.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # THEN I expect the stored Project to have the expected properties @@ -142,7 +142,9 @@ async def test_store_project_basic(self, project: Project) -> None: project_with_annotations.annotations = annotations # WHEN I store the Project on Synapse - stored_project_with_annotations = await project_with_annotations.store_async() + stored_project_with_annotations = await project_with_annotations.store_async( + synapse_client=self.syn + ) self.schedule_for_cleanup(project_with_annotations.id) # THEN I expect the stored Project to have the expected properties and annotations @@ -160,7 +162,7 @@ async def test_store_project_with_files(self, file: File, project: Project) -> N project.files.append(file) # WHEN I store the Project on Synapse - stored_project = await project.store_async() + stored_project = await project.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # THEN I expect the stored Project to have the expected properties and files @@ -175,7 +177,9 @@ async def test_store_project_with_files(self, file: File, project: Project) -> N project_multiple_files.files = files # WHEN I store the Project on Synapse - stored_project_multiple_files = await project_multiple_files.store_async() + stored_project_multiple_files = await project_multiple_files.store_async( + synapse_client=self.syn + ) self.schedule_for_cleanup(project_multiple_files.id) # THEN I expect the stored Project to have the expected properties and files @@ -201,7 +205,7 @@ async def test_store_project_with_nested_structure( project.folders = folders # WHEN I store the Project on Synapse - stored_project = await project.store_async() + stored_project = await project.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # THEN I expect the stored Project to have the expected properties, files, and folders @@ -214,7 +218,7 @@ async def test_store_project_with_nested_structure( existing_project = Project( name=str(uuid.uuid4()), description=DESCRIPTION_PROJECT ) - existing_project = await existing_project.store_async() + existing_project = await existing_project.store_async(synapse_client=self.syn) self.schedule_for_cleanup(existing_project.id) # AND a Folder with a File under the project @@ -223,7 +227,9 @@ async def test_store_project_with_nested_structure( existing_project.folders.append(folder) # WHEN I store the Project on Synapse - stored_existing_project = await existing_project.store_async() + stored_existing_project = await existing_project.store_async( + synapse_client=self.syn + ) # THEN I expect the stored Project to have the expected properties self.verify_project_properties( @@ -272,7 +278,7 @@ def verify_project_properties(self, project: Project): async def test_get_project_methods(self, project: Project) -> None: # GIVEN a Project object stored in Synapse - stored_project = await project.store_async() + stored_project = await project.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # Test Case 1: Get project by ID @@ -295,11 +301,11 @@ async def test_get_project_methods(self, project: Project) -> None: async def test_delete_project(self, project: Project) -> None: # GIVEN a Project object stored in Synapse - stored_project = await project.store_async() + stored_project = await project.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # WHEN I delete the Project from Synapse - await stored_project.delete_async() + await stored_project.delete_async(synapse_client=self.syn) # THEN I expect the project to have been deleted with pytest.raises(SynapseHTTPError) as e: @@ -404,24 +410,26 @@ def verify_copied_project( async def test_copy_project_variations(self) -> None: # GIVEN a nested source project and a destination project source_project = self.create_nested_project() - stored_source_project = await source_project.store_async() + stored_source_project = await source_project.store_async( + synapse_client=self.syn + ) self.schedule_for_cleanup(stored_source_project.id) # Test Case 1: Copy project with all contents # Create first destination project destination_project_1 = await Project( name=str(uuid.uuid4()), description="Destination for project copy 1" - ).store_async() + ).store_async(synapse_client=self.syn) self.schedule_for_cleanup(destination_project_1.id) # WHEN I copy the project to the destination project copied_project = await stored_source_project.copy_async( - destination_id=destination_project_1.id + destination_id=destination_project_1.id, synapse_client=self.syn ) # AND I sync the destination project from Synapse await destination_project_1.sync_from_synapse_async( - recursive=False, download_file=False + recursive=False, download_file=False, synapse_client=self.syn ) # THEN I expect the copied Project to have the expected properties @@ -433,12 +441,14 @@ async def test_copy_project_variations(self) -> None: # Create a second destination project for the second test case destination_project_2 = await Project( name=str(uuid.uuid4()), description="Destination for project copy 2" - ).store_async() + ).store_async(synapse_client=self.syn) self.schedule_for_cleanup(destination_project_2.id) # WHEN I copy the project to the destination project excluding files copied_project_no_files = await stored_source_project.copy_async( - destination_id=destination_project_2.id, exclude_types=["file"] + destination_id=destination_project_2.id, + exclude_types=["file"], + synapse_client=self.syn, ) # THEN I expect the copied Project to have the expected properties but no files @@ -453,12 +463,12 @@ async def test_sync_from_synapse(self, file: File) -> None: project = self.create_nested_project() # WHEN I store the Project on Synapse - stored_project = await project.store_async() + stored_project = await project.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND I sync the project from Synapse copied_project = await stored_project.sync_from_synapse_async( - path=root_directory_path + path=root_directory_path, synapse_client=self.syn ) # THEN I expect that the project and its contents are synced from Synapse to disk @@ -569,7 +579,7 @@ async def test_sync_all_entity_types(self) -> None: # WHEN I sync the project from Synapse synced_project = await project_model.sync_from_synapse_async( - recursive=False, download_file=False + recursive=False, download_file=False, synapse_client=self.syn ) # THEN all entity types should be present @@ -626,24 +636,24 @@ async def create_test_hierarchy(self, project: Project) -> dict: root_folder = Folder( name=f"root_folder_{str(uuid.uuid4())[:8]}", parent_id=project.id ) - root_folder = await root_folder.store_async() + root_folder = await root_folder.store_async(synapse_client=self.syn) self.schedule_for_cleanup(root_folder.id) root_file = self.create_file_instance(self.schedule_for_cleanup) root_file.parent_id = project.id - root_file = await root_file.store_async() + root_file = await root_file.store_async(synapse_client=self.syn) self.schedule_for_cleanup(root_file.id) # Create nested folder and file nested_folder = Folder( name=f"nested_folder_{str(uuid.uuid4())[:8]}", parent_id=root_folder.id ) - nested_folder = await nested_folder.store_async() + nested_folder = await nested_folder.store_async(synapse_client=self.syn) self.schedule_for_cleanup(nested_folder.id) nested_file = self.create_file_instance(self.schedule_for_cleanup) nested_file.parent_id = nested_folder.id - nested_file = await nested_file.store_async() + nested_file = await nested_file.store_async(synapse_client=self.syn) self.schedule_for_cleanup(nested_file.id) return { @@ -661,13 +671,15 @@ async def test_walk_async_recursive_true(self) -> None: name=f"integration_test_project{str(uuid.uuid4())}", description=DESCRIPTION_PROJECT, ) - project_model = await project_model.store_async() + project_model = await project_model.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project_model.id) hierarchy = await self.create_test_hierarchy(project_model) # WHEN: Walking through the project asynchronously with recursive=True results = [] - async for result in project_model.walk_async(recursive=True): + async for result in project_model.walk_async( + recursive=True, synapse_client=self.syn + ): results.append(result) # THEN: Should get 3 results (project root, root_folder, nested_folder) @@ -708,13 +720,15 @@ async def test_walk_async_recursive_false(self) -> None: name=f"integration_test_project{str(uuid.uuid4())}", description=DESCRIPTION_PROJECT, ) - project_model = await project_model.store_async() + project_model = await project_model.store_async(synapse_client=self.syn) self.schedule_for_cleanup(project_model.id) hierarchy = await self.create_test_hierarchy(project_model) # WHEN: Walking through the project asynchronously with recursive=False results = [] - async for result in project_model.walk_async(recursive=False): + async for result in project_model.walk_async( + recursive=False, synapse_client=self.syn + ): results.append(result) # THEN: Should get only 1 result (project root only) diff --git a/tests/integration/synapseclient/models/async/test_schema_organization.py b/tests/integration/synapseclient/models/async/test_schema_organization_async.py similarity index 84% rename from tests/integration/synapseclient/models/async/test_schema_organization.py rename to tests/integration/synapseclient/models/async/test_schema_organization_async.py index 36cfc5e83..72481c7f0 100644 --- a/tests/integration/synapseclient/models/async/test_schema_organization.py +++ b/tests/integration/synapseclient/models/async/test_schema_organization_async.py @@ -1,4 +1,5 @@ """Integration tests for SchemaOrganization and JSONSchema classes""" +import asyncio import uuid from typing import Any, Optional @@ -10,7 +11,6 @@ from synapseclient.core.exceptions import SynapseHTTPError from synapseclient.models import JSONSchema, SchemaOrganization from synapseclient.models.schema_organization import ( - SYNAPSE_SCHEMA_URL, CreateSchemaRequest, JSONSchemaVersionInfo, list_json_schema_organizations, @@ -27,7 +27,7 @@ def create_test_entity_name(): return f"SYNPY.TEST.{random_string}" -async def org_exists(name: str, synapse_client: Optional[Synapse] = None) -> bool: +def org_exists(name: str, synapse_client: Optional[Synapse] = None) -> bool: """ Checks if any organizations exists with the given name @@ -57,7 +57,7 @@ def fixture_module_organization(syn: Synapse, request) -> SchemaOrganization: def delete_org(): for schema in org.get_json_schemas(synapse_client=syn): - schema.delete() + schema.delete(synapse_client=syn) org.delete(synapse_client=syn) request.addfinalizer(delete_org) @@ -83,10 +83,10 @@ async def fixture_organization(syn: Synapse, request) -> SchemaOrganization: name = create_test_entity_name() org = SchemaOrganization(name) - async def delete_org(): - exists = await org_exists(name, syn) + def delete_org(): + exists = org_exists(name, syn) if exists: - org.delete() + org.delete(synapse_client=syn) request.addfinalizer(delete_org) @@ -96,25 +96,25 @@ async def delete_org(): @pytest_asyncio.fixture( name="organization_with_schema", loop_scope="function", scope="function" ) -async def fixture_organization_with_schema(request) -> SchemaOrganization: +async def fixture_organization_with_schema(syn: Synapse, request) -> SchemaOrganization: """ Returns a Synapse organization. As Cleanup it checks for JSON Schemas and deletes them""" name = create_test_entity_name() org = SchemaOrganization(name) - org.store() + await org.store_async(synapse_client=syn) js1 = JSONSchema("schema1", name) js2 = JSONSchema("schema2", name) js3 = JSONSchema("schema3", name) - js1.store({}) - js2.store({}) - js3.store({}) + await js1.store_async(schema_body={}, synapse_client=syn) + await js2.store_async(schema_body={}, synapse_client=syn) + await js3.store_async(schema_body={}, synapse_client=syn) def delete_org(): - for schema in org.get_json_schemas(): - schema.delete() - org.delete() + for schema in org.get_json_schemas(synapse_client=syn): + schema.delete(synapse_client=syn) + org.delete(synapse_client=syn) request.addfinalizer(delete_org) @@ -136,16 +136,17 @@ async def test_create_and_get(self, organization: SchemaOrganization) -> None: assert organization.created_by is None assert organization.created_on is None # AND it shouldn't exist in Synapse - exists = await org_exists(organization.name, synapse_client=self.syn) + exists = org_exists(organization.name, synapse_client=self.syn) assert not exists # WHEN I store the organization the metadata will be saved await organization.store_async(synapse_client=self.syn) + await asyncio.sleep(10) assert organization.name is not None assert organization.id is not None assert organization.created_by is not None assert organization.created_on is not None # AND it should exist in Synapse - exists = await org_exists(organization.name, synapse_client=self.syn) + exists = org_exists(organization.name, synapse_client=self.syn) assert exists # AND it should be getable by future instances with the same name org2 = SchemaOrganization(organization.name) @@ -157,7 +158,7 @@ async def test_create_and_get(self, organization: SchemaOrganization) -> None: # WHEN I try to store an organization that exists in Synapse # THEN I should get an exception with pytest.raises(SynapseHTTPError): - org2.store() + await org2.store_async(synapse_client=self.syn) async def test_get_json_schemas_async( self, @@ -166,6 +167,7 @@ async def test_get_json_schemas_async( ) -> None: # GIVEN an organization with no schemas and one with 3 schemas await organization.store_async(synapse_client=self.syn) + await asyncio.sleep(10) # THEN get_json_schema_list should return the correct list of schemas schema_list = [] async for item in organization.get_json_schemas_async(synapse_client=self.syn): @@ -189,6 +191,7 @@ async def test_get_acl_and_update_acl( await organization.get_acl_async(synapse_client=self.syn) # GIVEN an organization that has been created await organization.store_async(synapse_client=self.syn) + await asyncio.sleep(10) acl = await organization.get_acl_async(synapse_client=self.syn) resource_access: list[dict[str, Any]] = acl["resourceAccess"] # THEN the resource access should be have one principal @@ -242,41 +245,49 @@ async def test_store_and_get(self, json_schema: JSONSchema) -> None: async def test_delete(self, organization_with_schema: SchemaOrganization) -> None: # GIVEN an organization with 3 schema schemas: list[JSONSchema] = [] - async for item in organization_with_schema.get_json_schemas_async(): + async for item in organization_with_schema.get_json_schemas_async( + synapse_client=self.syn + ): schemas.append(item) assert len(schemas) == 3 # WHEN deleting one of those schemas schema = schemas[0] - await schema.delete_async() + await schema.delete_async(synapse_client=self.syn) # THEN there should be only two left schemas2: list[JSONSchema] = [] - async for item in organization_with_schema.get_json_schemas_async(): + async for item in organization_with_schema.get_json_schemas_async( + synapse_client=self.syn + ): schemas2.append(item) assert len(schemas2) == 2 async def test_delete_version(self, json_schema: JSONSchema) -> None: # GIVEN an organization and a JSONSchema - await json_schema.store_async(schema_body={}, version="0.0.1") + await json_schema.store_async( + schema_body={}, version="0.0.1", synapse_client=self.syn + ) # THEN that schema should have one version js_versions: list[JSONSchemaVersionInfo] = [] - async for item in json_schema.get_versions_async(): + async for item in json_schema.get_versions_async(synapse_client=self.syn): js_versions.append(item) assert len(js_versions) == 1 # WHEN storing a second version - await json_schema.store_async(schema_body={}, version="0.0.2") + await json_schema.store_async( + schema_body={}, version="0.0.2", synapse_client=self.syn + ) # THEN that schema should have two versions js_versions = [] - async for item in json_schema.get_versions_async(): + async for item in json_schema.get_versions_async(synapse_client=self.syn): js_versions.append(item) assert len(js_versions) == 2 # AND they should be the ones stored versions = [js_version.semantic_version for js_version in js_versions] assert versions == ["0.0.1", "0.0.2"] # WHEN deleting the first schema version - await json_schema.delete_async(version="0.0.1") + await json_schema.delete_async(version="0.0.1", synapse_client=self.syn) # THEN there should only be one version left js_versions = [] - async for item in json_schema.get_versions_async(): + async for item in json_schema.get_versions_async(synapse_client=self.syn): js_versions.append(item) assert len(js_versions) == 1 # AND it should be the second version @@ -355,11 +366,14 @@ async def test_create_schema_request_no_version( # WHEN creating a CreateSchemaRequest with no version given schema_name = create_test_entity_name() request = CreateSchemaRequest( - schema={}, name=schema_name, organization_name=module_organization.name + schema={}, + name=schema_name, + organization_name=module_organization.name, + synapse_schema_url=f"{self.syn.repoEndpoint}/schema/type/registered/", ) # THEN id in schema will not include version assert request.schema == { - "$id": f"{SYNAPSE_SCHEMA_URL}{module_organization.name}-{schema_name}" + "$id": f"{request.synapse_schema_url}{module_organization.name}-{schema_name}" } assert request.name == schema_name assert request.organization_name == module_organization.name @@ -371,12 +385,15 @@ async def test_create_schema_request_no_version( assert request.uri == f"{module_organization.name}-{schema_name}" assert ( request.id - == f"{SYNAPSE_SCHEMA_URL}{module_organization.name}-{schema_name}" + == f"{request.synapse_schema_url}{module_organization.name}-{schema_name}" ) assert not request.new_version_info # THEN the Schema should not be part of the organization yet assert request.uri not in [ - schema.uri for schema in module_organization.get_json_schemas() + schema.uri + async for schema in module_organization.get_json_schemas_async( + synapse_client=self.syn + ) ] # WHEN sending the CreateSchemaRequest @@ -401,10 +418,11 @@ async def test_create_schema_request_with_version( name=schema_name, organization_name=module_organization.name, version=version, + synapse_schema_url=f"{self.syn.repoEndpoint}/schema/type/registered/", ) # THEN id in schema will include version assert request.schema == { - "$id": f"{SYNAPSE_SCHEMA_URL}{module_organization.name}-{schema_name}-{version}" + "$id": f"{request.synapse_schema_url}{module_organization.name}-{schema_name}-{version}" } assert request.name == schema_name assert request.organization_name == module_organization.name @@ -416,12 +434,15 @@ async def test_create_schema_request_with_version( assert request.uri == f"{module_organization.name}-{schema_name}-{version}" assert ( request.id - == f"{SYNAPSE_SCHEMA_URL}{module_organization.name}-{schema_name}-{version}" + == f"{request.synapse_schema_url}{module_organization.name}-{schema_name}-{version}" ) assert not request.new_version_info # THEN the Schema should not be part of the organization yet assert f"{module_organization.name}-{schema_name}" not in [ - schema.uri for schema in module_organization.get_json_schemas() + schema.uri + async for schema in module_organization.get_json_schemas_async( + synapse_client=self.syn + ) ] # WHEN sending the CreateSchemaRequest @@ -432,12 +453,15 @@ async def test_create_schema_request_with_version( # THEN the Schema (minus version) should be part of the organization yet schemas = [ schema - for schema in module_organization.get_json_schemas() + async for schema in module_organization.get_json_schemas_async( + synapse_client=self.syn + ) if schema.uri == f"{module_organization.name}-{schema_name}" ] assert len(schemas) == 1 schema = schemas[0] # AND schema version should have matching full uri assert completed_request.uri in [ - version.id for version in schema.get_versions() + version.id + async for version in schema.get_versions_async(synapse_client=self.syn) ] diff --git a/tests/integration/synapseclient/models/async/test_submissionview_async.py b/tests/integration/synapseclient/models/async/test_submissionview_async.py index ac037ef6a..ce15bd0ec 100644 --- a/tests/integration/synapseclient/models/async/test_submissionview_async.py +++ b/tests/integration/synapseclient/models/async/test_submissionview_async.py @@ -32,7 +32,7 @@ async def test_create_submissionview_with_columns( ) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope - evaluation = self.syn.store( + evaluation = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation for submission view", @@ -54,7 +54,7 @@ async def test_create_submissionview_with_columns( # WHEN I store the submissionview submissionview = await submissionview.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # THEN the submissionview should be created assert submissionview.id is not None @@ -86,7 +86,7 @@ async def test_create_submissionview_with_columns( # WHEN I store the submissionview submissionview2 = await submissionview2.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview2.id) + self.schedule_for_cleanup(submissionview2) # THEN the submissionview should be created assert submissionview2.id is not None @@ -122,7 +122,7 @@ async def test_create_submissionview_with_columns( # WHEN I store the submissionview submissionview3 = await submissionview3.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview3.id) + self.schedule_for_cleanup(submissionview3) # THEN I can retrieve that submissionview with both columns new_submissionview_instance3 = await SubmissionView( @@ -144,7 +144,7 @@ async def test_create_submissionview_special_cases( ) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope - evaluation = self.syn.store( + evaluation = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation for submission view", @@ -191,7 +191,7 @@ async def test_create_submissionview_special_cases( # WHEN I store the submissionview submissionview2 = await submissionview2.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview2.id) + self.schedule_for_cleanup(submissionview2) # THEN the submissionview should be created but with empty scope retrieved_view = await SubmissionView(id=submissionview2.id).get_async( @@ -218,7 +218,7 @@ async def test_create_submissionview_special_cases( # WHEN I store the submissionview submissionview3 = await submissionview3.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview3.id) + self.schedule_for_cleanup(submissionview3) # THEN the submissionview should only contain our custom columns retrieved_view3 = await SubmissionView(id=submissionview3.id).get_async( @@ -241,7 +241,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: async def test_column_modifications(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope - evaluation = self.syn.store( + evaluation = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation for submission view", @@ -264,7 +264,7 @@ async def test_column_modifications(self, project_model: Project) -> None: scope_ids=[evaluation.id], ) submissionview = await submissionview.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # Test Case 1: Rename column # WHEN I rename the column @@ -306,7 +306,7 @@ async def test_column_modifications(self, project_model: Project) -> None: async def test_scope_modifications(self, project_model: Project) -> None: # GIVEN a project to work with # AND two evaluations for testing scope changes - evaluation1 = self.syn.store( + evaluation1 = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation 1", @@ -315,7 +315,7 @@ async def test_scope_modifications(self, project_model: Project) -> None: ) self.schedule_for_cleanup(evaluation1) - evaluation2 = self.syn.store( + evaluation2 = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation 2", @@ -332,7 +332,7 @@ async def test_scope_modifications(self, project_model: Project) -> None: scope_ids=[evaluation1.id], ) submissionview = await submissionview.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # Test Case 1: Update scope to include multiple evaluations # WHEN I update the scope to include both evaluations @@ -380,7 +380,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: async def test_query_submissionview(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope - evaluation = self.syn.store( + evaluation = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation for submission view", @@ -441,7 +441,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: async def test_submissionview_snapshots(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope - evaluation = self.syn.store( + evaluation = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation for submission view", @@ -467,7 +467,7 @@ async def test_submissionview_snapshots(self, project_model: Project) -> None: # AND the submissionview is stored in Synapse submissionview = await submissionview.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # WHEN I snapshot the submissionview snapshot = await submissionview.snapshot_async( @@ -539,7 +539,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: async def test_submission_lifecycle(self, project_model: Project) -> None: """Test submission lifecycle in a submission view: adding and removing submissions.""" # GIVEN an evaluation - evaluation = self.syn.store( + evaluation = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation for submission view", @@ -556,7 +556,7 @@ async def test_submission_lifecycle(self, project_model: Project) -> None: scope_ids=[evaluation.id], ) submissionview = await submissionview.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # AND a file for submission with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f: @@ -625,7 +625,7 @@ async def test_submission_lifecycle(self, project_model: Project) -> None: async def test_multiple_submissions(self, project_model: Project) -> None: """Test that multiple submissions to an evaluation appear in a submission view.""" # GIVEN an evaluation - evaluation = self.syn.store( + evaluation = await self.syn.store_async( Evaluation( name=str(uuid.uuid4()), description="Test evaluation for multiple submissions", @@ -642,7 +642,7 @@ async def test_multiple_submissions(self, project_model: Project) -> None: scope_ids=[evaluation.id], ) submissionview = await submissionview.store_async(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # WHEN I create and upload multiple test files for submission files = [] diff --git a/tests/integration/synapseclient/models/async/test_table_async.py b/tests/integration/synapseclient/models/async/test_table_async.py index 83624cf05..1f44571f2 100644 --- a/tests/integration/synapseclient/models/async/test_table_async.py +++ b/tests/integration/synapseclient/models/async/test_table_async.py @@ -1343,7 +1343,7 @@ async def test_upsert_all_data_types(self, project_model: Project) -> None: contentSource=project_model.id, ) # TODO: When Evaluation and Submission are implemented with Async methods update this test - evaluation = self.syn.store(evaluation) + evaluation = await self.syn.store_async(evaluation) try: submission = self.syn.submit( evaluation, file.id, name="Submission 1", submitterAlias="My Team" diff --git a/tests/integration/synapseclient/models/async/test_team_async.py b/tests/integration/synapseclient/models/async/test_team_async.py index 3fdf03565..7398f3347 100644 --- a/tests/integration/synapseclient/models/async/test_team_async.py +++ b/tests/integration/synapseclient/models/async/test_team_async.py @@ -45,7 +45,7 @@ async def test_team_lifecycle(self) -> None: # GIVEN a team object # WHEN I create the team on Synapse - test_team = await self.team.create_async() + test_team = await self.team.create_async(synapse_client=self.syn) # THEN I expect the created team to be returned with correct properties assert test_team.id is not None @@ -68,7 +68,9 @@ async def test_team_lifecycle(self) -> None: await self.verify_team_properties(id_team, test_team) # WHEN I retrieve the team using the static from_id method - from_id_team = await Team.from_id_async(id=test_team.id) + from_id_team = await Team.from_id_async( + id=test_team.id, synapse_client=self.syn + ) # THEN the retrieved team should match the created team await self.verify_team_properties(from_id_team, test_team) @@ -84,30 +86,32 @@ async def test_team_lifecycle(self) -> None: await self.verify_team_properties(name_team, test_team) # WHEN I retrieve the team using the static from_name method - from_name_team = await Team.from_name_async(name=test_team.name) + from_name_team = await Team.from_name_async( + name=test_team.name, synapse_client=self.syn + ) # THEN the retrieved team should match the created team await self.verify_team_properties(from_name_team, test_team) # WHEN I delete the team - await test_team.delete_async() + await test_team.delete_async(synapse_client=self.syn) # THEN the team should no longer exist with pytest.raises( SynapseHTTPError, match=f"404 Client Error: Team id: '{test_team.id}' does not exist", ): - await Team.from_id_async(id=test_team.id) + await Team.from_id_async(id=test_team.id, synapse_client=self.syn) async def test_team_membership_and_invitations(self) -> None: """Test team membership and invitation functionality""" # GIVEN a team object # WHEN I create the team on Synapse - test_team = await self.team.create_async() + test_team = await self.team.create_async(synapse_client=self.syn) # AND check the team members - members = await test_team.members_async() + members = await test_team.members_async(synapse_client=self.syn) # THEN the team should have exactly one member (the creator), who is an admin assert len(members) == 1 @@ -117,8 +121,7 @@ async def test_team_membership_and_invitations(self) -> None: # WHEN I invite a user to the team invite = await test_team.invite_async( - user=self.TEST_USER, - message=self.TEST_MESSAGE, + user=self.TEST_USER, message=self.TEST_MESSAGE, synapse_client=self.syn ) # THEN the invite should be created successfully @@ -130,7 +133,7 @@ async def test_team_membership_and_invitations(self) -> None: assert invite["createdBy"] is not None # WHEN I check the open invitations - invitations = await test_team.open_invitations_async() + invitations = await test_team.open_invitations_async(synapse_client=self.syn) # THEN I should see the invitation I just created assert len(invitations) == 1 @@ -142,17 +145,17 @@ async def test_team_membership_and_invitations(self) -> None: assert invitations[0]["createdBy"] is not None # Clean up - await test_team.delete_async() + await test_team.delete_async(synapse_client=self.syn) async def test_get_user_membership_status(self) -> None: """Test getting user membership status for a team""" # WHEN I create the team on Synapse - test_team = await self.team.create_async() + test_team = await self.team.create_async(synapse_client=self.syn) try: # AND I get the membership status for the creator (who should be a member) creator_status = await test_team.get_user_membership_status_async( - user_id=self.syn.getUserProfile().ownerId + user_id=self.syn.getUserProfile().ownerId, synapse_client=self.syn ) # THEN the creator should have membership status indicating they are a member @@ -166,14 +169,16 @@ async def test_get_user_membership_status(self) -> None: assert creator_status.can_send_email is True # WHEN I invite a test user to the team - invite = await test_team.invite_async( + await test_team.invite_async( user=self.TEST_USER, message=self.TEST_MESSAGE, + synapse_client=self.syn, ) # Check the invited user's status invited_status = await test_team.get_user_membership_status_async( - user_id=self.syn.getUserProfile(self.TEST_USER).ownerId + user_id=self.syn.getUserProfile(self.TEST_USER).ownerId, + synapse_client=self.syn, ) # THEN the invited user should show they have an open invitation @@ -186,4 +191,4 @@ async def test_get_user_membership_status(self) -> None: finally: # Clean up - await test_team.delete_async() + await test_team.delete_async(synapse_client=self.syn) diff --git a/tests/integration/synapseclient/models/async/test_user_async.py b/tests/integration/synapseclient/models/async/test_user_async.py index 99ac59afe..3a8f65f53 100644 --- a/tests/integration/synapseclient/models/async/test_user_async.py +++ b/tests/integration/synapseclient/models/async/test_user_async.py @@ -23,7 +23,9 @@ async def test_from_id(self) -> None: ) # WHEN we get the profile by ID - profile = await UserProfile.from_id_async(integration_test_profile.id) + profile = await UserProfile.from_id_async( + integration_test_profile.id, synapse_client=self.syn + ) # THEN we expect the profile to be the same as the one we got from the fixture assert profile == integration_test_profile @@ -36,7 +38,7 @@ async def test_from_username(self) -> None: # WHEN we get the profile by username profile = await UserProfile.from_username_async( - integration_test_profile.username + integration_test_profile.username, synapse_client=self.syn ) # THEN we expect the profile to be the same as the one we got from the fixture @@ -52,7 +54,7 @@ async def test_is_certified_id(self) -> None: profile_copy = UserProfile(id=integration_test_profile.id) # WHEN we check if the profile is certified - is_certified = await profile_copy.is_certified_async() + is_certified = await profile_copy.is_certified_async(synapse_client=self.syn) # THEN we expect the profile to not be certified assert is_certified is False @@ -67,7 +69,7 @@ async def test_is_certified_username(self) -> None: profile_copy = UserProfile(username=integration_test_profile.username) # WHEN we check if the profile is certified - is_certified = await profile_copy.is_certified_async() + is_certified = await profile_copy.is_certified_async(synapse_client=self.syn) # THEN we expect the profile to not be certified assert is_certified is False diff --git a/tests/integration/synapseclient/models/synchronous/test_activity.py b/tests/integration/synapseclient/models/synchronous/test_activity.py index 4e47060fa..4216f1377 100644 --- a/tests/integration/synapseclient/models/synchronous/test_activity.py +++ b/tests/integration/synapseclient/models/synchronous/test_activity.py @@ -1,6 +1,6 @@ """Integration tests for Activity.""" -import asyncio +import time import uuid from typing import Callable @@ -22,7 +22,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def create_file_with_activity( + def create_file_with_activity( self, project: Synapse_Project, activity: Activity = None, @@ -39,12 +39,12 @@ async def create_file_with_activity( self.schedule_for_cleanup(file.path) if store_file: - file.store() + file.store(synapse_client=self.syn) self.schedule_for_cleanup(file.id) return file - async def verify_activity_properties( + def verify_activity_properties( self, activity, expected_name, expected_description, has_references=False ): """Helper to verify common activity properties""" @@ -77,10 +77,10 @@ async def verify_activity_properties( assert activity.used == [] assert activity.executed == [] - async def test_activity_lifecycle(self, project: Synapse_Project) -> None: + def test_activity_lifecycle(self, project: Synapse_Project) -> None: """Test complete activity lifecycle - create, update, retrieve, and delete""" # GIVEN a file in a project - file = await self.create_file_with_activity(project) + file = self.create_file_with_activity(project) # AND an activity with references activity = Activity( @@ -97,12 +97,12 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: ) # WHEN I store the activity - result = activity.store(parent=file) + result = activity.store(parent=file, synapse_client=self.syn) self.schedule_for_cleanup(result.id) # THEN I expect the activity to be stored correctly assert result == activity - await self.verify_activity_properties( + self.verify_activity_properties( result, activity.name, "some_description", has_references=True ) @@ -110,10 +110,10 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: modified_name = f"modified_name_{str(uuid.uuid4())}" result.name = modified_name result.description = "modified_description" - modified_result = result.store() + modified_result = result.store(synapse_client=self.syn) # THEN I expect the modified activity to be stored - await self.verify_activity_properties( + self.verify_activity_properties( modified_result, modified_name, "modified_description", @@ -121,12 +121,12 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: ) # WHEN I get the activity from the file - retrieved_activity = Activity.from_parent(parent=file) + retrieved_activity = Activity.from_parent(parent=file, synapse_client=self.syn) # THEN I expect the retrieved activity to match the modified one assert retrieved_activity.name == modified_name assert retrieved_activity.description == "modified_description" - await self.verify_activity_properties( + self.verify_activity_properties( retrieved_activity, modified_name, "modified_description", @@ -134,15 +134,15 @@ async def test_activity_lifecycle(self, project: Synapse_Project) -> None: ) # WHEN I delete the activity - result.delete(parent=file) + result.delete(parent=file, synapse_client=self.syn) # THEN I expect no activity to be associated with the file - activity_after_delete = Activity.from_parent(parent=file) + activity_after_delete = Activity.from_parent( + parent=file, synapse_client=self.syn + ) assert activity_after_delete is None - async def test_store_activity_with_no_references( - self, project: Synapse_Project - ) -> None: + def test_store_activity_with_no_references(self, project: Synapse_Project) -> None: """Test storing an activity without references""" # GIVEN an activity with no references activity = Activity( @@ -151,10 +151,10 @@ async def test_store_activity_with_no_references( ) # AND a file with that activity - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) # THEN I expect the activity to have been stored properly - await self.verify_activity_properties( + self.verify_activity_properties( file.activity, activity.name, "activity with no references", @@ -162,11 +162,9 @@ async def test_store_activity_with_no_references( ) # Clean up - file.activity.delete(parent=file) + file.activity.delete(parent=file, synapse_client=self.syn) - async def test_store_activity_via_file_creation( - self, project: Synapse_Project - ) -> None: + def test_store_activity_via_file_creation(self, project: Synapse_Project) -> None: """Test storing an activity as part of file creation""" # GIVEN an activity with references activity = Activity( @@ -183,10 +181,10 @@ async def test_store_activity_via_file_creation( ) # WHEN I create a file with the activity - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) # THEN I expect the activity to have been stored with the file - await self.verify_activity_properties( + self.verify_activity_properties( file.activity, activity.name, "activity stored with file", @@ -194,9 +192,9 @@ async def test_store_activity_via_file_creation( ) # Clean up - file.activity.delete(parent=file) + file.activity.delete(parent=file, synapse_client=self.syn) - async def test_get_by_activity_id(self, project: Synapse_Project) -> None: + def test_get_by_activity_id(self, project: Synapse_Project) -> None: """Test retrieving an activity by its ID""" # GIVEN a file with an activity activity = Activity( @@ -207,18 +205,20 @@ async def test_get_by_activity_id(self, project: Synapse_Project) -> None: UsedEntity(target_id="syn456", target_version_number=1), ], ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) stored_activity = file.activity # WHEN I retrieve the activity by its ID - retrieved_activity = Activity.get(activity_id=stored_activity.id) + retrieved_activity = Activity.get( + activity_id=stored_activity.id, synapse_client=self.syn + ) # THEN I expect to get the same activity assert retrieved_activity is not None assert retrieved_activity.id == stored_activity.id assert retrieved_activity.name == activity.name assert retrieved_activity.description == "activity for get by id test" - await self.verify_activity_properties( + self.verify_activity_properties( retrieved_activity, activity.name, "activity for get by id test", @@ -226,9 +226,9 @@ async def test_get_by_activity_id(self, project: Synapse_Project) -> None: ) # Clean up - stored_activity.delete(parent=file) + stored_activity.delete(parent=file, synapse_client=self.syn) - async def test_get_by_parent_id(self, project: Synapse_Project) -> None: + def test_get_by_parent_id(self, project: Synapse_Project) -> None: """Test retrieving an activity by parent entity ID""" # GIVEN a file with an activity activity = Activity( @@ -236,19 +236,19 @@ async def test_get_by_parent_id(self, project: Synapse_Project) -> None: description="activity for get by parent test", used=[UsedURL(name="example", url=BOGUS_URL)], ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) stored_activity = file.activity - await asyncio.sleep(2) + time.sleep(2) # WHEN I retrieve the activity by parent ID - retrieved_activity = Activity.get(parent_id=file.id) + retrieved_activity = Activity.get(parent_id=file.id, synapse_client=self.syn) # THEN I expect to get the same activity assert retrieved_activity is not None assert retrieved_activity.id == stored_activity.id assert retrieved_activity.name == activity.name assert retrieved_activity.description == "activity for get by parent test" - await self.verify_activity_properties( + self.verify_activity_properties( retrieved_activity, activity.name, "activity for get by parent test", @@ -256,24 +256,24 @@ async def test_get_by_parent_id(self, project: Synapse_Project) -> None: ) # Clean up - stored_activity.delete(parent=file) + stored_activity.delete(parent=file, synapse_client=self.syn) - async def test_get_by_parent_id_with_version( - self, project: Synapse_Project - ) -> None: + def test_get_by_parent_id_with_version(self, project: Synapse_Project) -> None: """Test retrieving an activity by parent entity ID with version number""" # GIVEN a file with an activity activity = Activity( name=f"test_get_by_parent_version_{str(uuid.uuid4())}", description="activity for get by parent version test", ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) stored_activity = file.activity - await asyncio.sleep(2) + time.sleep(2) # WHEN I retrieve the activity by parent ID with version retrieved_activity = Activity.get( - parent_id=file.id, parent_version_number=file.version_number + parent_id=file.id, + parent_version_number=file.version_number, + synapse_client=self.syn, ) # THEN I expect to get the same activity @@ -285,25 +285,27 @@ async def test_get_by_parent_id_with_version( ) # Clean up - stored_activity.delete(parent=file) + stored_activity.delete(parent=file, synapse_client=self.syn) - async def test_get_nonexistent_activity(self) -> None: + def test_get_nonexistent_activity(self) -> None: """Test retrieving a nonexistent activity returns None""" # WHEN I try to retrieve a nonexistent activity by ID - retrieved_activity = Activity.get(activity_id="syn999999999") + retrieved_activity = Activity.get( + activity_id="syn999999999", synapse_client=self.syn + ) # THEN I expect to get None assert retrieved_activity is None # AND when I try to retrieve by nonexistent parent ID - retrieved_activity = Activity.get(parent_id="syn999999999") + retrieved_activity = Activity.get( + parent_id="syn999999999", synapse_client=self.syn + ) # THEN I expect to get None assert retrieved_activity is None - async def test_get_activity_id_takes_precedence( - self, project: Synapse_Project - ) -> None: + def test_get_activity_id_takes_precedence(self, project: Synapse_Project) -> None: """Test that activity_id takes precedence over parent_id when both are provided""" # GIVEN two files with different activities activity1 = Activity( @@ -315,16 +317,16 @@ async def test_get_activity_id_takes_precedence( description="second activity", ) - file1 = await self.create_file_with_activity(project, activity=activity1) - file2 = await self.create_file_with_activity(project, activity=activity2) + file1 = self.create_file_with_activity(project, activity=activity1) + file2 = self.create_file_with_activity(project, activity=activity2) stored_activity1 = file1.activity stored_activity2 = file2.activity - await asyncio.sleep(2) + time.sleep(2) # WHEN I retrieve using activity_id from first activity and parent_id from second retrieved_activity = Activity.get( - activity_id=stored_activity1.id, parent_id=file2.id + activity_id=stored_activity1.id, parent_id=file2.id, synapse_client=self.syn ) # THEN I expect to get the first activity (activity_id takes precedence) @@ -334,10 +336,10 @@ async def test_get_activity_id_takes_precedence( assert retrieved_activity.description == "first activity" # Clean up - stored_activity1.delete(parent=file1) - stored_activity2.delete(parent=file2) + stored_activity1.delete(parent=file1, synapse_client=self.syn) + stored_activity2.delete(parent=file2, synapse_client=self.syn) - async def test_get_no_parameters_raises_error(self) -> None: + def test_get_no_parameters_raises_error(self) -> None: """Test that calling get() without parameters raises ValueError""" # WHEN I try to call get() without any parameters # THEN I expect a ValueError to be raised @@ -346,12 +348,10 @@ async def test_get_no_parameters_raises_error(self) -> None: ): Activity.get() - async def test_store_activity_with_string_parent( - self, project: Synapse_Project - ) -> None: + def test_store_activity_with_string_parent(self, project: Synapse_Project) -> None: """Test storing an activity with a string parent ID""" # GIVEN a file in a project - file = await self.create_file_with_activity(project) + file = self.create_file_with_activity(project) # AND an activity with references activity = Activity( @@ -364,26 +364,24 @@ async def test_store_activity_with_string_parent( ) # WHEN I store the activity using a string parent ID - result = activity.store(parent=file.id) + result = activity.store(parent=file.id, synapse_client=self.syn) self.schedule_for_cleanup(result.id) # THEN I expect the activity to be stored correctly assert result == activity - await self.verify_activity_properties( + self.verify_activity_properties( result, activity.name, "testing string parent ID", has_references=True ) # AND when I retrieve it from the file - retrieved_activity = Activity.from_parent(parent=file) + retrieved_activity = Activity.from_parent(parent=file, synapse_client=self.syn) assert retrieved_activity.id == result.id assert retrieved_activity.name == activity.name # Clean up - Activity.delete(parent=file.id) + Activity.delete(parent=file.id, synapse_client=self.syn) - async def test_from_parent_with_string_parent( - self, project: Synapse_Project - ) -> None: + def test_from_parent_with_string_parent(self, project: Synapse_Project) -> None: """Test retrieving an activity using a string parent ID""" # GIVEN a file with an activity activity = Activity( @@ -391,11 +389,13 @@ async def test_from_parent_with_string_parent( description="testing from_parent with string", used=[UsedURL(name="example", url=BOGUS_URL)], ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) stored_activity = file.activity # WHEN I retrieve the activity using a string parent ID - retrieved_activity = Activity.from_parent(parent=file.id) + retrieved_activity = Activity.from_parent( + parent=file.id, synapse_client=self.syn + ) # THEN I expect to get the same activity assert retrieved_activity is not None @@ -404,9 +404,9 @@ async def test_from_parent_with_string_parent( assert retrieved_activity.description == "testing from_parent with string" # Clean up - Activity.delete(parent=file) + Activity.delete(parent=file, synapse_client=self.syn) - async def test_from_parent_with_string_parent_and_version( + def test_from_parent_with_string_parent_and_version( self, project: Synapse_Project ) -> None: """Test retrieving an activity using a string parent ID with version""" @@ -415,12 +415,14 @@ async def test_from_parent_with_string_parent_and_version( name=f"from_parent_string_version_test_{str(uuid.uuid4())}", description="testing from_parent with string and version", ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) stored_activity = file.activity # WHEN I retrieve the activity using a string parent ID with version parameter retrieved_activity = Activity.from_parent( - parent=file.id, parent_version_number=file.version_number + parent=file.id, + parent_version_number=file.version_number, + synapse_client=self.syn, ) # THEN I expect to get the same activity @@ -429,9 +431,9 @@ async def test_from_parent_with_string_parent_and_version( assert retrieved_activity.name == activity.name # Clean up - Activity.delete(parent=file) + Activity.delete(parent=file, synapse_client=self.syn) - async def test_from_parent_with_string_parent_with_embedded_version( + def test_from_parent_with_string_parent_with_embedded_version( self, project: Synapse_Project ) -> None: """Test retrieving an activity using a string parent ID with embedded version""" @@ -440,12 +442,14 @@ async def test_from_parent_with_string_parent_with_embedded_version( name=f"from_parent_embedded_version_test_{str(uuid.uuid4())}", description="testing from_parent with embedded version", ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) stored_activity = file.activity # WHEN I retrieve the activity using a string parent ID with embedded version parent_with_version = f"{file.id}.{file.version_number}" - retrieved_activity = Activity.from_parent(parent=parent_with_version) + retrieved_activity = Activity.from_parent( + parent=parent_with_version, synapse_client=self.syn + ) # THEN I expect to get the same activity assert retrieved_activity is not None @@ -453,18 +457,16 @@ async def test_from_parent_with_string_parent_with_embedded_version( assert retrieved_activity.name == activity.name # Clean up - Activity.delete(parent=file) + Activity.delete(parent=file, synapse_client=self.syn) - async def test_from_parent_version_precedence( - self, project: Synapse_Project - ) -> None: + def test_from_parent_version_precedence(self, project: Synapse_Project) -> None: """Test that embedded version takes precedence over parent_version_number parameter""" # GIVEN a file with an activity activity = Activity( name=f"version_precedence_test_{str(uuid.uuid4())}", description="testing version precedence", ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) stored_activity = file.activity # WHEN I retrieve the activity using a string parent ID with embedded version @@ -472,7 +474,9 @@ async def test_from_parent_version_precedence( parent_with_version = f"{file.id}.{file.version_number}" wrong_version = file.version_number + 1 if file.version_number > 1 else 999 retrieved_activity = Activity.from_parent( - parent=parent_with_version, parent_version_number=wrong_version + parent=parent_with_version, + parent_version_number=wrong_version, + synapse_client=self.syn, ) # THEN I expect to get the activity (embedded version should take precedence) @@ -481,38 +485,40 @@ async def test_from_parent_version_precedence( assert retrieved_activity.name == activity.name # Clean up - Activity.delete(parent=file) + Activity.delete(parent=file, synapse_client=self.syn) - async def test_delete_with_string_parent(self, project: Synapse_Project) -> None: + def test_delete_with_string_parent(self, project: Synapse_Project) -> None: """Test deleting an activity using a string parent ID""" # GIVEN a file with an activity activity = Activity( name=f"delete_string_test_{str(uuid.uuid4())}", description="testing delete with string parent", ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) # WHEN I delete the activity using a string parent ID - Activity.delete(parent=file.id) + Activity.delete(parent=file.id, synapse_client=self.syn) # THEN I expect no activity to be associated with the file - activity_after_delete = Activity.from_parent(parent=file) + activity_after_delete = Activity.from_parent( + parent=file, synapse_client=self.syn + ) assert activity_after_delete is None - async def test_disassociate_with_string_parent( - self, project: Synapse_Project - ) -> None: + def test_disassociate_with_string_parent(self, project: Synapse_Project) -> None: """Test disassociating an activity using a string parent ID""" # GIVEN a file with an activity activity = Activity( name=f"disassociate_string_test_{str(uuid.uuid4())}", description="testing disassociate with string parent", ) - file = await self.create_file_with_activity(project, activity=activity) + file = self.create_file_with_activity(project, activity=activity) # WHEN I disassociate the activity using a string parent ID - Activity.disassociate_from_entity(parent=file.id) + Activity.disassociate_from_entity(parent=file.id, synapse_client=self.syn) # THEN I expect no activity to be associated with the file - activity_after_disassociate = Activity.from_parent(parent=file) + activity_after_disassociate = Activity.from_parent( + parent=file, synapse_client=self.syn + ) assert activity_after_disassociate is None diff --git a/tests/integration/synapseclient/models/synchronous/test_agent.py b/tests/integration/synapseclient/models/synchronous/test_agent.py index 2fab158c0..0aa13584b 100644 --- a/tests/integration/synapseclient/models/synchronous/test_agent.py +++ b/tests/integration/synapseclient/models/synchronous/test_agent.py @@ -25,7 +25,7 @@ def init(self, syn: Synapse) -> None: else: self.AGENT_REGISTRATION_ID = "29" - async def test_start(self) -> None: + def test_start(self) -> None: # GIVEN an agent session with a valid agent registration id agent_session = AgentSession(agent_registration_id=self.AGENT_REGISTRATION_ID) @@ -45,7 +45,7 @@ async def test_start(self) -> None: assert result_session.etag is not None assert result_session.chat_history == [] - async def test_get(self) -> None: + def test_get(self) -> None: # GIVEN an agent session with a valid agent registration id agent_session = AgentSession(agent_registration_id=self.AGENT_REGISTRATION_ID) # WHEN I start a session @@ -54,7 +54,7 @@ async def test_get(self) -> None: new_session = AgentSession(id=agent_session.id).get(synapse_client=self.syn) assert new_session == agent_session - async def test_update(self) -> None: + def test_update(self) -> None: # GIVEN an agent session with a valid agent registration id and access level set agent_session = AgentSession( agent_registration_id=self.AGENT_REGISTRATION_ID, @@ -72,7 +72,7 @@ async def test_update(self) -> None: == AgentSessionAccessLevel.READ_YOUR_PRIVATE_DATA ) - async def test_prompt(self) -> None: + def test_prompt(self) -> None: # GIVEN an agent session with a valid agent registration id agent_session = AgentSession(agent_registration_id=self.AGENT_REGISTRATION_ID) # WHEN I start a session @@ -81,6 +81,7 @@ async def test_prompt(self) -> None: agent_session.prompt( prompt="hello", enable_trace=True, + synapse_client=self.syn, ) # AND I expect the chat history to be updated with the prompt and response assert len(agent_session.chat_history) == 1 @@ -113,7 +114,7 @@ def init(self, syn: Synapse) -> None: current_session=None, ) - async def test_register(self) -> None: + def test_register(self) -> None: # GIVEN an Agent with a valid agent AWS id agent = Agent(cloud_agent_id=AGENT_AWS_ID) # WHEN I register the agent @@ -122,7 +123,7 @@ async def test_register(self) -> None: expected_agent = self.agent assert agent == expected_agent - async def test_get(self) -> None: + def test_get(self) -> None: # GIVEN an Agent with a valid agent registration id agent = Agent(registration_id=self.AGENT_REGISTRATION_ID) # WHEN I get the agent @@ -131,14 +132,14 @@ async def test_get(self) -> None: expected_agent = self.agent assert agent == expected_agent - async def test_get_no_registration_id(self) -> None: + def test_get_no_registration_id(self) -> None: # GIVEN an Agent with no registration id agent = Agent() # WHEN I get the agent, I expect a ValueError to be raised with pytest.raises(ValueError, match="Registration ID is required"): agent.get(synapse_client=self.syn) - async def test_start_session(self) -> None: + def test_start_session(self) -> None: # GIVEN an Agent with a valid agent registration id agent = Agent(registration_id=self.AGENT_REGISTRATION_ID).get( synapse_client=self.syn @@ -150,7 +151,7 @@ async def test_start_session(self) -> None: # AND I expect the session to be in the sessions dictionary assert agent.sessions[agent.current_session.id] == agent.current_session - async def test_get_session(self) -> None: + def test_get_session(self) -> None: # GIVEN an Agent with a valid agent registration id agent = Agent(registration_id=self.AGENT_REGISTRATION_ID).get( synapse_client=self.syn @@ -158,13 +159,15 @@ async def test_get_session(self) -> None: # WHEN I start a session session = agent.start_session(synapse_client=self.syn) # THEN I expect to be able to get the session with its id - existing_session = agent.get_session(session_id=session.id) + existing_session = agent.get_session( + session_id=session.id, synapse_client=self.syn + ) # AND I expect those sessions to be the same assert existing_session == session # AND I expect it to be the current session assert existing_session == agent.current_session - async def test_prompt_with_session(self) -> None: + def test_prompt_with_session(self) -> None: # GIVEN an Agent with a valid agent registration id agent = Agent(registration_id=self.AGENT_REGISTRATION_ID).get( synapse_client=self.syn @@ -174,7 +177,9 @@ async def test_prompt_with_session(self) -> None: synapse_client=self.syn ) # WHEN I prompt the agent with a session - agent.prompt(prompt="hello", enable_trace=True, session=session) + agent.prompt( + prompt="hello", enable_trace=True, session=session, synapse_client=self.syn + ) test_session = agent.sessions[session.id] # THEN I expect the chat history to be updated with the prompt and response assert len(test_session.chat_history) == 1 @@ -184,14 +189,14 @@ async def test_prompt_with_session(self) -> None: # AND I expect the current session to be the session provided assert agent.current_session.id == session.id - async def test_prompt_no_session(self) -> None: + def test_prompt_no_session(self) -> None: # GIVEN an Agent with a valid agent registration id agent = Agent(registration_id=self.AGENT_REGISTRATION_ID).get( synapse_client=self.syn ) # WHEN I prompt the agent without a current session set # and no session provided - agent.prompt(prompt="hello", enable_trace=True) + agent.prompt(prompt="hello", enable_trace=True, synapse_client=self.syn) # THEN I expect a new session to be started and set as the current session assert agent.current_session is not None # AND I expect the chat history to be updated with the prompt and response diff --git a/tests/integration/synapseclient/models/synchronous/test_column.py b/tests/integration/synapseclient/models/synchronous/test_column.py index c345c443e..1b1686baf 100644 --- a/tests/integration/synapseclient/models/synchronous/test_column.py +++ b/tests/integration/synapseclient/models/synchronous/test_column.py @@ -14,7 +14,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_get_column_by_id(self, project_model: Project) -> None: + def test_get_column_by_id(self, project_model: Project) -> None: """Test getting a column by its ID.""" # GIVEN a table with a column table_name = str(uuid.uuid4()) @@ -49,7 +49,7 @@ async def test_get_column_by_id(self, project_model: Project) -> None: assert column.column_type == ColumnType.STRING assert column.maximum_size == 50 - async def test_get_column_by_invalid_id(self) -> None: + def test_get_column_by_invalid_id(self) -> None: """Test getting a column by an invalid ID.""" # GIVEN an invalid column ID invalid_id = "999999999" @@ -62,7 +62,7 @@ async def test_get_column_by_invalid_id(self) -> None: # Verify the error is a 404 Not Found assert "404" in str(exc_info.value) - async def test_list_all_columns(self, project_model: Project) -> None: + def test_list_all_columns(self, project_model: Project) -> None: """Test listing all columns without a prefix.""" # GIVEN a single table with multiple columns table_name = str(uuid.uuid4()) @@ -94,7 +94,7 @@ async def test_list_all_columns(self, project_model: Project) -> None: assert all(col.id is not None for col in columns) assert all(col.name is not None for col in columns) - async def test_list_columns_with_prefix(self, project_model: Project) -> None: + def test_list_columns_with_prefix(self, project_model: Project) -> None: """Test listing columns with a prefix filter.""" # GIVEN a table with columns that have a specific prefix prefix = "test_prefix_column_filter" @@ -135,9 +135,7 @@ async def test_list_columns_with_prefix(self, project_model: Project) -> None: assert "test_prefix_column_filter_col2" in column_names assert "other_col" not in column_names - async def test_list_columns_with_limit_and_offset( - self, project_model: Project - ) -> None: + def test_list_columns_with_limit_and_offset(self, project_model: Project) -> None: """Test listing columns with limit and offset parameters to verify pagination behavior.""" # GIVEN a single table with multiple columns table_name = str(uuid.uuid4()) @@ -180,7 +178,7 @@ async def test_list_columns_with_limit_and_offset( assert len(columns_page2) <= 3 assert all(isinstance(col, Column) for col in columns_page2) - async def test_list_columns_with_no_prefix_match(self) -> None: + def test_list_columns_with_no_prefix_match(self) -> None: """Test listing columns with a prefix that doesn't match any columns.""" # GIVEN a unique prefix that won't match any existing columns unique_prefix = "nonexistent_prefix_static" diff --git a/tests/integration/synapseclient/models/synchronous/test_curation.py b/tests/integration/synapseclient/models/synchronous/test_curation.py index 5dcb4ef61..cda597efc 100644 --- a/tests/integration/synapseclient/models/synchronous/test_curation.py +++ b/tests/integration/synapseclient/models/synchronous/test_curation.py @@ -27,7 +27,7 @@ class TestFileBasedMetadataTaskProperties: """Tests for the FileBasedMetadataTaskProperties class.""" - async def test_fill_from_dict_and_to_synapse_request(self) -> None: + def test_fill_from_dict_and_to_synapse_request(self) -> None: # GIVEN a dictionary representing a FileBasedMetadataTaskProperties response response_dict = { "concreteType": "org.sagebionetworks.repo.model.curation.metadata.FileBasedMetadataTaskProperties", @@ -54,7 +54,7 @@ async def test_fill_from_dict_and_to_synapse_request(self) -> None: assert request_dict["uploadFolderId"] == "syn123456" assert request_dict["fileViewId"] == "syn789012" - async def test_fill_from_dict_with_none_values(self) -> None: + def test_fill_from_dict_with_none_values(self) -> None: # GIVEN a dictionary with missing optional fields response_dict = { "concreteType": "org.sagebionetworks.repo.model.curation.metadata.FileBasedMetadataTaskProperties" @@ -67,7 +67,7 @@ async def test_fill_from_dict_with_none_values(self) -> None: assert properties.upload_folder_id is None assert properties.file_view_id is None - async def test_to_synapse_request_with_partial_data(self) -> None: + def test_to_synapse_request_with_partial_data(self) -> None: # GIVEN a FileBasedMetadataTaskProperties with only some fields set properties = FileBasedMetadataTaskProperties(upload_folder_id="syn123456") @@ -87,7 +87,7 @@ async def test_to_synapse_request_with_partial_data(self) -> None: class TestRecordBasedMetadataTaskProperties: """Tests for the RecordBasedMetadataTaskProperties class.""" - async def test_fill_from_dict_and_to_synapse_request(self) -> None: + def test_fill_from_dict_and_to_synapse_request(self) -> None: # GIVEN a dictionary representing a RecordBasedMetadataTaskProperties response response_dict = { "concreteType": "org.sagebionetworks.repo.model.curation.metadata.RecordBasedMetadataTaskProperties", @@ -111,7 +111,7 @@ async def test_fill_from_dict_and_to_synapse_request(self) -> None: assert request_dict["concreteType"] == expected_concrete_type assert request_dict["recordSetId"] == "syn123456" - async def test_fill_from_dict_with_none_values(self) -> None: + def test_fill_from_dict_with_none_values(self) -> None: # GIVEN a dictionary with missing optional fields response_dict = { "concreteType": "org.sagebionetworks.repo.model.curation.metadata.RecordBasedMetadataTaskProperties" @@ -123,7 +123,7 @@ async def test_fill_from_dict_with_none_values(self) -> None: # THEN the properties should handle None values correctly assert properties.record_set_id is None - async def test_to_synapse_request_with_none_values(self) -> None: + def test_to_synapse_request_with_none_values(self) -> None: # GIVEN a RecordBasedMetadataTaskProperties with no record set ID properties = RecordBasedMetadataTaskProperties() @@ -238,7 +238,7 @@ def record_set(self, project_model: Project) -> RecordSet: os.unlink(filename) raise - async def test_store_file_based_curation_task( + def test_store_file_based_curation_task( self, project_model: Project, folder_with_view: tuple[Folder, EntityView] ) -> None: # GIVEN a project, folder, and entity view @@ -274,7 +274,7 @@ async def test_store_file_based_curation_task( assert stored_task.created_on is not None assert stored_task.created_by is not None - async def test_store_record_based_curation_task( + def test_store_record_based_curation_task( self, project_model: Project, record_set: RecordSet ) -> None: # GIVEN a project and record set @@ -308,7 +308,7 @@ async def test_store_record_based_curation_task( assert stored_task.created_on is not None assert stored_task.created_by is not None - async def test_store_update_existing_curation_task( + def test_store_update_existing_curation_task( self, project_model: Project, record_set: RecordSet ) -> None: # GIVEN an existing curation task @@ -343,7 +343,7 @@ async def test_store_update_existing_curation_task( ) assert stored_updated_task.task_properties.record_set_id == record_set.id - async def test_store_validation_errors(self) -> None: + def test_store_validation_errors(self) -> None: # GIVEN a CurationTask without required fields curation_task = CurationTask() @@ -403,7 +403,7 @@ def folder_with_view(self, project_model: Project) -> tuple[Folder, EntityView]: return folder, entity_view - async def test_get_curation_task( + def test_get_curation_task( self, project_model: Project, folder_with_view: tuple[Folder, EntityView] ) -> None: # GIVEN a project, folder, and entity view @@ -441,7 +441,7 @@ async def test_get_curation_task( assert retrieved_task.created_on == original_task.created_on assert retrieved_task.created_by == original_task.created_by - async def test_get_validation_error(self) -> None: + def test_get_validation_error(self) -> None: # GIVEN a CurationTask without a task_id curation_task = CurationTask() @@ -452,7 +452,7 @@ async def test_get_validation_error(self) -> None: ): curation_task.get(synapse_client=self.syn) - async def test_get_non_existent_task(self) -> None: + def test_get_non_existent_task(self) -> None: # GIVEN a non-existent task ID curation_task = CurationTask(task_id=999999) @@ -507,7 +507,7 @@ def folder_with_view(self, project_model: Project) -> tuple[Folder, EntityView]: return folder, entity_view - async def test_delete_curation_task( + def test_delete_curation_task( self, project_model: Project, folder_with_view: tuple[Folder, EntityView] ) -> None: # GIVEN a project, folder, and entity view @@ -536,7 +536,7 @@ async def test_delete_curation_task( with pytest.raises(SynapseHTTPError): CurationTask(task_id=task_id).get(synapse_client=self.syn) - async def test_delete_validation_error(self) -> None: + def test_delete_validation_error(self) -> None: # GIVEN a CurationTask without a task_id curation_task = CurationTask() @@ -593,7 +593,7 @@ def folder_with_view(self, project_model: Project) -> tuple[Folder, EntityView]: return folder, entity_view - async def test_list_curation_tasks( + def test_list_curation_tasks( self, project_model: Project, folder_with_view: tuple[Folder, EntityView] ) -> None: # GIVEN a project, folder, and entity view @@ -640,7 +640,7 @@ async def test_list_curation_tasks( assert task.created_on is not None assert task.created_by is not None - async def test_list_empty_project(self) -> None: + def test_list_empty_project(self) -> None: # GIVEN a project with no curation tasks empty_project = Project(name=str(uuid.uuid4())).store(synapse_client=self.syn) self.schedule_for_cleanup(empty_project.id) diff --git a/tests/integration/synapseclient/models/synchronous/test_dataset.py b/tests/integration/synapseclient/models/synchronous/test_dataset.py index 5d8037d0b..ddbb59703 100644 --- a/tests/integration/synapseclient/models/synchronous/test_dataset.py +++ b/tests/integration/synapseclient/models/synchronous/test_dataset.py @@ -66,7 +66,7 @@ def create_file_instance(self) -> File: content_type=CONTENT_TYPE, ) - async def create_dataset_with_items( + def create_dataset_with_items( self, project_model: Project, files: Optional[List[File]] = None, @@ -86,13 +86,15 @@ async def create_dataset_with_items( # Add files if provided if files: for file in files: - stored_file = file.store(parent=project_model) + stored_file = file.store(parent=project_model, synapse_client=self.syn) dataset.add_item(stored_file) # Add folders if provided if folders: for folder in folders: - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store( + parent=project_model, synapse_client=self.syn + ) dataset.add_item(stored_folder) # Store the dataset @@ -101,7 +103,7 @@ async def create_dataset_with_items( return dataset - async def test_dataset_basic_operations(self, project_model: Project) -> None: + def test_dataset_basic_operations(self, project_model: Project) -> None: """Test dataset creation, retrieval, updating and deletion""" # GIVEN a name and description for a dataset dataset_name = str(uuid.uuid4()) @@ -151,7 +153,7 @@ async def test_dataset_basic_operations(self, project_model: Project) -> None: ): Dataset(id=dataset.id).get(synapse_client=self.syn) - async def test_dataset_with_items(self, project_model: Project) -> None: + def test_dataset_with_items(self, project_model: Project) -> None: """Test creating and managing a dataset with various items (files, folders)""" # GIVEN 3 files and a folder with 2 files files = [self.create_file_instance() for _ in range(3)] @@ -161,11 +163,11 @@ async def test_dataset_with_items(self, project_model: Project) -> None: # WHEN I store the files and folder stored_files = [] for file in files: - stored_file = file.store(parent=project_model) + stored_file = file.store(parent=project_model, synapse_client=self.syn) stored_files.append(stored_file) folder.files = folder_files - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store(parent=project_model, synapse_client=self.syn) # AND create a dataset with these items dataset = Dataset( @@ -176,10 +178,10 @@ async def test_dataset_with_items(self, project_model: Project) -> None: # Add individual files for file in stored_files: - dataset.add_item(file) + dataset.add_item(file, synapse_client=self.syn) # Add folder - dataset.add_item(stored_folder) + dataset.add_item(stored_folder, synapse_client=self.syn) # Store the dataset dataset = dataset.store(synapse_client=self.syn) @@ -201,7 +203,7 @@ async def test_dataset_with_items(self, project_model: Project) -> None: assert item in retrieved_dataset.items # WHEN I remove one file from the dataset - dataset.remove_item(stored_files[0]) + dataset.remove_item(stored_files[0], synapse_client=self.syn) dataset.store(synapse_client=self.syn) # THEN that file should no longer be in the dataset @@ -212,11 +214,11 @@ async def test_dataset_with_items(self, project_model: Project) -> None: ) assert len(updated_dataset.items) == len(expected_items) - 1 - async def test_dataset_query_operations(self, project_model: Project) -> None: + def test_dataset_query_operations(self, project_model: Project) -> None: """Test querying a dataset and different query modes""" # GIVEN a dataset with a file and custom column file = self.create_file_instance() - stored_file = file.store(parent=project_model) + stored_file = file.store(parent=project_model, synapse_client=self.syn) dataset = Dataset( name=str(uuid.uuid4()), @@ -224,7 +226,7 @@ async def test_dataset_query_operations(self, project_model: Project) -> None: parent_id=project_model.id, columns=[Column(name="my_annotation", column_type=ColumnType.STRING)], ) - dataset.add_item(stored_file) + dataset.add_item(stored_file, synapse_client=self.syn) dataset = dataset.store(synapse_client=self.syn) self.schedule_for_cleanup(dataset.id) @@ -240,11 +242,13 @@ async def test_dataset_query_operations(self, project_model: Project) -> None: primary_keys=["id"], wait_for_eventually_consistent_view=True, dry_run=False, + synapse_client=self.syn, ) # THEN I can query the data row = Dataset.query( - query=f"SELECT * FROM {dataset.id} WHERE id = '{stored_file.id}'" + query=f"SELECT * FROM {dataset.id} WHERE id = '{stored_file.id}'", + synapse_client=self.syn, ) # AND the query results should match the expected values @@ -285,10 +289,10 @@ async def test_dataset_query_operations(self, project_model: Project) -> None: assert results_only.sum_file_sizes is None assert results_only.last_updated_on is None - async def test_dataset_column_operations(self, project_model: Project) -> None: + def test_dataset_column_operations(self, project_model: Project) -> None: """Test operations on dataset columns: add, rename, reorder, delete""" # GIVEN a dataset with no custom columns - dataset = await self.create_dataset_with_items(project_model) + dataset = self.create_dataset_with_items(project_model) # WHEN I add a column to the dataset column_name = "test_column" @@ -337,14 +341,14 @@ async def test_dataset_column_operations(self, project_model: Project) -> None: assert second_column not in updated_dataset.columns assert new_name in updated_dataset.columns - async def test_dataset_versioning(self, project_model: Project) -> None: + def test_dataset_versioning(self, project_model: Project) -> None: """Test creating snapshots and versioning of datasets""" # GIVEN a dataset and two files file1 = self.create_file_instance() file2 = self.create_file_instance() - file1 = file1.store(parent=project_model) - file2 = file2.store(parent=project_model) + file1 = file1.store(parent=project_model, synapse_client=self.syn) + file2 = file2.store(parent=project_model, synapse_client=self.syn) dataset = Dataset( name=str(uuid.uuid4()), @@ -355,12 +359,12 @@ async def test_dataset_versioning(self, project_model: Project) -> None: self.schedule_for_cleanup(dataset.id) # WHEN I add the first file and create a snapshot - dataset.add_item(file1) + dataset.add_item(file1, synapse_client=self.syn) dataset.store(synapse_client=self.syn) dataset.snapshot(synapse_client=self.syn) # AND I add the second file and create another snapshot - dataset.add_item(file2) + dataset.add_item(file2, synapse_client=self.syn) dataset.store(synapse_client=self.syn) dataset.snapshot(synapse_client=self.syn) @@ -400,9 +404,7 @@ def create_file_instance(self) -> File: content_type=CONTENT_TYPE, ) - async def create_dataset( - self, project_model: Project, has_file: bool = False - ) -> Dataset: + def create_dataset(self, project_model: Project, has_file: bool = False) -> Dataset: """Helper to create a dataset""" dataset = Dataset( name=str(uuid.uuid4()), @@ -412,18 +414,18 @@ async def create_dataset( if has_file: file = self.create_file_instance() - stored_file = file.store(parent=project_model) - dataset.add_item(stored_file) + stored_file = file.store(parent=project_model, synapse_client=self.syn) + dataset.add_item(stored_file, synapse_client=self.syn) dataset = dataset.store(synapse_client=self.syn) self.schedule_for_cleanup(dataset.id) return dataset - async def test_dataset_collection_lifecycle(self, project_model: Project) -> None: + def test_dataset_collection_lifecycle(self, project_model: Project) -> None: """Test creating, updating, and deleting a DatasetCollection""" # GIVEN two datasets - dataset1 = await self.create_dataset(project_model, has_file=True) - dataset2 = await self.create_dataset(project_model, has_file=True) + dataset1 = self.create_dataset(project_model, has_file=True) + dataset2 = self.create_dataset(project_model, has_file=True) # WHEN I create a DatasetCollection with the first dataset collection = DatasetCollection( @@ -480,10 +482,10 @@ async def test_dataset_collection_lifecycle(self, project_model: Project) -> Non ): DatasetCollection(id=collection.id).get(synapse_client=self.syn) - async def test_dataset_collection_queries(self, project_model: Project) -> None: + def test_dataset_collection_queries(self, project_model: Project) -> None: """Test querying DatasetCollections with various part masks""" # GIVEN a dataset and a collection with that dataset - dataset = await self.create_dataset(project_model, has_file=True) + dataset = self.create_dataset(project_model, has_file=True) collection = DatasetCollection( name=str(uuid.uuid4()), @@ -507,11 +509,13 @@ async def test_dataset_collection_queries(self, project_model: Project) -> None: primary_keys=["id"], wait_for_eventually_consistent_view=True, dry_run=False, + synapse_client=self.syn, ) # THEN I can query and get the updated data row = DatasetCollection.query( - query=f"SELECT * FROM {collection.id} WHERE id = '{dataset.id}'" + query=f"SELECT * FROM {collection.id} WHERE id = '{dataset.id}'", + synapse_client=self.syn, ) assert row["id"][0] == dataset.id assert row["name"][0] == dataset.name @@ -544,7 +548,9 @@ async def test_dataset_collection_queries(self, project_model: Project) -> None: # WHEN I query with only results results_only = DatasetCollection.query_part_mask( - query=f"SELECT * FROM {collection.id}", part_mask=QUERY_RESULTS + query=f"SELECT * FROM {collection.id}", + part_mask=QUERY_RESULTS, + synapse_client=self.syn, ) # THEN the data in the columns should match assert results_only.result["id"][0] == dataset.id @@ -556,7 +562,7 @@ async def test_dataset_collection_queries(self, project_model: Project) -> None: assert results_only.sum_file_sizes is None assert results_only.last_updated_on is None - async def test_dataset_collection_columns(self, project_model: Project) -> None: + def test_dataset_collection_columns(self, project_model: Project) -> None: """Test column operations on DatasetCollections""" # GIVEN a DatasetCollection collection = DatasetCollection( @@ -608,12 +614,12 @@ async def test_dataset_collection_columns(self, project_model: Project) -> None: assert second_col not in updated.columns assert new_name in updated.columns - async def test_dataset_collection_versioning(self, project_model: Project) -> None: + def test_dataset_collection_versioning(self, project_model: Project) -> None: """Test versioning of DatasetCollections""" # GIVEN a DatasetCollection and datasets - dataset1 = await self.create_dataset(project_model) - dataset2 = await self.create_dataset(project_model) + dataset1 = self.create_dataset(project_model) + dataset2 = self.create_dataset(project_model) collection = DatasetCollection( name=str(uuid.uuid4()), diff --git a/tests/integration/synapseclient/models/synchronous/test_entityview.py b/tests/integration/synapseclient/models/synchronous/test_entityview.py index 7af33226c..4de1db347 100644 --- a/tests/integration/synapseclient/models/synchronous/test_entityview.py +++ b/tests/integration/synapseclient/models/synchronous/test_entityview.py @@ -1,3 +1,4 @@ +import asyncio import tempfile import uuid from typing import Callable, List @@ -34,7 +35,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def setup_files_in_folder( + def setup_files_in_folder( self, project_model: Project, num_files: int = 4 ) -> tuple[Folder, List[File]]: """Helper to create a folder with files for testing""" @@ -71,9 +72,7 @@ async def setup_files_in_folder( return folder, files - async def test_entityview_creation_with_columns( - self, project_model: Project - ) -> None: + def test_entityview_creation_with_columns(self, project_model: Project) -> None: """Test creating entity views with different column configurations""" # GIVEN parameters for three different entity view configurations test_cases = [ @@ -104,8 +103,10 @@ async def test_entityview_creation_with_columns( ] # Get default column count to set expectation - default_columns = await get_default_columns( - view_type_mask=ViewTypeMask.FILE.value, synapse_client=self.syn + default_columns = asyncio.run( + get_default_columns( + view_type_mask=ViewTypeMask.FILE.value, synapse_client=self.syn + ) ) test_cases[0]["expected_column_count"] = len(default_columns) @@ -161,7 +162,7 @@ async def test_entityview_creation_with_columns( == ColumnType.INTEGER ) - async def test_entityview_invalid_column(self, project_model: Project) -> None: + def test_entityview_invalid_column(self, project_model: Project) -> None: """Test creating an entity view with an invalid column""" # GIVEN an entity view with an invalid column entityview = EntityView( @@ -188,10 +189,10 @@ async def test_entityview_invalid_column(self, project_model: Project) -> None: in str(e.value) ) - async def test_entityview_with_files_in_scope(self, project_model: Project) -> None: + def test_entityview_with_files_in_scope(self, project_model: Project) -> None: """Test creating entity view with files in scope and querying it""" # GIVEN a folder with files - folder, files = await self.setup_files_in_folder(project_model) + folder, files = self.setup_files_in_folder(project_model) # WHEN I create an entity view with that folder in its scope entityview = EntityView( @@ -214,12 +215,12 @@ async def test_entityview_with_files_in_scope(self, project_model: Project) -> N assert results["name"][i] == file.name assert results["description"][i] == file.description - async def test_update_rows_and_annotations( + def test_update_rows_and_annotations( self, mocker: MockerFixture, project_model: Project ) -> None: """Test updating rows in an entity view from different sources and verifying annotations""" # GIVEN a folder with files - folder, files = await self.setup_files_in_folder(project_model) + folder, files = self.setup_files_in_folder(project_model) # AND an entity view with columns and files in scope entityview = EntityView( @@ -396,10 +397,10 @@ def csv_wrapper(*args, **kwargs): else: assert "float_column" not in file_copy.annotations.keys() - async def test_update_rows_without_id_column(self, project_model: Project) -> None: + def test_update_rows_without_id_column(self, project_model: Project) -> None: """Test that updating rows requires the id column""" # GIVEN a folder with files and an entity view - folder, _ = await self.setup_files_in_folder(project_model, num_files=1) + folder, _ = self.setup_files_in_folder(project_model, num_files=1) entityview = EntityView( name=str(uuid.uuid4()), @@ -428,7 +429,7 @@ async def test_update_rows_without_id_column(self, project_model: Project) -> No in str(e.value) ) - async def test_column_modifications(self, project_model: Project) -> None: + def test_column_modifications(self, project_model: Project) -> None: """Test renaming and deleting columns in an entity view""" # GIVEN an entity view with multiple columns old_column_name = "column_string" @@ -481,10 +482,10 @@ async def test_column_modifications(self, project_model: Project) -> None: assert new_column_name not in retrieved_view.columns assert column_to_keep in retrieved_view.columns - async def test_query_with_part_mask(self, project_model: Project) -> None: + def test_query_with_part_mask(self, project_model: Project) -> None: """Test querying an entity view with different part masks""" # GIVEN a folder with files - folder, files = await self.setup_files_in_folder(project_model, num_files=2) + folder, files = self.setup_files_in_folder(project_model, num_files=2) # AND an entity view with the folder in scope entityview = EntityView( @@ -532,10 +533,10 @@ async def test_query_with_part_mask(self, project_model: Project) -> None: assert results_only.last_updated_on is None assert results_only.result["name"].tolist() == [file.name for file in files] - async def test_snapshot_functionality(self, project_model: Project) -> None: + def test_snapshot_functionality(self, project_model: Project) -> None: """Test creating snapshots of entity views with different activity configurations""" # GIVEN a folder with a file - folder, [file] = await self.setup_files_in_folder(project_model, num_files=1) + folder, [file] = self.setup_files_in_folder(project_model, num_files=1) # AND an entity view with an activity entityview = EntityView( @@ -624,7 +625,7 @@ async def test_snapshot_functionality(self, project_model: Project) -> None: else: assert newest_instance.activity is None - async def test_snapshot_with_no_scope(self, project_model: Project) -> None: + def test_snapshot_with_no_scope(self, project_model: Project) -> None: """Test that creating a snapshot of an entity view with no scope raises an error""" # GIVEN an entity view with no scope entityview = EntityView( diff --git a/tests/integration/synapseclient/models/synchronous/test_evaluation.py b/tests/integration/synapseclient/models/synchronous/test_evaluation.py index cf687c690..a1c8fdb6f 100644 --- a/tests/integration/synapseclient/models/synchronous/test_evaluation.py +++ b/tests/integration/synapseclient/models/synchronous/test_evaluation.py @@ -17,7 +17,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_create_evaluation(self): + def test_create_evaluation(self): # GIVEN a project to work with project = Project(name=f"test_project_{uuid.uuid4()}").store( synapse_client=self.syn @@ -54,7 +54,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.schedule_for_cleanup = schedule_for_cleanup @pytest.fixture(scope="function") - async def test_project( + def test_project( self, syn: Synapse, schedule_for_cleanup: Callable[..., None] ) -> Project: """Create a test project for evaluation tests.""" @@ -65,7 +65,7 @@ async def test_project( return project @pytest.fixture(scope="function") - async def test_evaluation( + def test_evaluation( self, test_project: Project, syn: Synapse, @@ -84,7 +84,7 @@ async def test_evaluation( return created_evaluation @pytest.fixture(scope="function") - async def multiple_evaluations( + def multiple_evaluations( self, test_project: Project, syn: Synapse, @@ -105,7 +105,7 @@ async def multiple_evaluations( evaluations.append(created_evaluation) return evaluations - async def test_get_evaluation_by_id( + def test_get_evaluation_by_id( self, test_evaluation: Evaluation, test_project: Project ): # WHEN I get an evaluation by id using the dataclass method @@ -124,7 +124,7 @@ async def test_get_evaluation_by_id( retrieved_evaluation.created_on is not None ) # Check that created_on is set - async def test_get_evaluation_by_name( + def test_get_evaluation_by_name( self, test_evaluation: Evaluation, test_project: Project ): # WHEN I get an evaluation by name using the dataclass method @@ -143,7 +143,7 @@ async def test_get_evaluation_by_name( retrieved_evaluation.created_on is not None ) # Check that created_on is set - async def test_get_all_evaluations( + def test_get_all_evaluations( self, multiple_evaluations: list[Evaluation], limit: int = 1 ): # Test 1: Grab evaluations that the user has access to @@ -172,9 +172,7 @@ async def test_get_all_evaluations( # THEN the evaluations retrieved should match said limit assert len(limited_evaluations) == limit - async def test_get_available_evaluations( - self, multiple_evaluations: list[Evaluation] - ): + def test_get_available_evaluations(self, multiple_evaluations: list[Evaluation]): # WHEN a call is made to get available evaluations for a given user evaluations = Evaluation.get_available_evaluations(synapse_client=self.syn) @@ -182,7 +180,7 @@ async def test_get_available_evaluations( assert evaluations is not None assert len(evaluations) >= len(multiple_evaluations) - async def test_get_evaluations_by_project( + def test_get_evaluations_by_project( self, test_project: Project, multiple_evaluations: list[Evaluation] ): # WHEN a call is made to get evaluations by project @@ -206,7 +204,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.schedule_for_cleanup = schedule_for_cleanup @pytest.fixture(scope="function") - async def test_project( + def test_project( self, syn: Synapse, schedule_for_cleanup: Callable[..., None] ) -> Project: """Create a test project for evaluation tests.""" @@ -217,7 +215,7 @@ async def test_project( return project @pytest.fixture(scope="function") - async def test_evaluation( + def test_evaluation( self, test_project: Project, syn: Synapse, @@ -235,7 +233,7 @@ async def test_evaluation( schedule_for_cleanup(created_evaluation.id) return created_evaluation - async def test_store_evaluation_with_same_name( + def test_store_evaluation_with_same_name( self, test_project: Project, test_evaluation: Evaluation ): # GIVEN an existing evaluation @@ -260,7 +258,7 @@ async def test_store_evaluation_with_same_name( "already exists with the name" in error_message ), f"Unexpected error message: {error_message}" - async def test_update_evaluation_name(self, test_evaluation: Evaluation): + def test_update_evaluation_name(self, test_evaluation: Evaluation): # WHEN I update the evaluation name in my evaluation object new_name = f"updated_evaluation_{uuid.uuid4()}" test_evaluation.name = new_name @@ -273,7 +271,7 @@ async def test_update_evaluation_name(self, test_evaluation: Evaluation): assert updated_evaluation.description == test_evaluation.description assert updated_evaluation.etag == test_evaluation.etag - async def test_update_evaluation_description(self, test_evaluation: Evaluation): + def test_update_evaluation_description(self, test_evaluation: Evaluation): # WHEN I update the evaluation description new_description = f"Updated description {uuid.uuid4()}" old_etag = test_evaluation.etag @@ -290,7 +288,7 @@ async def test_update_evaluation_description(self, test_evaluation: Evaluation): assert updated_evaluation.etag is not None assert updated_evaluation.etag != old_etag - async def test_update_multiple_fields(self, test_evaluation: Evaluation): + def test_update_multiple_fields(self, test_evaluation: Evaluation): # WHEN I update multiple fields at once new_name = f"multi_update_{uuid.uuid4()}" new_description = f"Multi-updated description {uuid.uuid4()}" @@ -315,7 +313,7 @@ async def test_update_multiple_fields(self, test_evaluation: Evaluation): assert updated_evaluation.etag is not None assert updated_evaluation.etag != old_etag - async def test_certain_fields_unchanged_once_retrieved_from_synapse( + def test_certain_fields_unchanged_once_retrieved_from_synapse( self, test_evaluation: Evaluation ): # GIVEN an existing evaluation @@ -338,7 +336,7 @@ async def test_certain_fields_unchanged_once_retrieved_from_synapse( assert updated_evaluation.id == original_id assert updated_evaluation.content_source == original_content_source - async def test_store_with_nonexistent_id(self, test_project: Project): + def test_store_with_nonexistent_id(self, test_project: Project): # GIVEN an evaluation with a non-existent ID that's never been stored unique_name = f"test_evaluation_{uuid.uuid4()}" evaluation = Evaluation( @@ -374,9 +372,7 @@ async def test_store_with_nonexistent_id(self, test_project: Project): assert updated_eval.id != "syn999999999" assert updated_eval.name == retrieved_eval.name - async def test_store_unchanged_evaluation( - self, test_evaluation: Evaluation, monkeypatch - ): + def test_store_unchanged_evaluation(self, test_evaluation: Evaluation, monkeypatch): warning_messages = [] def mock_warning(self, msg, *args, **kwargs): @@ -421,7 +417,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.schedule_for_cleanup = schedule_for_cleanup @pytest.fixture(scope="function") - async def test_project( + def test_project( self, syn: Synapse, schedule_for_cleanup: Callable[..., None] ) -> Project: """Create a test project for evaluation tests.""" @@ -432,7 +428,7 @@ async def test_project( return project @pytest.fixture(scope="function") - async def test_evaluation( + def test_evaluation( self, test_project: Project, syn: Synapse, @@ -450,7 +446,7 @@ async def test_evaluation( schedule_for_cleanup(created_evaluation.id) return created_evaluation - async def test_delete_evaluation(self, test_evaluation: Evaluation): + def test_delete_evaluation(self, test_evaluation: Evaluation): # WHEN I delete the evaluation using the dataclass method test_evaluation.delete(synapse_client=self.syn) @@ -466,7 +462,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.schedule_for_cleanup = schedule_for_cleanup @pytest.fixture(scope="function") - async def test_project( + def test_project( self, syn: Synapse, schedule_for_cleanup: Callable[..., None] ) -> Project: """Create a test project for evaluation tests.""" @@ -477,7 +473,7 @@ async def test_project( return project @pytest.fixture(scope="function") - async def test_evaluation( + def test_evaluation( self, test_project: Project, syn: Synapse, @@ -495,7 +491,7 @@ async def test_evaluation( schedule_for_cleanup(created_evaluation.id) return created_evaluation - async def test_get_evaluation_acl(self, test_evaluation: Evaluation): + def test_get_evaluation_acl(self, test_evaluation: Evaluation): # GIVEN the current user's ID user_profile = self.syn.getUserProfile() current_user_id = int(user_profile.get("ownerId")) @@ -520,14 +516,14 @@ async def test_get_evaluation_acl(self, test_evaluation: Evaluation): current_user_id in principal_ids ), f"Current user {current_user_id} not found in resourceAccess principal IDs: {principal_ids}" - async def test_get_evaluation_permissions(self, test_evaluation: Evaluation): + def test_get_evaluation_permissions(self, test_evaluation: Evaluation): # WHEN I get evaluation permissions using the dataclass method permissions = test_evaluation.get_permissions(synapse_client=self.syn) # THEN the permissions should be retrieved assert permissions is not None - async def test_update_acl_with_principal_id(self, test_evaluation: Evaluation): + def test_update_acl_with_principal_id(self, test_evaluation: Evaluation): """Test updating ACL for an evaluation using principal_id and access_type.""" # GIVEN the current user's ID user_profile = self.syn.getUserProfile() @@ -558,7 +554,7 @@ async def test_update_acl_with_principal_id(self, test_evaluation: Evaluation): ["READ", "UPDATE", "DELETE", "CHANGE_PERMISSIONS"] ) - async def test_update_acl_with_full_dictionary(self, test_evaluation: Evaluation): + def test_update_acl_with_full_dictionary(self, test_evaluation: Evaluation): """Test updating ACL for an evaluation using a complete ACL dictionary.""" # GIVEN the current ACL current_acl = test_evaluation.get_acl(synapse_client=self.syn) @@ -601,7 +597,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_create_evaluation_missing_required_fields(self): + def test_create_evaluation_missing_required_fields(self): # WHEN I try to create an evaluation with missing required fields evaluation = Evaluation(name="test_evaluation") @@ -609,7 +605,7 @@ async def test_create_evaluation_missing_required_fields(self): with pytest.raises(ValueError, match="missing the 'description' attribute"): evaluation.store(synapse_client=self.syn) - async def test_get_evaluation_missing_id_and_name(self): + def test_get_evaluation_missing_id_and_name(self): # WHEN I try to get an evaluation without id or name evaluation = Evaluation() @@ -619,7 +615,7 @@ async def test_get_evaluation_missing_id_and_name(self): ): evaluation.get(synapse_client=self.syn) - async def test_delete_evaluation_missing_id(self): + def test_delete_evaluation_missing_id(self): # WHEN I try to delete an evaluation without an id evaluation = Evaluation(name="test_evaluation") @@ -627,7 +623,7 @@ async def test_delete_evaluation_missing_id(self): with pytest.raises(ValueError, match="id must be set to delete an evaluation"): evaluation.delete(synapse_client=self.syn) - async def test_get_acl_missing_id(self): + def test_get_acl_missing_id(self): # WHEN I try to get ACL for an evaluation without an id evaluation = Evaluation(name="test_evaluation") @@ -635,7 +631,7 @@ async def test_get_acl_missing_id(self): with pytest.raises(ValueError, match="id must be set to get evaluation ACL"): evaluation.get_acl(synapse_client=self.syn) - async def test_get_permissions_missing_id(self): + def test_get_permissions_missing_id(self): # WHEN I try to get permissions for an evaluation without an id evaluation = Evaluation(name="test_evaluation") diff --git a/tests/integration/synapseclient/models/synchronous/test_file.py b/tests/integration/synapseclient/models/synchronous/test_file.py index 287bc82dc..842e29e0f 100644 --- a/tests/integration/synapseclient/models/synchronous/test_file.py +++ b/tests/integration/synapseclient/models/synchronous/test_file.py @@ -40,7 +40,7 @@ def file(self, schedule_for_cleanup: Callable[..., None]) -> None: version_label=str(uuid.uuid4()), ) - async def test_store_in_project(self, project_model: Project, file: File) -> None: + def test_store_in_project(self, project_model: Project, file: File) -> None: # GIVEN a file file.name = str(uuid.uuid4()) @@ -79,7 +79,7 @@ async def test_store_in_project(self, project_model: Project, file: File) -> Non assert file.file_handle.key is not None assert file.file_handle.external_url is None - async def test_activity_store_then_delete( + def test_activity_store_then_delete( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -126,7 +126,7 @@ async def test_activity_store_then_delete( assert file.activity is None assert file.version_number == 1 - async def test_store_in_folder(self, project_model: Project, file: File) -> None: + def test_store_in_folder(self, project_model: Project, file: File) -> None: # GIVEN a file file.name = str(uuid.uuid4()) @@ -171,7 +171,7 @@ async def test_store_in_folder(self, project_model: Project, file: File) -> None assert file.file_handle.key is not None assert file.file_handle.external_url is None - async def test_store_multiple_files(self, project_model: Project) -> None: + def test_store_multiple_files(self, project_model: Project) -> None: # GIVEN a file filename = utils.make_bogus_uuid_file() self.schedule_for_cleanup(filename) @@ -232,9 +232,7 @@ async def test_store_multiple_files(self, project_model: Project) -> None: assert file.file_handle.key is not None assert file.file_handle.external_url is None - async def test_store_change_filename( - self, project_model: Project, file: File - ) -> None: + def test_store_change_filename(self, project_model: Project, file: File) -> None: # GIVEN a file file.name = str(uuid.uuid4()) file.parent_id = project_model.id @@ -257,7 +255,7 @@ async def test_store_change_filename( assert file.etag is not None assert before_etag != file.etag - async def test_store_move_file(self, project_model: Project, file: File) -> None: + def test_store_move_file(self, project_model: Project, file: File) -> None: # GIVEN a file file.name = str(uuid.uuid4()) file.parent_id = project_model.id @@ -283,7 +281,7 @@ async def test_store_move_file(self, project_model: Project, file: File) -> None # AND the file does not have an updated ID assert before_file_id == file.id - async def test_store_same_data_file_handle_id(self, project_model: Project) -> None: + def test_store_same_data_file_handle_id(self, project_model: Project) -> None: # GIVEN a file filename = utils.make_bogus_uuid_file() self.schedule_for_cleanup(filename) @@ -323,7 +321,7 @@ async def test_store_same_data_file_handle_id(self, project_model: Project) -> N file_2.get(synapse_client=self.syn) ).file_handle.id - async def test_store_updated_file(self, project_model: Project) -> None: + def test_store_updated_file(self, project_model: Project) -> None: # GIVEN a file filename = utils.make_bogus_uuid_file() self.schedule_for_cleanup(filename) @@ -353,9 +351,7 @@ async def test_store_updated_file(self, project_model: Project) -> None: assert file.file_handle.id is not None assert before_file_handle_id != file.file_handle.id - async def test_store_and_get_activity( - self, project_model: Project, file: File - ) -> None: + def test_store_and_get_activity(self, project_model: Project, file: File) -> None: # GIVEN an activity activity = Activity( name="some_name", @@ -402,7 +398,7 @@ async def test_store_and_get_activity( # THEN I expect that the activity is not returned assert file_copy_2.activity is None - async def test_store_annotations(self, project_model: Project, file: File) -> None: + def test_store_annotations(self, project_model: Project, file: File) -> None: # GIVEN an annotation annotations_for_my_file = { "my_single_key_string": "a", @@ -440,7 +436,7 @@ async def test_store_annotations(self, project_model: Project, file: File) -> No assert file.annotations["my_key_double"] == [1.2, 3.4, 5.6] assert file.annotations["my_key_long"] == [1, 2, 3] - async def test_setting_annotations_directly( + def test_setting_annotations_directly( self, project_model: Project, file: File ) -> None: # GIVEN a file with the annotation @@ -475,7 +471,7 @@ async def test_setting_annotations_directly( assert file.annotations["my_key_double"] == [1.2, 3.4, 5.6] assert file.annotations["my_key_long"] == [1, 2, 3] - async def test_removing_annotations_to_none( + def test_removing_annotations_to_none( self, project_model: Project, file: File ) -> None: # GIVEN an annotation @@ -514,7 +510,7 @@ async def test_removing_annotations_to_none( file_copy = File(id=file.id, download_file=False).get(synapse_client=self.syn) assert not file_copy.annotations and isinstance(file_copy.annotations, dict) - async def test_removing_annotations_to_empty_dict( + def test_removing_annotations_to_empty_dict( self, project_model: Project, file: File ) -> None: # GIVEN an annotation @@ -553,9 +549,7 @@ async def test_removing_annotations_to_empty_dict( file_copy = File(id=file.id, download_file=False).get(synapse_client=self.syn) assert not file_copy.annotations and isinstance(file_copy.annotations, dict) - async def test_store_without_upload( - self, project_model: Project, file: File - ) -> None: + def test_store_without_upload(self, project_model: Project, file: File) -> None: # GIVEN a file file.name = str(uuid.uuid4()) @@ -598,7 +592,7 @@ async def test_store_without_upload( assert file.file_handle.bucket_name is None assert file.file_handle.key is None - async def test_store_without_upload_non_matching_md5( + def test_store_without_upload_non_matching_md5( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -619,7 +613,7 @@ async def test_store_without_upload_non_matching_md5( f"[{utils.md5_for_file_hex(file.path)}] for local file" in str(e.value) ) - async def test_store_without_upload_non_matching_size( + def test_store_without_upload_non_matching_size( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -667,9 +661,7 @@ async def test_store_without_upload_non_matching_size( assert file.file_handle.bucket_name is None assert file.file_handle.key is None - async def test_store_as_external_url( - self, project_model: Project, file: File - ) -> None: + def test_store_as_external_url(self, project_model: Project, file: File) -> None: # GIVEN a file file.name = str(uuid.uuid4()) @@ -717,7 +709,7 @@ async def test_store_as_external_url( assert file.file_handle.key is None assert file.file_handle.external_url == BOGUS_URL - async def test_store_as_external_url_with_content_size( + def test_store_as_external_url_with_content_size( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -770,7 +762,7 @@ async def test_store_as_external_url_with_content_size( assert file.file_handle.key is None assert file.file_handle.external_url == BOGUS_URL - async def test_store_as_external_url_with_content_size_and_md5( + def test_store_as_external_url_with_content_size_and_md5( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -827,7 +819,7 @@ async def test_store_as_external_url_with_content_size_and_md5( assert file.file_handle.external_url == BOGUS_URL assert file.file_handle.content_md5 == BOGUS_MD5 - async def test_store_conflict_with_existing_object( + def test_store_conflict_with_existing_object( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -887,7 +879,7 @@ async def test_store_conflict_with_existing_object( in str(e.value) ) - async def test_store_force_version_no_change( + def test_store_force_version_no_change( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -915,7 +907,7 @@ async def test_store_force_version_no_change( # THEN the version should not be updated assert file.version_number == 1 - async def test_store_force_version_with_change( + def test_store_force_version_with_change( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -945,9 +937,7 @@ async def test_store_force_version_with_change( # THEN the version should be updated assert file.version_number == 2 - async def test_store_is_restricted( - self, project_model: Project, file: File - ) -> None: + def test_store_is_restricted(self, project_model: Project, file: File) -> None: """Tests that setting is_restricted is calling the correct Synapse method. We are not testing the actual behavior of the method, only that it is called with the correct arguments. We do not want to actually restrict the file in Synapse as it @@ -968,7 +958,7 @@ async def test_store_is_restricted( # THEN I expect the file to be restricted assert intercepted.called - async def test_store_and_get_with_activity( + def test_store_and_get_with_activity( self, project_model: Project, file: File ) -> None: # GIVEN a file @@ -1049,7 +1039,7 @@ def file( ) return file - async def test_change_name( + def test_change_name( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1073,7 +1063,7 @@ async def test_change_name( assert file_copy.name == new_filename assert file_copy.content_type == CONTENT_TYPE - async def test_change_content_type_and_download( + def test_change_content_type_and_download( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1101,7 +1091,7 @@ async def test_change_content_type_and_download( assert file_copy.name == current_filename assert file_copy.content_type == CONTENT_TYPE_JSON - async def test_change_content_type_and_download_and_name( + def test_change_content_type_and_download_and_name( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1155,7 +1145,7 @@ def file( ) return file - async def test_from_id( + def test_from_id( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1169,7 +1159,7 @@ async def test_from_id( # THEN I expect the file to be returned assert file_copy.id == file.id - async def test_from_path( + def test_from_path( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1209,7 +1199,7 @@ def file( ) return file - async def test_delete( + def test_delete( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1225,7 +1215,7 @@ async def test_delete( file.get(synapse_client=self.syn) assert f"404 Client Error: Entity {file.id} is in trash can." in str(e.value) - async def test_delete_specific_version( + def test_delete_specific_version( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1282,7 +1272,7 @@ def file( return file - async def test_get_by_path( + def test_get_by_path( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1297,7 +1287,7 @@ async def test_get_by_path( # THEN I expect the file to be returned assert file_copy.id == file.id - async def test_get_by_id( + def test_get_by_id( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1314,7 +1304,7 @@ async def test_get_by_id( assert file_copy.id == file.id assert utils.equal_paths(file_copy.path, path_for_file) - async def test_get_previous_version( + def test_get_previous_version( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1333,7 +1323,7 @@ async def test_get_previous_version( assert file_copy.id == file.id assert file_copy.version_number == 1 - async def test_get_keep_both_for_matching_filenames( + def test_get_keep_both_for_matching_filenames( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1362,7 +1352,9 @@ async def test_get_keep_both_for_matching_filenames( file_2.change_metadata(download_as=file.name, synapse_client=self.syn) # WHEN I get the file with the default collision of `keep.both` - file_2 = File(id=file_2.id, path=os.path.dirname(file.path)).get() + file_2 = File(id=file_2.id, path=os.path.dirname(file.path)).get( + synapse_client=self.syn + ) # THEN I expect both files to exist assert file.path != file_2.path @@ -1374,7 +1366,7 @@ async def test_get_keep_both_for_matching_filenames( # AND the second file to have a different path assert os.path.basename(file_2.path) == f"{base_name}(1){extension}" - async def test_get_overwrite_local_for_matching_filenames( + def test_get_overwrite_local_for_matching_filenames( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1416,7 +1408,7 @@ async def test_get_overwrite_local_for_matching_filenames( assert file_1_md5 != file_2_md5 assert utils.md5_for_file(file_2.path).hexdigest() == file_2_md5 - async def test_get_keep_local_for_matching_filenames( + def test_get_keep_local_for_matching_filenames( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1459,7 +1451,7 @@ async def test_get_keep_local_for_matching_filenames( assert file_1_md5 != file_2_md5 assert utils.md5_for_file(file.path).hexdigest() == file_1_md5 - async def test_get_by_path_limit_search( + def test_get_by_path_limit_search( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1516,7 +1508,7 @@ def file( return file - async def test_copy_same_path( + def test_copy_same_path( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1538,7 +1530,7 @@ async def test_copy_same_path( # THEN I expect the paths to be the same file assert file_1.path == file_2.path - async def test_copy_annotations_and_activity( + def test_copy_annotations_and_activity( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1585,7 +1577,7 @@ async def test_copy_annotations_and_activity( assert file_1.annotations == file_2.annotations assert file_1.activity == file_2.activity - async def test_copy_activity_only( + def test_copy_activity_only( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1635,7 +1627,7 @@ async def test_copy_activity_only( assert not file_2.annotations and isinstance(file_2.annotations, dict) assert file_1.activity == file_2.activity - async def test_copy_with_no_activity_or_annotations( + def test_copy_with_no_activity_or_annotations( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse @@ -1693,7 +1685,7 @@ async def test_copy_with_no_activity_or_annotations( assert not file_2.annotations and isinstance(file_2.annotations, dict) assert file_2.activity is None - async def test_copy_previous_version( + def test_copy_previous_version( self, file: File, schedule_for_cleanup: Callable[..., None] ) -> None: # GIVEN a file stored in synapse diff --git a/tests/integration/synapseclient/models/synchronous/test_folder.py b/tests/integration/synapseclient/models/synchronous/test_folder.py index e48876669..deaef8bd5 100644 --- a/tests/integration/synapseclient/models/synchronous/test_folder.py +++ b/tests/integration/synapseclient/models/synchronous/test_folder.py @@ -95,14 +95,14 @@ def verify_folder_properties( assert isinstance(folder.annotations, dict) - async def test_store_folder_variations( + def test_store_folder_variations( self, project_model: Project, folder: Folder ) -> None: # Test Case 1: Simple folder storage # GIVEN a Folder object and a Project object # WHEN I store the Folder on Synapse - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # THEN I expect the stored Folder to have the expected properties @@ -125,7 +125,7 @@ async def test_store_folder_variations( # WHEN I store the Folder on Synapse stored_folder_with_annotations = folder_with_annotations.store( - parent=project_model + parent=project_model, synapse_client=self.syn ) self.schedule_for_cleanup(folder_with_annotations.id) @@ -136,7 +136,7 @@ async def test_store_folder_variations( Folder(id=stored_folder_with_annotations.id).get(synapse_client=self.syn) ).annotations == annotations - async def test_store_folder_with_files( + def test_store_folder_with_files( self, project_model: Project, file: File, folder: Folder ) -> None: # Test Case 1: Folder with a single file @@ -144,7 +144,7 @@ async def test_store_folder_with_files( folder.files.append(file) # WHEN I store the Folder on Synapse - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # THEN I expect the stored Folder to have the expected properties and files @@ -162,7 +162,7 @@ async def test_store_folder_with_files( # WHEN I store the Folder on Synapse stored_folder_with_multiple_files = folder_with_multiple_files.store( - parent=project_model + parent=project_model, synapse_client=self.syn ) self.schedule_for_cleanup(folder_with_multiple_files.id) @@ -171,7 +171,7 @@ async def test_store_folder_with_files( stored_folder_with_multiple_files, project_model.id, expected_files=files ) - async def test_store_folder_with_files_and_folders( + def test_store_folder_with_files_and_folders( self, project_model: Project, folder: Folder ) -> None: # GIVEN a folder with nested structure (files and sub-folders with files) @@ -187,7 +187,7 @@ async def test_store_folder_with_files_and_folders( folder.folders = folders # WHEN I store the Folder on Synapse - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # THEN I expect the stored Folder to have the expected properties, files, and folders @@ -212,11 +212,9 @@ def folder(self) -> Folder: folder = Folder(name=str(uuid.uuid4()), description=DESCRIPTION_FOLDER) return folder - async def test_get_folder_methods( - self, project_model: Project, folder: Folder - ) -> None: + def test_get_folder_methods(self, project_model: Project, folder: Folder) -> None: # GIVEN a Folder object stored in Synapse - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # Test Case 1: Get folder by ID @@ -259,17 +257,17 @@ def verify_folder_properties(self, folder: Folder, parent_id: str): assert folder.folders == [] assert not folder.annotations and isinstance(folder.annotations, dict) - async def test_delete_folder(self, project_model: Project, folder: Folder) -> None: + def test_delete_folder(self, project_model: Project, folder: Folder) -> None: # GIVEN a Folder object stored in Synapse - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # WHEN I delete the Folder from Synapse - stored_folder.delete() + stored_folder.delete(synapse_client=self.syn) # THEN I expect the folder to have been deleted with pytest.raises(SynapseHTTPError) as e: - stored_folder.get() + stored_folder.get(synapse_client=self.syn) assert f"404 Client Error: Entity {stored_folder.id} is in trash can." in str( e.value @@ -363,27 +361,31 @@ def verify_copied_folder( assert sub_file.name is not None assert sub_file.parent_id == sub_folder.id - async def test_copy_folder_with_files_and_folders( - self, project_model: Project - ) -> None: + def test_copy_folder_with_files_and_folders(self, project_model: Project) -> None: # GIVEN a nested folder structure with files and folders source_folder = self.create_nested_folder() source_folder.annotations = {"test": ["test"]} - stored_source_folder = source_folder.store(parent=project_model) + stored_source_folder = source_folder.store( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(stored_source_folder.id) # Test Case 1: Copy folder with all contents # Create first destination folder destination_folder_1 = Folder( name=str(uuid.uuid4()), description="Destination for folder copy 1" - ).store(parent=project_model) + ).store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(destination_folder_1.id) # WHEN I copy the folder to the destination folder - copied_folder = stored_source_folder.copy(parent_id=destination_folder_1.id) + copied_folder = stored_source_folder.copy( + parent_id=destination_folder_1.id, synapse_client=self.syn + ) # AND I sync the destination folder from Synapse - destination_folder_1.sync_from_synapse(recursive=False, download_file=False) + destination_folder_1.sync_from_synapse( + recursive=False, download_file=False, synapse_client=self.syn + ) # THEN I expect the copied Folder to have the expected properties assert len(destination_folder_1.folders) == 1 @@ -394,16 +396,20 @@ async def test_copy_folder_with_files_and_folders( # Create a second destination folder for the second test case destination_folder_2 = Folder( name=str(uuid.uuid4()), description="Destination for folder copy 2" - ).store(parent=project_model) + ).store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(destination_folder_2.id) # WHEN I copy the folder to the destination folder excluding files copied_folder_no_files = stored_source_folder.copy( - parent_id=destination_folder_2.id, exclude_types=["file"] + parent_id=destination_folder_2.id, + exclude_types=["file"], + synapse_client=self.syn, ) # AND I sync the destination folder from Synapse - destination_folder_2.sync_from_synapse(recursive=False, download_file=False) + destination_folder_2.sync_from_synapse( + recursive=False, download_file=False, synapse_client=self.syn + ) # THEN I expect the copied Folder to have the expected properties but no files assert len(destination_folder_2.folders) == 1 @@ -445,7 +451,7 @@ def folder(self) -> Folder: folder = Folder(name=str(uuid.uuid4()), description=DESCRIPTION_FOLDER) return folder - async def test_sync_from_synapse( + def test_sync_from_synapse( self, project_model: Project, file: File, folder: Folder ) -> None: # GIVEN a nested folder structure with files and folders @@ -463,11 +469,13 @@ async def test_sync_from_synapse( folder.folders = sub_folders # WHEN I store the Folder on Synapse - stored_folder = folder.store(parent=project_model) + stored_folder = folder.store(parent=project_model, synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # AND I sync the folder from Synapse - copied_folder = stored_folder.sync_from_synapse(path=root_directory_path) + copied_folder = stored_folder.sync_from_synapse( + path=root_directory_path, synapse_client=self.syn + ) # THEN I expect that the folder and its contents are synced from Synapse to disk # Verify files in root folder @@ -493,7 +501,7 @@ async def test_sync_from_synapse( == sub_file.file_handle.content_md5 ) - async def test_sync_all_entity_types(self, project_model: Project) -> None: + def test_sync_all_entity_types(self, project_model: Project) -> None: """Test syncing a folder with all supported entity types.""" # GIVEN a folder with one of each entity type from synapseclient.models import ( @@ -602,10 +610,12 @@ async def test_sync_all_entity_types(self, project_model: Project) -> None: scope_ids=[folder.id], ) submission_view = submission_view.store(synapse_client=self.syn) - self.schedule_for_cleanup(submission_view.id) + self.schedule_for_cleanup(submission_view) # WHEN I sync the folder from Synapse - synced_folder = folder.sync_from_synapse(recursive=False, download_file=False) + synced_folder = folder.sync_from_synapse( + recursive=False, download_file=False, synapse_client=self.syn + ) # THEN all entity types should be present assert len(synced_folder.files) == 1 @@ -646,7 +656,7 @@ async def test_sync_all_entity_types(self, project_model: Project) -> None: class TestFolderWalk: - """Tests for the synapseclient.models.Folder.walk and walk_async methods.""" + """Tests for the synapseclient.models.Folder.walk methods.""" @pytest.fixture(autouse=True, scope="function") def init( @@ -670,30 +680,30 @@ def create_test_hierarchy(self, project_model: Project) -> dict: folder = Folder( name=f"test_walk_folder_{str(uuid.uuid4())}", parent_id=project_model.id ) - folder = folder.store() + folder = folder.store(synapse_client=self.syn) self.schedule_for_cleanup(folder.id) # Create a file in the root folder root_file = self.create_file_instance(self.schedule_for_cleanup) root_file.parent_id = folder.id - root_file = root_file.store() + root_file = root_file.store(synapse_client=self.syn) self.schedule_for_cleanup(root_file.id) # Create nested folder and file nested_folder = Folder(name=f"nested_folder_{str(uuid.uuid4())[:8]}") nested_folder.parent_id = folder.id - nested_folder = nested_folder.store() + nested_folder = nested_folder.store(synapse_client=self.syn) self.schedule_for_cleanup(nested_folder.id) nested_file = self.create_file_instance(self.schedule_for_cleanup) nested_file.parent_id = nested_folder.id - nested_file = nested_file.store() + nested_file = nested_file.store(synapse_client=self.syn) self.schedule_for_cleanup(nested_file.id) # Create another nested folder with no files empty_folder = Folder(name=f"empty_folder_{str(uuid.uuid4())[:8]}") empty_folder.parent_id = folder.id - empty_folder = empty_folder.store() + empty_folder = empty_folder.store(synapse_client=self.syn) self.schedule_for_cleanup(empty_folder.id) return { @@ -704,13 +714,15 @@ def create_test_hierarchy(self, project_model: Project) -> dict: "empty_folder": empty_folder, } - async def test_walk_recursive_true(self, project_model: Project) -> None: + def test_walk_recursive_true(self, project_model: Project) -> None: """Test walk method with recursive=True.""" # GIVEN: A folder with a hierarchical structure hierarchy = self.create_test_hierarchy(project_model) # WHEN: Walking through the folder with recursive=True - results = list(hierarchy["folder"].walk(recursive=True)) + results = list( + hierarchy["folder"].walk(recursive=True, synapse_client=self.syn) + ) # THEN: Should get 3 results (folder root, nested_folder, empty_folder) assert len(results) == 3 @@ -750,13 +762,15 @@ async def test_walk_recursive_true(self, project_model: Project) -> None: assert len(empty_dirs) == 0 assert len(empty_nondirs) == 0 - async def test_walk_recursive_false(self, project_model: Project) -> None: + def test_walk_recursive_false(self, project_model: Project) -> None: """Test walk method with recursive=False.""" # GIVEN: A folder with a hierarchical structure hierarchy = self.create_test_hierarchy(project_model) # WHEN: Walking through the folder with recursive=False - results = list(hierarchy["folder"].walk(recursive=False)) + results = list( + hierarchy["folder"].walk(recursive=False, synapse_client=self.syn) + ) # THEN: Should get only 1 result (folder root only) assert len(results) == 1 diff --git a/tests/integration/synapseclient/models/synchronous/test_grid.py b/tests/integration/synapseclient/models/synchronous/test_grid.py index 27adec81b..019e25a3b 100644 --- a/tests/integration/synapseclient/models/synchronous/test_grid.py +++ b/tests/integration/synapseclient/models/synchronous/test_grid.py @@ -61,9 +61,7 @@ def record_set_fixture(self, project_model: Project) -> RecordSet: os.unlink(filename) raise - async def test_create_and_list_grid_sessions( - self, record_set_fixture: RecordSet - ) -> None: + def test_create_and_list_grid_sessions(self, record_set_fixture: RecordSet) -> None: # GIVEN: A Grid instance with a record_set_id grid = Grid(record_set_id=record_set_fixture.id) @@ -97,7 +95,7 @@ async def test_create_and_list_grid_sessions( assert our_session.started_by == created_grid.started_by assert our_session.source_entity_id == record_set_fixture.id - async def test_create_grid_session_and_reuse_session( + def test_create_grid_session_and_reuse_session( self, record_set_fixture: RecordSet ) -> None: # GIVEN: Create the first Grid instance with a record_set_id @@ -122,7 +120,7 @@ async def test_create_grid_session_and_reuse_session( assert created_grid2.started_on == created_grid1.started_on assert created_grid2.source_entity_id == record_set_fixture.id - async def test_create_grid_session_validation_error(self) -> None: + def test_create_grid_session_validation_error(self) -> None: # GIVEN: A Grid instance with no record_set_id or initial_query grid = Grid() @@ -133,7 +131,7 @@ async def test_create_grid_session_validation_error(self) -> None: ): grid.create(synapse_client=self.syn) - async def test_delete_grid_session(self, record_set_fixture: RecordSet) -> None: + def test_delete_grid_session(self, record_set_fixture: RecordSet) -> None: # GIVEN: Create a grid session first grid = Grid(record_set_id=record_set_fixture.id) created_grid = grid.create(synapse_client=self.syn) @@ -154,7 +152,7 @@ async def test_delete_grid_session(self, record_set_fixture: RecordSet) -> None: session_ids = [session.session_id for session in sessions] assert session_id not in session_ids - async def test_delete_grid_session_validation_error(self) -> None: + def test_delete_grid_session_validation_error(self) -> None: # GIVEN: A Grid instance with no session_id grid = Grid() diff --git a/tests/integration/synapseclient/models/synchronous/test_json_schema.py b/tests/integration/synapseclient/models/synchronous/test_json_schema.py index f68f6f7c1..20f2be6e6 100644 --- a/tests/integration/synapseclient/models/synchronous/test_json_schema.py +++ b/tests/integration/synapseclient/models/synchronous/test_json_schema.py @@ -48,18 +48,18 @@ def create_entity( entity_name = str(uuid.uuid4()) + name_suffix if entity_type == Project: - entity = Project(name=entity_name).store() + entity = Project(name=entity_name).store(synapse_client=self.syn) elif entity_type == Folder: folder = Folder(name=entity_name) - entity = folder.store(parent=project_model) + entity = folder.store(parent=project_model, synapse_client=self.syn) elif entity_type == File: file_fixture.name = entity_name - entity = file_fixture.store(parent=project_model) + entity = file_fixture.store(parent=project_model, synapse_client=self.syn) elif entity_type == Table: table_fixture.name = entity_name - entity = table_fixture.store() + entity = table_fixture.store(synapse_client=self.syn) elif entity_type == EntityView: - entity = entity_view_fixture.store() + entity = entity_view_fixture.store(synapse_client=self.syn) else: raise ValueError(f"Unsupported entity type: {entity_type}") @@ -139,7 +139,7 @@ def entity_view(self, project_model: Project) -> EntityView: return entity_view @pytest.mark.parametrize("entity_type", [Folder, Project, File, EntityView, Table]) - async def test_bind_schema( + def test_bind_schema( self, entity_type: Type[Union[Folder, Project, File, EntityView, Table]], project_model: Project, @@ -172,7 +172,7 @@ async def test_bind_schema( created_entity.unbind_schema(synapse_client=self.syn) @pytest.mark.parametrize("entity_type", [Folder, Project, File, EntityView, Table]) - async def test_get_schema( + def test_get_schema( self, entity_type: Type[Union[Folder, Project, File, EntityView, Table]], project_model: Project, @@ -207,7 +207,7 @@ async def test_get_schema( created_entity.unbind_schema(synapse_client=self.syn) @pytest.mark.parametrize("entity_type", [Folder, Project, File, EntityView, Table]) - async def test_unbind_schema( + def test_unbind_schema( self, entity_type: Type[Union[Folder, Project, File, EntityView, Table]], project_model: Project, @@ -244,7 +244,7 @@ async def test_unbind_schema( created_entity.get_schema(synapse_client=self.syn) @pytest.mark.parametrize("entity_type", [Folder, Project, File, EntityView, Table]) - async def test_get_schema_derived_keys( + def test_get_schema_derived_keys( self, entity_type: Type[Union[Folder, Project, File, EntityView, Table]], project_model: Project, @@ -282,7 +282,7 @@ async def test_get_schema_derived_keys( "productPrice": 100, } - created_entity.store() + created_entity.store(synapse_client=self.syn) response = created_entity.get_schema(synapse_client=self.syn) assert response.enable_derived_annotations == True @@ -298,7 +298,7 @@ async def test_get_schema_derived_keys( created_entity.unbind_schema(synapse_client=self.syn) @pytest.mark.parametrize("entity_type", [Folder, Project, File, EntityView, Table]) - async def test_validate_schema_invalid_annos( + def test_validate_schema_invalid_annos( self, entity_type: Type[Union[Folder, Project, File, EntityView, Table]], project_model: Project, @@ -331,7 +331,7 @@ async def test_validate_schema_invalid_annos( "productDescription": 1000, "productQuantity": "invalid string", } - created_entity.store() + created_entity.store(synapse_client=self.syn) # Ensure annotations are stored time.sleep(2) @@ -355,7 +355,7 @@ async def test_validate_schema_invalid_annos( created_entity.unbind_schema(synapse_client=self.syn) @pytest.mark.parametrize("entity_type", [Folder, Project, File, EntityView, Table]) - async def test_validate_schema_valid_annos( + def test_validate_schema_valid_annos( self, entity_type: Type[Union[Folder, Project, File, EntityView, Table]], project_model: Project, @@ -387,7 +387,7 @@ async def test_validate_schema_valid_annos( "productDescription": "This is a test product.", "productQuantity": 100, } - created_entity.store() + created_entity.store(synapse_client=self.syn) # Ensure annotations are stored time.sleep(2) response = created_entity.validate_schema(synapse_client=self.syn) @@ -397,7 +397,7 @@ async def test_validate_schema_valid_annos( created_entity.unbind_schema(synapse_client=self.syn) @pytest.mark.parametrize("entity_type", [Folder, Project]) - async def test_get_validation_statistics( + def test_get_validation_statistics( self, entity_type: Type[Union[Folder, Project, File, EntityView, Table]], project_model: Project, @@ -457,8 +457,8 @@ async def test_get_validation_statistics( "productQuantity": "invalid string", } - file_1.store(parent=created_entity) - file_2.store(parent=created_entity) + file_1.store(parent=created_entity, synapse_client=self.syn) + file_2.store(parent=created_entity, synapse_client=self.syn) # Ensure annotations are stored time.sleep(2) @@ -476,7 +476,7 @@ async def test_get_validation_statistics( created_entity.unbind_schema(synapse_client=self.syn) @pytest.mark.parametrize("entity_type", [Folder, Project]) - async def test_get_invalid_validation( + def test_get_invalid_validation( self, entity_type: Type[Union[Folder, Project]], project_model: Project, @@ -532,8 +532,8 @@ async def test_get_invalid_validation( "productQuantity": "invalid string", } - file_1.store(parent=created_entity) - file_2.store(parent=created_entity) + file_1.store(parent=created_entity, synapse_client=self.syn) + file_2.store(parent=created_entity, synapse_client=self.syn) # Ensure annotations are stored time.sleep(2) diff --git a/tests/integration/synapseclient/models/synchronous/test_materializedview.py b/tests/integration/synapseclient/models/synchronous/test_materializedview.py index 4ddbe5e22..39bd2f64b 100644 --- a/tests/integration/synapseclient/models/synchronous/test_materializedview.py +++ b/tests/integration/synapseclient/models/synchronous/test_materializedview.py @@ -1,4 +1,4 @@ -import asyncio +import time import uuid from typing import Callable @@ -16,7 +16,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_empty_defining_sql_validation(self, project_model: Project) -> None: + def test_empty_defining_sql_validation(self, project_model: Project) -> None: # GIVEN a MaterializedView with an empty defining SQL materialized_view = MaterializedView( name=str(uuid.uuid4()), @@ -35,9 +35,7 @@ async def test_empty_defining_sql_validation(self, project_model: Project) -> No "and must not be the empty string." in str(e.value) ) - async def test_table_without_columns_validation( - self, project_model: Project - ) -> None: + def test_table_without_columns_validation(self, project_model: Project) -> None: # GIVEN a table with no columns table_name = str(uuid.uuid4()) table = Table( @@ -63,7 +61,7 @@ async def test_table_without_columns_validation( assert f"400 Client Error: Schema for {table.id} is empty." in str(e.value) - async def test_invalid_sql_validation(self, project_model: Project) -> None: + def test_invalid_sql_validation(self, project_model: Project) -> None: # GIVEN a materialized view with invalid SQL materialized_view = MaterializedView( name=str(uuid.uuid4()), @@ -82,7 +80,7 @@ async def test_invalid_sql_validation(self, project_model: Project) -> None: "at line 1, column 1.\nWas expecting one of:" in str(e.value) ) - async def test_create_and_retrieve_materialized_view( + def test_create_and_retrieve_materialized_view( self, project_model: Project ) -> None: # GIVEN a table with columns @@ -124,7 +122,7 @@ async def test_create_and_retrieve_materialized_view( assert new_materialized_view.id == materialized_view.id assert new_materialized_view.description == materialized_view_description - async def test_update_materialized_view(self, project_model: Project) -> None: + def test_update_materialized_view(self, project_model: Project) -> None: # GIVEN a table with columns table_name = str(uuid.uuid4()) table = Table( @@ -168,7 +166,7 @@ async def test_update_materialized_view(self, project_model: Project) -> None: assert retrieved_materialized_view.description == new_description assert retrieved_materialized_view.defining_sql == new_sql - async def test_delete_materialized_view(self, project_model: Project) -> None: + def test_delete_materialized_view(self, project_model: Project) -> None: # GIVEN a table with columns table_name = str(uuid.uuid4()) table = Table( @@ -207,7 +205,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def setup_table_with_data(self, project_model: Project) -> Table: + def setup_table_with_data(self, project_model: Project) -> Table: """Helper method to create a table with data for testing""" table_name = str(uuid.uuid4()) table = Table( @@ -227,9 +225,9 @@ async def setup_table_with_data(self, project_model: Project) -> Table: return table - async def test_query_materialized_view(self, project_model: Project) -> None: + def test_query_materialized_view(self, project_model: Project) -> None: # GIVEN a table with data - table = await self.setup_table_with_data(project_model) + table = self.setup_table_with_data(project_model) # AND a materialized view based on the table materialized_view = MaterializedView( @@ -250,9 +248,9 @@ async def test_query_materialized_view(self, project_model: Project) -> None: assert query_result["name"].tolist() == ["Alice", "Bob"] assert query_result["age"].tolist() == [30, 25] - async def test_update_defining_sql(self, project_model: Project) -> None: + def test_update_defining_sql(self, project_model: Project) -> None: # GIVEN a table with data - table = await self.setup_table_with_data(project_model) + table = self.setup_table_with_data(project_model) # AND a materialized view based on the table materialized_view = MaterializedView( @@ -268,7 +266,7 @@ async def test_update_defining_sql(self, project_model: Project) -> None: materialized_view = materialized_view.store(synapse_client=self.syn) # AND querying the materialized view (with delay for eventual consistency) - asyncio.sleep(5) + time.sleep(5) query_result = materialized_view.query( f"SELECT * FROM {materialized_view.id}", synapse_client=self.syn ) @@ -285,7 +283,7 @@ async def test_update_defining_sql(self, project_model: Project) -> None: assert "age" not in retrieved_view.columns.keys() assert "name" in retrieved_view.columns.keys() - async def test_materialized_view_reflects_table_updates( + def test_materialized_view_reflects_table_updates( self, project_model: Project ) -> None: # GIVEN a table with columns but no data @@ -323,11 +321,11 @@ async def test_materialized_view_reflects_table_updates( table.store_rows(data, synapse_client=self.syn) # AND querying again (with delay for eventual consistency) - asyncio.sleep(5) + time.sleep(5) query_result = materialized_view.query( f"SELECT * FROM {materialized_view.id}", synapse_client=self.syn ) - await asyncio.sleep(5) + time.sleep(5) query_result = materialized_view.query( f"SELECT * FROM {materialized_view.id}", synapse_client=self.syn ) @@ -337,11 +335,11 @@ async def test_materialized_view_reflects_table_updates( assert query_result["name"].tolist() == ["Alice", "Bob"] assert query_result["age"].tolist() == [30, 25] - async def test_materialized_view_reflects_table_data_removal( + def test_materialized_view_reflects_table_data_removal( self, project_model: Project ) -> None: # GIVEN a table with data - table = await self.setup_table_with_data(project_model) + table = self.setup_table_with_data(project_model) # AND a materialized view based on the table materialized_view = MaterializedView( @@ -358,7 +356,7 @@ async def test_materialized_view_reflects_table_data_removal( ) # AND querying the materialized view (with delay for eventual consistency) - asyncio.sleep(5) + time.sleep(5) query_result = materialized_view.query( f"SELECT * FROM {materialized_view.id}", synapse_client=self.syn ) @@ -369,9 +367,9 @@ async def test_materialized_view_reflects_table_data_removal( # THEN the query results should reflect the removed data assert len(query_result) == 0 - async def test_query_part_mask(self, project_model: Project) -> None: + def test_query_part_mask(self, project_model: Project) -> None: # GIVEN a table with data - table = await self.setup_table_with_data(project_model) + table = self.setup_table_with_data(project_model) # AND a materialized view based on the table materialized_view = MaterializedView( @@ -402,9 +400,7 @@ async def test_query_part_mask(self, project_model: Project) -> None: assert query_result.count == 2 assert query_result.last_updated_on is not None - async def test_materialized_view_with_left_join( - self, project_model: Project - ) -> None: + def test_materialized_view_with_left_join(self, project_model: Project) -> None: # GIVEN two tables with related data table1 = Table( name=str(uuid.uuid4()), @@ -459,9 +455,7 @@ async def test_materialized_view_with_left_join( assert result["age"][0] == 30 assert pd.isna(result["age"][1]) - async def test_materialized_view_with_right_join( - self, project_model: Project - ) -> None: + def test_materialized_view_with_right_join(self, project_model: Project) -> None: # GIVEN two tables with related data table1 = Table( name=str(uuid.uuid4()), @@ -516,9 +510,7 @@ async def test_materialized_view_with_right_join( assert pd.isna(result["name"][1]) assert result["age"].tolist() == [30, 40] - async def test_materialized_view_with_inner_join( - self, project_model: Project - ) -> None: + def test_materialized_view_with_inner_join(self, project_model: Project) -> None: # GIVEN two tables with related data table1 = Table( name=str(uuid.uuid4()), @@ -572,7 +564,7 @@ async def test_materialized_view_with_inner_join( assert result["name"].tolist() == ["Alice"] assert result["age"].tolist() == [30] - async def test_materialized_view_with_union(self, project_model: Project) -> None: + def test_materialized_view_with_union(self, project_model: Project) -> None: # GIVEN two tables with data table1 = Table( name=str(uuid.uuid4()), diff --git a/tests/integration/synapseclient/models/synchronous/test_permissions.py b/tests/integration/synapseclient/models/synchronous/test_permissions.py index c86547a43..03d7fa461 100644 --- a/tests/integration/synapseclient/models/synchronous/test_permissions.py +++ b/tests/integration/synapseclient/models/synchronous/test_permissions.py @@ -1,8 +1,8 @@ """Integration tests for ACL on several models.""" -import asyncio import logging import random +import time import uuid from typing import Callable, Dict, List, Optional, Type, Union @@ -62,7 +62,7 @@ def table(self, project_model: Project) -> Table: parent_id=project_model.id, ) - async def create_entity( + def create_entity( self, entity_type: Type[Union[Project, Folder, File, Table]], project_model: Optional[Project] = None, @@ -74,34 +74,36 @@ async def create_entity( entity_name = str(uuid.uuid4()) + name_suffix if entity_type == Project: - entity = await Project(name=entity_name).store_async() + entity = Project(name=entity_name).store(synapse_client=self.syn) elif entity_type == Folder: - entity = await Folder(name=entity_name).store_async(parent=project_model) + entity = Folder(name=entity_name).store( + parent=project_model, synapse_client=self.syn + ) elif entity_type == File: file_fixture.name = entity_name - entity = await file_fixture.store_async(parent=project_model) + entity = file_fixture.store(parent=project_model, synapse_client=self.syn) elif entity_type == Table: table_fixture.name = entity_name - entity = await table_fixture.store_async() + entity = table_fixture.store(synapse_client=self.syn) else: raise ValueError(f"Unsupported entity type: {entity_type}") self.schedule_for_cleanup(entity.id) return entity - async def create_team(self, description: str = DESCRIPTION_FAKE_TEAM) -> Team: + def create_team(self, description: str = DESCRIPTION_FAKE_TEAM) -> Team: """Helper to create a team with cleanup handling""" name = TEAM_PREFIX + str(uuid.uuid4()) - team = await Team(name=name, description=description).create_async() + team = Team(name=name, description=description).create(synapse_client=self.syn) self.schedule_for_cleanup(team) return team @pytest.mark.parametrize("entity_type", [Project, Folder, File, Table]) - async def test_get_acl_default( + def test_get_acl_default( self, entity_type, project_model: Project, file: File, table: Table ) -> None: # GIVEN an entity created with default permissions - entity = await self.create_entity( + entity = self.create_entity( entity_type, project_model, file, table, name_suffix="_test_get_acl_default" ) @@ -109,7 +111,7 @@ async def test_get_acl_default( user = UserProfile().get(synapse_client=self.syn) # WHEN getting the permissions for the user on the entity - permissions = entity.get_acl(principal_id=user.id) + permissions = entity.get_acl(principal_id=user.id, synapse_client=self.syn) # THEN the expected default admin permissions should be present expected_permissions = [ @@ -125,11 +127,11 @@ async def test_get_acl_default( assert set(expected_permissions) == set(permissions) @pytest.mark.parametrize("entity_type", [Project, Folder, File, Table]) - async def test_get_acl_limited_permissions( + def test_get_acl_limited_permissions( self, entity_type, project_model: Project, file: File, table: Table ) -> None: # GIVEN an entity created with default permissions - entity = await self.create_entity( + entity = self.create_entity( entity_type, project_model, file, table, name_suffix="_test_get_acl_limited" ) @@ -147,23 +149,24 @@ async def test_get_acl_limited_permissions( entity.set_permissions( principal_id=user.id, access_type=limited_permissions, + synapse_client=self.syn, ) # WHEN getting the permissions for the user on the entity - permissions = entity.get_acl(principal_id=user.id) + permissions = entity.get_acl(principal_id=user.id, synapse_client=self.syn) # THEN only the limited permissions should be present assert set(limited_permissions) == set(permissions) @pytest.mark.parametrize("entity_type", [Project, Folder, File, Table]) - async def test_get_acl_through_team( + def test_get_acl_through_team( self, entity_type, project_model: Project, file: File, table: Table ) -> None: # GIVEN a team - team = await self.create_team() + team = self.create_team() # AND an entity created with default permissions - entity = await self.create_entity( + entity = self.create_entity( entity_type, project_model, file, table, name_suffix="_test_get_acl_team" ) @@ -183,27 +186,30 @@ async def test_get_acl_through_team( entity.set_permissions( principal_id=team.id, access_type=team_permissions, + synapse_client=self.syn, ) # AND the user has no direct permissions - entity.set_permissions(principal_id=user.id, access_type=[]) + entity.set_permissions( + principal_id=user.id, access_type=[], synapse_client=self.syn + ) # WHEN getting the permissions for the user on the entity - permissions = entity.get_acl(principal_id=user.id) + permissions = entity.get_acl(principal_id=user.id, synapse_client=self.syn) # THEN the permissions should match the team's permissions assert set(team_permissions) == set(permissions) @pytest.mark.parametrize("entity_type", [Project, Folder, File, Table]) - async def test_get_acl_through_multiple_teams( + def test_get_acl_through_multiple_teams( self, entity_type, project_model: Project, file: File, table: Table ) -> None: # GIVEN two teams - team_1 = await self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 1") - team_2 = await self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 2") + team_1 = self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 1") + team_2 = self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 2") # AND an entity created with default permissions - entity = await self.create_entity( + entity = self.create_entity( entity_type, project_model, file, @@ -227,17 +233,24 @@ async def test_get_acl_through_multiple_teams( entity.set_permissions( principal_id=team_1.id, access_type=team_1_permissions, + synapse_client=self.syn, ) # AND the second team has only READ and DOWNLOAD permissions team_2_permissions = ["READ", "DOWNLOAD"] - entity.set_permissions(principal_id=team_2.id, access_type=team_2_permissions) + entity.set_permissions( + principal_id=team_2.id, + access_type=team_2_permissions, + synapse_client=self.syn, + ) # AND the user has no direct permissions - entity.set_permissions(principal_id=user.id, access_type=[]) + entity.set_permissions( + principal_id=user.id, access_type=[], synapse_client=self.syn + ) # WHEN getting the permissions for the user on the entity - permissions = entity.get_acl(principal_id=user.id) + permissions = entity.get_acl(principal_id=user.id, synapse_client=self.syn) # THEN the permissions should be the combined set from both teams expected_permissions = [ @@ -253,11 +266,11 @@ async def test_get_acl_through_multiple_teams( assert set(expected_permissions) == set(permissions) @pytest.mark.parametrize("entity_type", [Project, Folder, File, Table]) - async def test_get_acl_with_public_and_authenticated_users( + def test_get_acl_with_public_and_authenticated_users( self, entity_type, project_model: Project, file: File, table: Table ) -> None: # GIVEN an entity created with default permissions - entity = await self.create_entity( + entity = self.create_entity( entity_type, project_model, file, @@ -269,11 +282,15 @@ async def test_get_acl_with_public_and_authenticated_users( user = UserProfile().get(synapse_client=self.syn) # AND public users have READ permission - entity.set_permissions(principal_id=PUBLIC, access_type=["READ"]) + entity.set_permissions( + principal_id=PUBLIC, access_type=["READ"], synapse_client=self.syn + ) # AND authenticated users have READ and DOWNLOAD permissions entity.set_permissions( - principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"] + principal_id=AUTHENTICATED_USERS, + access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # AND the user has specific permissions (excluding DOWNLOAD) @@ -289,16 +306,17 @@ async def test_get_acl_with_public_and_authenticated_users( entity.set_permissions( principal_id=user.id, access_type=user_permissions, + synapse_client=self.syn, ) # WHEN getting public permissions (no principal_id) - public_permissions = entity.get_acl() + public_permissions = entity.get_acl(synapse_client=self.syn) # THEN only public permissions should be present assert set(["READ"]) == set(public_permissions) # WHEN getting the permissions for the authenticated user - user_permissions = entity.get_acl(principal_id=user.id) + user_permissions = entity.get_acl(principal_id=user.id, synapse_client=self.syn) # THEN the permissions should include direct user permissions plus # permissions from authenticated users and public users @@ -314,19 +332,19 @@ async def test_get_acl_with_public_and_authenticated_users( ] assert set(expected_permissions) == set(user_permissions) - async def test_get_acl_for_subfolder_with_different_permissions( + def test_get_acl_for_subfolder_with_different_permissions( self, project_model: Project ) -> None: # GIVEN a parent folder with default permissions - parent_folder = await Folder( - name=str(uuid.uuid4()) + "_parent_folder_test" - ).store_async(parent=project_model) + parent_folder = Folder(name=str(uuid.uuid4()) + "_parent_folder_test").store( + parent=project_model, synapse_client=self.syn + ) self.schedule_for_cleanup(parent_folder.id) # AND a subfolder created inside the parent folder - subfolder = await Folder( - name=str(uuid.uuid4()) + "_subfolder_test" - ).store_async(parent=parent_folder) + subfolder = Folder(name=str(uuid.uuid4()) + "_subfolder_test").store( + parent=parent_folder, synapse_client=self.syn + ) self.schedule_for_cleanup(subfolder.id) # AND the user that created the folders @@ -343,13 +361,18 @@ async def test_get_acl_for_subfolder_with_different_permissions( subfolder.set_permissions( principal_id=user.id, access_type=limited_permissions, + synapse_client=self.syn, ) # WHEN getting permissions for the subfolder - subfolder_permissions = subfolder.get_acl(principal_id=user.id) + subfolder_permissions = subfolder.get_acl( + principal_id=user.id, synapse_client=self.syn + ) # AND getting permissions for the parent folder - parent_permissions = parent_folder.get_acl(principal_id=user.id) + parent_permissions = parent_folder.get_acl( + principal_id=user.id, synapse_client=self.syn + ) # THEN the subfolder should have the limited permissions assert set(limited_permissions) == set(subfolder_permissions) @@ -368,11 +391,11 @@ async def test_get_acl_for_subfolder_with_different_permissions( assert set(expected_parent_permissions) == set(parent_permissions) @pytest.mark.parametrize("entity_type", [Project, Folder, File, Table]) - async def test_remove_team_permissions_with_empty_access_type( + def test_remove_team_permissions_with_empty_access_type( self, entity_type, project_model: Project, file: File, table: Table ) -> None: # GIVEN an entity created with default permissions - entity = await self.create_entity( + entity = self.create_entity( entity_type, project_model, file, @@ -381,31 +404,33 @@ async def test_remove_team_permissions_with_empty_access_type( ) # AND a test team - team = await self.create_team() + team = self.create_team() # AND the team initially has specific permissions initial_team_permissions = ["READ", "UPDATE", "CREATE", "DOWNLOAD"] entity.set_permissions( principal_id=team.id, access_type=initial_team_permissions, + synapse_client=self.syn, ) # WHEN verifying the team has the initial permissions - team_acl_before = entity.get_acl(principal_id=team.id) + team_acl_before = entity.get_acl(principal_id=team.id, synapse_client=self.syn) assert set(initial_team_permissions) == set(team_acl_before) # AND WHEN removing the team's permissions by setting access_type to empty list entity.set_permissions( principal_id=team.id, access_type=[], # Empty list to remove permissions + synapse_client=self.syn, ) # THEN the team should have no permissions - team_acl_after = entity.get_acl(principal_id=team.id) + team_acl_after = entity.get_acl(principal_id=team.id, synapse_client=self.syn) assert team_acl_after == [] # AND the team should not appear in the full ACL list with any permissions - all_acls = entity.list_acl() + all_acls = entity.list_acl(synapse_client=self.syn) team_acl_entries = [ acl for acl in all_acls.entity_acl @@ -416,11 +441,11 @@ async def test_remove_team_permissions_with_empty_access_type( ), f"Team {team.id} should have no ACL entries but found: {team_acl_entries}" # AND other entities should still maintain their permissions (verify no side effects) - user = await UserProfile().get_async(synapse_client=self.syn) - user_acl = entity.get_acl(principal_id=user.id) + user = UserProfile().get(synapse_client=self.syn) + user_acl = entity.get_acl(principal_id=user.id, synapse_client=self.syn) assert len(user_acl) > 0, "User permissions should remain intact" - async def test_table_permissions(self, project_model: Project) -> None: + def test_table_permissions(self, project_model: Project) -> None: """Comprehensive test for Table permissions - setting, listing, and deleting.""" # GIVEN a table with test data columns = [ @@ -432,40 +457,44 @@ async def test_table_permissions(self, project_model: Project) -> None: columns=columns, parent_id=project_model.id, ) - table = await table.store_async() + table = table.store(synapse_client=self.syn) self.schedule_for_cleanup(table.id) # AND a test team - team = await self.create_team() - user = await UserProfile().get_async(synapse_client=self.syn) + team = self.create_team() + user = UserProfile().get(synapse_client=self.syn) # WHEN setting various permissions # Set team permissions team_permissions = ["READ", "UPDATE", "CREATE"] table.set_permissions( - principal_id=team.id, - access_type=team_permissions, + principal_id=team.id, access_type=team_permissions, synapse_client=self.syn ) # Set authenticated users permissions table.set_permissions( principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) # THEN listing permissions should show all set permissions # Check team permissions - team_acl = table.get_acl(principal_id=team.id) + team_acl = table.get_acl(principal_id=team.id, synapse_client=self.syn) assert set(team_permissions) == set(team_acl) # Check authenticated users permissions - auth_acl = table.get_acl(principal_id=AUTHENTICATED_USERS) + auth_acl = table.get_acl( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert set(["READ", "DOWNLOAD"]) == set(auth_acl) # Check user effective permissions (should include permissions from all sources) - user_effective_acl = table.get_acl(principal_id=user.id) + user_effective_acl = table.get_acl( + principal_id=user.id, synapse_client=self.syn + ) expected_user_permissions = { "READ", "DELETE", @@ -479,24 +508,30 @@ async def test_table_permissions(self, project_model: Project) -> None: assert expected_user_permissions.issubset(set(user_effective_acl)) # AND listing all ACLs should return complete ACL information - all_acls = table.list_acl() + all_acls = table.list_acl(synapse_client=self.syn) assert isinstance(all_acls, AclListResult) assert len(all_acls.entity_acl) >= 3 # User, team, authenticated_users # WHEN deleting specific permissions for the team - table.set_permissions(principal_id=team.id, access_type=[]) + table.set_permissions( + principal_id=team.id, access_type=[], synapse_client=self.syn + ) - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) # THEN team should no longer have permissions - team_acl_after_delete = table.get_acl(principal_id=team.id) + team_acl_after_delete = table.get_acl( + principal_id=team.id, synapse_client=self.syn + ) assert team_acl_after_delete == [] # BUT other permissions should remain - auth_acl_after = table.get_acl(principal_id=AUTHENTICATED_USERS) + auth_acl_after = table.get_acl( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert set(["READ", "DOWNLOAD"]) == set(auth_acl_after) - async def test_entity_view_permissions(self, project_model: Project) -> None: + def test_entity_view_permissions(self, project_model: Project) -> None: """Comprehensive test for EntityView permissions - setting, listing, and deleting.""" # GIVEN an entity view entity_view = EntityView( @@ -505,25 +540,25 @@ async def test_entity_view_permissions(self, project_model: Project) -> None: scope_ids=[project_model.id], view_type_mask=0x01, # File view ) - entity_view = await entity_view.store_async() + entity_view = entity_view.store(synapse_client=self.syn) self.schedule_for_cleanup(entity_view.id) # AND test subjects - team = await self.create_team() - user = await UserProfile().get_async(synapse_client=self.syn) + team = self.create_team() + user = UserProfile().get(synapse_client=self.syn) # WHEN setting comprehensive permissions # Set team permissions (moderate permissions) team_permissions = ["READ", "UPDATE", "MODERATE"] entity_view.set_permissions( - principal_id=team.id, - access_type=team_permissions, + principal_id=team.id, access_type=team_permissions, synapse_client=self.syn ) # Set authenticated users permissions entity_view.set_permissions( principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # Set limited user permissions (remove some admin permissions) @@ -538,49 +573,60 @@ async def test_entity_view_permissions(self, project_model: Project) -> None: entity_view.set_permissions( principal_id=user.id, access_type=limited_user_permissions, + synapse_client=self.syn, ) - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) # THEN listing permissions should reflect all changes # Verify team permissions - team_acl = entity_view.get_acl(principal_id=team.id) + team_acl = entity_view.get_acl(principal_id=team.id, synapse_client=self.syn) assert set(team_permissions) == set(team_acl) # Verify authenticated users permissions - auth_acl = entity_view.get_acl(principal_id=AUTHENTICATED_USERS) + auth_acl = entity_view.get_acl( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert set(["READ", "DOWNLOAD"]) == set(auth_acl) # Verify user permissions include both direct and inherited permissions - user_acl = entity_view.get_acl(principal_id=user.id) + user_acl = entity_view.get_acl(principal_id=user.id, synapse_client=self.syn) expected_user_permissions = set( limited_user_permissions + ["DOWNLOAD"] ) # Includes auth users perm assert expected_user_permissions == set(user_acl) # Verify complete ACL listing - all_acls = entity_view.list_acl() + all_acls = entity_view.list_acl(synapse_client=self.syn) assert isinstance(all_acls, AclListResult) assert len(all_acls.entity_acl) >= 3 # User, team, authenticated_users # WHEN deleting authenticated users permissions - entity_view.set_permissions(principal_id=AUTHENTICATED_USERS, access_type=[]) + entity_view.set_permissions( + principal_id=AUTHENTICATED_USERS, access_type=[], synapse_client=self.syn + ) - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) # THEN authenticated users should lose permissions - auth_acl_after = entity_view.get_acl(principal_id=AUTHENTICATED_USERS) + auth_acl_after = entity_view.get_acl( + principal_id=AUTHENTICATED_USERS, synapse_client=self.syn + ) assert auth_acl_after == [] # AND user permissions should no longer include DOWNLOAD - user_acl_after = entity_view.get_acl(principal_id=user.id) + user_acl_after = entity_view.get_acl( + principal_id=user.id, synapse_client=self.syn + ) assert set(limited_user_permissions + ["MODERATE"]) == set(user_acl_after) # BUT team permissions should remain - team_acl_after = entity_view.get_acl(principal_id=team.id) + team_acl_after = entity_view.get_acl( + principal_id=team.id, synapse_client=self.syn + ) assert set(team_permissions) == set(team_acl_after) - async def test_submission_view_permissions(self, project_model: Project) -> None: + def test_submission_view_permissions(self, project_model: Project) -> None: """Comprehensive test for SubmissionView permissions - setting, listing, and deleting.""" # GIVEN a submission view submission_view = SubmissionView( @@ -588,13 +634,13 @@ async def test_submission_view_permissions(self, project_model: Project) -> None parent_id=project_model.id, scope_ids=[project_model.id], ) - submission_view = await submission_view.store_async() - self.schedule_for_cleanup(submission_view.id) + submission_view = submission_view.store(synapse_client=self.syn) + self.schedule_for_cleanup(submission_view) # AND test subjects - team1 = await self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - team1") - team2 = await self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - team2") - user = await UserProfile().get_async(synapse_client=self.syn) + team1 = self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - team1") + team2 = self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - team2") + user = UserProfile().get(synapse_client=self.syn) # WHEN setting overlapping permissions across multiple teams # Team1 gets full read/write access @@ -602,6 +648,7 @@ async def test_submission_view_permissions(self, project_model: Project) -> None submission_view.set_permissions( principal_id=team1.id, access_type=team1_permissions, + synapse_client=self.syn, ) # Team2 gets read-only access with download @@ -609,12 +656,12 @@ async def test_submission_view_permissions(self, project_model: Project) -> None submission_view.set_permissions( principal_id=team2.id, access_type=team2_permissions, + synapse_client=self.syn, ) # Public gets read access submission_view.set_permissions( - principal_id=PUBLIC, - access_type=["READ"], + principal_id=PUBLIC, access_type=["READ"], synapse_client=self.syn ) # User gets minimal direct permissions @@ -622,54 +669,73 @@ async def test_submission_view_permissions(self, project_model: Project) -> None submission_view.set_permissions( principal_id=user.id, access_type=user_direct_permissions, + synapse_client=self.syn, ) - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) # THEN listing permissions should show proper aggregation # Check individual team permissions - team1_acl = submission_view.get_acl(principal_id=team1.id) + team1_acl = submission_view.get_acl( + principal_id=team1.id, synapse_client=self.syn + ) assert set(team1_permissions) == set(team1_acl) - team2_acl = submission_view.get_acl(principal_id=team2.id) + team2_acl = submission_view.get_acl( + principal_id=team2.id, synapse_client=self.syn + ) assert set(team2_permissions) == set(team2_acl) # Check public permissions - public_acl = submission_view.get_acl() + public_acl = submission_view.get_acl(synapse_client=self.syn) assert set(["READ"]) == set(public_acl) # Check user effective permissions (should aggregate from all teams) - user_effective_acl = submission_view.get_acl(principal_id=user.id) + user_effective_acl = submission_view.get_acl( + principal_id=user.id, synapse_client=self.syn + ) expected_effective = set( user_direct_permissions + team1_permissions + team2_permissions ) assert expected_effective == set(user_effective_acl) # Verify complete ACL structure - all_acls = submission_view.list_acl() + all_acls = submission_view.list_acl(synapse_client=self.syn) assert isinstance(all_acls, AclListResult) assert len(all_acls.entity_acl) >= 4 # User, team1, team2, public # WHEN selectively deleting permissions # Remove PUBLIC and team permissions - submission_view.set_permissions(principal_id=PUBLIC, access_type=[]) - submission_view.set_permissions(principal_id=team1.id, access_type=[]) + submission_view.set_permissions( + principal_id=PUBLIC, access_type=[], synapse_client=self.syn + ) + submission_view.set_permissions( + principal_id=team1.id, access_type=[], synapse_client=self.syn + ) # THEN PUBLIC should lose all permissions - public_acl_after = submission_view.get_acl(principal_id=PUBLIC) + public_acl_after = submission_view.get_acl( + principal_id=PUBLIC, synapse_client=self.syn + ) assert public_acl_after == [] # AND team should lose all permissions - team_acl_after = submission_view.get_acl(principal_id=team1.id) + team_acl_after = submission_view.get_acl( + principal_id=team1.id, synapse_client=self.syn + ) assert team_acl_after == [] # AND user effective permissions should no longer include team1 permissions - user_effective_after = submission_view.get_acl(principal_id=user.id) + user_effective_after = submission_view.get_acl( + principal_id=user.id, synapse_client=self.syn + ) expected_after = set(user_direct_permissions + team2_permissions) assert expected_after == set(user_effective_after) # BUT other permissions should remain unchanged - team2_acl_after = submission_view.get_acl(principal_id=team2.id) + team2_acl_after = submission_view.get_acl( + principal_id=team2.id, synapse_client=self.syn + ) assert set(team2_permissions) == set(team2_acl_after) @@ -681,22 +747,22 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def create_team(self, description: str = DESCRIPTION_FAKE_TEAM) -> Team: + def create_team(self, description: str = DESCRIPTION_FAKE_TEAM) -> Team: """Helper to create a team with cleanup handling""" name = TEAM_PREFIX + str(uuid.uuid4()) - team = await Team(name=name, description=description).create_async() + team = Team(name=name, description=description).create(synapse_client=self.syn) self.schedule_for_cleanup(team) return team - async def test_get_permissions_default(self) -> None: + def test_get_permissions_default(self) -> None: # GIVEN a project created with default permissions - project = await Project( + project = Project( name=str(uuid.uuid4()) + "_test_get_permissions_default" - ).store_async() + ).store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # WHEN getting the permissions for the current user - permissions = project.get_permissions() + permissions = project.get_permissions(synapse_client=self.syn) # THEN all default permissions should be present expected_permissions = [ @@ -711,36 +777,38 @@ async def test_get_permissions_default(self) -> None: ] assert set(expected_permissions) == set(permissions.access_types) - async def test_get_permissions_with_limited_access(self) -> None: + def test_get_permissions_with_limited_access(self) -> None: # GIVEN a project created with default permissions - project = await Project( + project = Project( name=str(uuid.uuid4()) + "_test_get_permissions_limited" - ).store_async() + ).store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND the current user that created the project user = UserProfile().get(synapse_client=self.syn) # AND the permissions for the user are set to READ only - project.set_permissions(principal_id=user.id, access_type=["READ"]) + project.set_permissions( + principal_id=user.id, access_type=["READ"], synapse_client=self.syn + ) # WHEN getting the permissions for the current user - permissions = project.get_permissions() + permissions = project.get_permissions(synapse_client=self.syn) # THEN READ and CHANGE_SETTINGS permissions should be present # Note: CHANGE_SETTINGS is bound to ownerId and can't be removed expected_permissions = ["READ", "CHANGE_SETTINGS"] assert set(expected_permissions) == set(permissions.access_types) - async def test_get_permissions_through_teams(self) -> None: + def test_get_permissions_through_teams(self) -> None: # GIVEN two teams - team_1 = await self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 1") - team_2 = await self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 2") + team_1 = self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 1") + team_2 = self.create_team(description=f"{DESCRIPTION_FAKE_TEAM} - 2") # AND a project created with default permissions - project = await Project( + project = Project( name=str(uuid.uuid4()) + "_test_get_permissions_through_teams" - ).store_async() + ).store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND the current user that created the project @@ -759,18 +827,23 @@ async def test_get_permissions_through_teams(self) -> None: project.set_permissions( principal_id=team_1.id, access_type=team_1_permissions, + synapse_client=self.syn, ) # AND team 2 has only READ and DOWNLOAD permissions project.set_permissions( - principal_id=team_2.id, access_type=["READ", "DOWNLOAD"] + principal_id=team_2.id, + access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # AND the user has no direct permissions - project.set_permissions(principal_id=user.id, access_type=[]) + project.set_permissions( + principal_id=user.id, access_type=[], synapse_client=self.syn + ) # WHEN getting the permissions for the current user - permissions = project.get_permissions() + permissions = project.get_permissions(synapse_client=self.syn) # THEN the effective permissions should be the combined permissions from both teams expected_permissions = [ @@ -785,11 +858,11 @@ async def test_get_permissions_through_teams(self) -> None: ] assert set(expected_permissions) == set(permissions.access_types) - async def test_get_permissions_with_authenticated_users(self) -> None: + def test_get_permissions_with_authenticated_users(self) -> None: # GIVEN a project created with default permissions - project = await Project( + project = Project( name=str(uuid.uuid4()) + "_test_get_permissions_authenticated" - ).store_async() + ).store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND the current user that created the project @@ -797,7 +870,9 @@ async def test_get_permissions_with_authenticated_users(self) -> None: # AND authenticated users have READ and DOWNLOAD permissions project.set_permissions( - principal_id=AUTHENTICATED_USERS, access_type=["READ", "DOWNLOAD"] + principal_id=AUTHENTICATED_USERS, + access_type=["READ", "DOWNLOAD"], + synapse_client=self.syn, ) # AND the current user has specific permissions (without DOWNLOAD) @@ -813,10 +888,11 @@ async def test_get_permissions_with_authenticated_users(self) -> None: project.set_permissions( principal_id=user.id, access_type=user_permissions, + synapse_client=self.syn, ) # WHEN getting the permissions for the current user - permissions = project.get_permissions() + permissions = project.get_permissions(synapse_client=self.syn) # THEN the permissions should include user permissions plus # the DOWNLOAD permission from authenticated users @@ -858,28 +934,30 @@ def file(self, schedule_for_cleanup: Callable[..., None]) -> File: schedule_for_cleanup(filename) return File(path=filename) - async def _set_custom_permissions( - self, entity: Union[File, Folder, Project] - ) -> None: + def _set_custom_permissions(self, entity: Union[File, Folder, Project]) -> None: """Helper to set custom permissions on an entity so we can verify deletion.""" # Set custom permissions for authenticated users - entity.set_permissions(principal_id=AUTHENTICATED_USERS, access_type=["READ"]) + entity.set_permissions( + principal_id=AUTHENTICATED_USERS, + access_type=["READ"], + synapse_client=self.syn, + ) # Verify permissions were set - acl = entity.get_acl(principal_id=AUTHENTICATED_USERS) + acl = entity.get_acl(principal_id=AUTHENTICATED_USERS, synapse_client=self.syn) assert "READ" in acl return acl - async def _verify_permissions_deleted( - self, entity: Union[File, Folder, Project] - ) -> None: + def _verify_permissions_deleted(self, entity: Union[File, Folder, Project]) -> None: """Helper to verify that permissions have been deleted (entity inherits from parent).""" for attempt in range(self.verification_attempts): - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) acl = entity.get_acl( - principal_id=AUTHENTICATED_USERS, check_benefactor=False + principal_id=AUTHENTICATED_USERS, + check_benefactor=False, + synapse_client=self.syn, ) if not acl: @@ -891,14 +969,16 @@ async def _verify_permissions_deleted( f"[id: {entity.id}, name: {entity.name}, {entity.__class__}]." ) - async def _verify_permissions_not_deleted( + def _verify_permissions_not_deleted( self, entity: Union[File, Folder, Project] ) -> bool: """Helper to verify that permissions are still set on an entity.""" for attempt in range(self.verification_attempts): - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) acl = entity.get_acl( - principal_id=AUTHENTICATED_USERS, check_benefactor=False + principal_id=AUTHENTICATED_USERS, + check_benefactor=False, + synapse_client=self.syn, ) if "READ" in acl: return True @@ -908,7 +988,7 @@ async def _verify_permissions_not_deleted( return True - async def _verify_list_acl_functionality( + def _verify_list_acl_functionality( self, entity: Union[File, Folder, Project], expected_entity_count: int, @@ -919,7 +999,7 @@ async def _verify_list_acl_functionality( ) -> AclListResult: """Helper to verify list_acl functionality and return results.""" for attempt in range(self.verification_attempts): - await asyncio.sleep(random.randint(10, 20)) + time.sleep(random.randint(10, 20)) acl_result = entity.list_acl( recursive=recursive, include_container_content=include_container_content, @@ -980,7 +1060,7 @@ def _verify_log_messages( assert "Permission Deletion Impact Analysis" in log_text assert "End of Dry Run Analysis" in log_text - async def create_simple_tree_structure( + def create_simple_tree_structure( self, project_model: Project ) -> Dict[str, Union[Folder, File]]: """ @@ -993,14 +1073,14 @@ async def create_simple_tree_structure( └── file_1 ``` """ - folder_a = await Folder(name=f"folder_a_{uuid.uuid4()}").store_async( + folder_a = Folder(name=f"folder_a_{uuid.uuid4()}").store( parent=project_model, synapse_client=self.syn ) self.schedule_for_cleanup(folder_a.id) - file_1 = await File( + file_1 = File( path=utils.make_bogus_uuid_file(), name=f"file_1_{uuid.uuid4()}" - ).store_async(parent=folder_a, synapse_client=self.syn) + ).store(parent=folder_a, synapse_client=self.syn) self.schedule_for_cleanup(file_1.id) return { @@ -1008,7 +1088,7 @@ async def create_simple_tree_structure( "file_1": file_1, } - async def create_deep_nested_structure( + def create_deep_nested_structure( self, project_model: Project ) -> Dict[str, Union[Folder, File]]: """ @@ -1027,50 +1107,47 @@ async def create_deep_nested_structure( └── file_at_4 ``` """ - level_1 = await Folder(name=f"level_1_{uuid.uuid4()}").store_async( + level_1 = Folder(name=f"level_1_{uuid.uuid4()}").store( parent=project_model, synapse_client=self.syn ) self.schedule_for_cleanup(level_1.id) # Create file_at_1 and level_2 in parallel since they don't depend on each other - file_at_1_task = File( + file_at_1 = File( path=utils.make_bogus_uuid_file(), name=f"file_at_1_{uuid.uuid4()}" - ).store_async(parent=level_1, synapse_client=self.syn) - level_2_task = Folder(name=f"level_2_{uuid.uuid4()}").store_async( + ).store(parent=level_1, synapse_client=self.syn) + level_2 = Folder(name=f"level_2_{uuid.uuid4()}").store( parent=level_1, synapse_client=self.syn ) - file_at_1, level_2 = await asyncio.gather(file_at_1_task, level_2_task) self.schedule_for_cleanup(file_at_1.id) self.schedule_for_cleanup(level_2.id) # Create file_at_2 and level_3 in parallel since they don't depend on each other - file_at_2_task = File( + file_at_2 = File( path=utils.make_bogus_uuid_file(), name=f"file_at_2_{uuid.uuid4()}" - ).store_async(parent=level_2, synapse_client=self.syn) - level_3_task = Folder(name=f"level_3_{uuid.uuid4()}").store_async( + ).store(parent=level_2, synapse_client=self.syn) + level_3 = Folder(name=f"level_3_{uuid.uuid4()}").store( parent=level_2, synapse_client=self.syn ) - file_at_2, level_3 = await asyncio.gather(file_at_2_task, level_3_task) self.schedule_for_cleanup(file_at_2.id) self.schedule_for_cleanup(level_3.id) # Create file_at_3 and level_4 in parallel since they don't depend on each other - file_at_3_task = File( + file_at_3 = File( path=utils.make_bogus_uuid_file(), name=f"file_at_3_{uuid.uuid4()}" - ).store_async(parent=level_3, synapse_client=self.syn) - level_4_task = Folder(name=f"level_4_{uuid.uuid4()}").store_async( + ).store(parent=level_3, synapse_client=self.syn) + level_4 = Folder(name=f"level_4_{uuid.uuid4()}").store( parent=level_3, synapse_client=self.syn ) - file_at_3, level_4 = await asyncio.gather(file_at_3_task, level_4_task) self.schedule_for_cleanup(file_at_3.id) self.schedule_for_cleanup(level_4.id) - file_at_4 = await File( + file_at_4 = File( path=utils.make_bogus_uuid_file(), name=f"file_at_4_{uuid.uuid4()}" - ).store_async(parent=level_4, synapse_client=self.syn) + ).store(parent=level_4, synapse_client=self.syn) self.schedule_for_cleanup(file_at_4.id) return { @@ -1084,7 +1161,7 @@ async def create_deep_nested_structure( "file_at_4": file_at_4, } - async def create_wide_tree_structure( + def create_wide_tree_structure( self, project_model: Project ) -> Dict[str, Union[Folder, List[Union[Folder, File]]]]: """ @@ -1103,34 +1180,32 @@ async def create_wide_tree_structure( ``` """ # Create folders in parallel - folder_tasks = [ - Folder(name=f"folder_{folder_letter}_{uuid.uuid4()}").store_async( + folders = [ + Folder(name=f"folder_{folder_letter}_{uuid.uuid4()}").store( parent=project_model, synapse_client=self.syn ) for folder_letter in ["a", "b", "c"] ] - folders = await asyncio.gather(*folder_tasks) # Schedule cleanup for folders for folder in folders: self.schedule_for_cleanup(folder.id) - # Create files in parallel - file_tasks = [ + # Create files + file_results = [ File( path=utils.make_bogus_uuid_file(), name=f"file_{folder_letter}_{uuid.uuid4()}", - ).store_async(parent=folder, synapse_client=self.syn) + ).store(parent=folder, synapse_client=self.syn) for folder_letter, folder in zip(["a", "b", "c"], folders) ] # Create root file task - root_file_task = File( + root_file = File( path=utils.make_bogus_uuid_file(), name=f"root_file_{uuid.uuid4()}" - ).store_async(parent=project_model, synapse_client=self.syn) + ).store(parent=project_model, synapse_client=self.syn) + file_results.append(root_file) - # Execute file creation tasks in parallel - file_results = await asyncio.gather(*file_tasks, root_file_task) all_files = file_results[:-1] # All but the last (root file) root_file = file_results[-1] # The last one (root file) @@ -1145,7 +1220,7 @@ async def create_wide_tree_structure( "root_file": root_file, } - async def create_complex_mixed_structure( + def create_complex_mixed_structure( self, project_model: Project ) -> Dict[str, Union[Folder, File, List]]: """ @@ -1170,20 +1245,15 @@ async def create_complex_mixed_structure( └── mixed_file_b ``` """ - # Create top-level folders in parallel - top_folder_tasks = [ - Folder(name=f"shallow_folder_{uuid.uuid4()}").store_async( - parent=project_model, synapse_client=self.syn - ), - Folder(name=f"deep_branch_{uuid.uuid4()}").store_async( - parent=project_model, synapse_client=self.syn - ), - Folder(name=f"mixed_folder_{uuid.uuid4()}").store_async( - parent=project_model, synapse_client=self.syn - ), - ] - shallow_folder, deep_branch, mixed_folder = await asyncio.gather( - *top_folder_tasks + # Create top-level folders + shallow_folder = Folder(name=f"shallow_folder_{uuid.uuid4()}").store( + parent=project_model, synapse_client=self.syn + ) + deep_branch = Folder(name=f"deep_branch_{uuid.uuid4()}").store( + parent=project_model, synapse_client=self.syn + ) + mixed_folder = Folder(name=f"mixed_folder_{uuid.uuid4()}").store( + parent=project_model, synapse_client=self.syn ) # Schedule cleanup for top-level folders @@ -1191,77 +1261,66 @@ async def create_complex_mixed_structure( self.schedule_for_cleanup(folder.id) # Create first level files and folders - shallow_file = await File( + shallow_file = File( path=utils.make_bogus_uuid_file(), name=f"shallow_file_{uuid.uuid4()}" - ).store_async(parent=shallow_folder, synapse_client=self.syn) + ).store(parent=shallow_folder, synapse_client=self.syn) self.schedule_for_cleanup(shallow_file.id) # Deep branch structure - deep_file_1_task = File( + deep_file_1 = File( path=utils.make_bogus_uuid_file(), name=f"deep_file_1_{uuid.uuid4()}" - ).store_async(parent=deep_branch, synapse_client=self.syn) + ).store(parent=deep_branch, synapse_client=self.syn) - sub_deep_task = Folder(name=f"sub_deep_{uuid.uuid4()}").store_async( + sub_deep = Folder(name=f"sub_deep_{uuid.uuid4()}").store( parent=deep_branch, synapse_client=self.syn ) - deep_file_1, sub_deep = await asyncio.gather(deep_file_1_task, sub_deep_task) self.schedule_for_cleanup(deep_file_1.id) self.schedule_for_cleanup(sub_deep.id) # Continue deep structure - deep_file_2_task = File( + deep_file_2 = File( path=utils.make_bogus_uuid_file(), name=f"deep_file_2_{uuid.uuid4()}" - ).store_async(parent=sub_deep, synapse_client=self.syn) + ).store(parent=sub_deep, synapse_client=self.syn) - sub_sub_deep_task = Folder(name=f"sub_sub_deep_{uuid.uuid4()}").store_async( + sub_sub_deep = Folder(name=f"sub_sub_deep_{uuid.uuid4()}").store( parent=sub_deep, synapse_client=self.syn ) - deep_file_2, sub_sub_deep = await asyncio.gather( - deep_file_2_task, sub_sub_deep_task - ) self.schedule_for_cleanup(deep_file_2.id) self.schedule_for_cleanup(sub_sub_deep.id) - deep_file_3 = await File( + deep_file_3 = File( path=utils.make_bogus_uuid_file(), name=f"deep_file_3_{uuid.uuid4()}" - ).store_async(parent=sub_sub_deep, synapse_client=self.syn) + ).store(parent=sub_sub_deep, synapse_client=self.syn) self.schedule_for_cleanup(deep_file_3.id) # Mixed folder structure - mixed_file_task = File( + mixed_file = File( path=utils.make_bogus_uuid_file(), name=f"mixed_file_{uuid.uuid4()}" - ).store_async(parent=mixed_folder, synapse_client=self.syn) + ).store(parent=mixed_folder, synapse_client=self.syn) - mixed_sub_a_task = Folder(name=f"mixed_sub_a_{uuid.uuid4()}").store_async( + mixed_sub_a = Folder(name=f"mixed_sub_a_{uuid.uuid4()}").store( parent=mixed_folder, synapse_client=self.syn ) - mixed_sub_b_task = Folder(name=f"mixed_sub_b_{uuid.uuid4()}").store_async( + mixed_sub_b = Folder(name=f"mixed_sub_b_{uuid.uuid4()}").store( parent=mixed_folder, synapse_client=self.syn ) - mixed_file, mixed_sub_a, mixed_sub_b = await asyncio.gather( - mixed_file_task, mixed_sub_a_task, mixed_sub_b_task - ) - # Schedule cleanup self.schedule_for_cleanup(mixed_file.id) self.schedule_for_cleanup(mixed_sub_a.id) self.schedule_for_cleanup(mixed_sub_b.id) # Create files in mixed sub-folders in parallel - mixed_file_a_task = File( + mixed_file_a = File( path=utils.make_bogus_uuid_file(), name=f"mixed_file_a_{uuid.uuid4()}" - ).store_async(parent=mixed_sub_a, synapse_client=self.syn) + ).store(parent=mixed_sub_a, synapse_client=self.syn) - mixed_file_b_task = File( + mixed_file_b = File( path=utils.make_bogus_uuid_file(), name=f"mixed_file_b_{uuid.uuid4()}" - ).store_async(parent=mixed_sub_b, synapse_client=self.syn) + ).store(parent=mixed_sub_b, synapse_client=self.syn) - mixed_file_a, mixed_file_b = await asyncio.gather( - mixed_file_a_task, mixed_file_b_task - ) self.schedule_for_cleanup(mixed_file_a.id) self.schedule_for_cleanup(mixed_file_b.id) @@ -1277,7 +1336,7 @@ async def create_complex_mixed_structure( "mixed_files": [mixed_file, mixed_file_a, mixed_file_b], } - async def test_delete_permissions_on_new_project( + def test_delete_permissions_on_new_project( self, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a newly created project.""" @@ -1285,15 +1344,17 @@ async def test_delete_permissions_on_new_project( caplog.set_level(logging.DEBUG) # GIVEN a newly created project with custom permissions - project = await Project(name=f"test_project_{uuid.uuid4()}").store_async() + project = Project(name=f"test_project_{uuid.uuid4()}").store( + synapse_client=self.syn + ) self.schedule_for_cleanup(project.id) # AND custom permissions are set for authenticated users - await self._set_custom_permissions(project) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(project) + time.sleep(random.randint(10, 20)) # WHEN I delete permissions on the project - project.delete_permissions() + project.delete_permissions(synapse_client=self.syn) # THEN the permissions should not be deleted # Check either for the log message or verify the permissions still exist @@ -1306,27 +1367,27 @@ async def test_delete_permissions_on_new_project( else: # Alternatively, verify that the permissions weren't actually deleted # by checking if they still exist - assert await self._verify_permissions_not_deleted(project) + assert self._verify_permissions_not_deleted(project) - async def test_delete_permissions_simple_tree_structure( + def test_delete_permissions_simple_tree_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a simple tree structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a simple tree structure with permissions - structure = await self.create_simple_tree_structure(project_object) + structure = self.create_simple_tree_structure(project_object) folder_a = structure["folder_a"] file_1 = structure["file_1"] # Set permissions on all entities - await self._set_custom_permissions(folder_a) - await self._set_custom_permissions(file_1) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(folder_a) + self._set_custom_permissions(file_1) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion + self._verify_list_acl_functionality( entity=folder_a, expected_entity_count=2, # folder_a and file_1 recursive=True, @@ -1347,26 +1408,26 @@ async def test_delete_permissions_simple_tree_structure( ) # THEN all permissions should be deleted - await self._verify_permissions_deleted(folder_a) - await self._verify_permissions_deleted(file_1) + self._verify_permissions_deleted(folder_a) + self._verify_permissions_deleted(file_1) - async def test_delete_permissions_deep_nested_structure( + def test_delete_permissions_deep_nested_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a deeply nested structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a deeply nested structure with permissions - structure = await self.create_deep_nested_structure(project_object) + structure = self.create_deep_nested_structure(project_object) # Set permissions on all entities for entity in structure.values(): - await self._set_custom_permissions(entity) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(entity) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion + self._verify_list_acl_functionality( entity=structure["level_1"], expected_entity_count=8, # all levels and files recursive=True, @@ -1388,17 +1449,17 @@ async def test_delete_permissions_deep_nested_structure( # THEN all permissions should be deleted for entity in structure.values(): - await self._verify_permissions_deleted(entity) + self._verify_permissions_deleted(entity) - async def test_delete_permissions_wide_tree_structure( + def test_delete_permissions_wide_tree_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a wide tree structure with multiple siblings.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a wide tree structure with permissions - structure = await self.create_wide_tree_structure(project_object) + structure = self.create_wide_tree_structure(project_object) folders = structure["folders"] all_files = structure["all_files"] root_file = structure["root_file"] @@ -1406,11 +1467,11 @@ async def test_delete_permissions_wide_tree_structure( # Set permissions on all entities entities_to_set = folders + all_files + [root_file] for entity in entities_to_set: - await self._set_custom_permissions(entity) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(entity) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion + self._verify_list_acl_functionality( entity=project_object, expected_entity_count=7, # 3 folders + 3 files + 1 root file recursive=True, @@ -1433,17 +1494,17 @@ async def test_delete_permissions_wide_tree_structure( # THEN all permissions should be deleted (except project which can't be deleted) entities_to_verify = folders + all_files + [root_file] for entity in entities_to_verify: - await self._verify_permissions_deleted(entity) + self._verify_permissions_deleted(entity) - async def test_delete_permissions_complex_mixed_structure( + def test_delete_permissions_complex_mixed_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a complex mixed structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a complex mixed structure with permissions - structure = await self.create_complex_mixed_structure(project_object) + structure = self.create_complex_mixed_structure(project_object) # Set permissions on all entities entities_to_set = ( @@ -1461,11 +1522,11 @@ async def test_delete_permissions_complex_mixed_structure( ) for entity in entities_to_set: - await self._set_custom_permissions(entity) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(entity) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion + self._verify_list_acl_functionality( entity=project_object, expected_entity_count=12, # complex structure with multiple entities recursive=True, @@ -1487,26 +1548,26 @@ async def test_delete_permissions_complex_mixed_structure( # THEN all permissions should be deleted for entity in entities_to_set: - await self._verify_permissions_deleted(entity) + self._verify_permissions_deleted(entity) # Edge case tests - async def test_delete_permissions_empty_folder( + def test_delete_permissions_empty_folder( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on an empty folder.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN an empty folder with custom permissions - empty_folder = await Folder(name=f"empty_folder_{uuid.uuid4()}").store_async( + empty_folder = Folder(name=f"empty_folder_{uuid.uuid4()}").store( parent=project_object, synapse_client=self.syn ) self.schedule_for_cleanup(empty_folder.id) - await self._set_custom_permissions(empty_folder) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(empty_folder) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion (empty folder) - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion (empty folder) + self._verify_list_acl_functionality( entity=empty_folder, expected_entity_count=1, # just the empty folder itself recursive=True, @@ -1527,33 +1588,33 @@ async def test_delete_permissions_empty_folder( ) # THEN the folder permissions should be deleted - await self._verify_permissions_deleted(empty_folder) + self._verify_permissions_deleted(empty_folder) - async def test_delete_permissions_folder_with_only_files( + def test_delete_permissions_folder_with_only_files( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a folder that contains only files.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a folder with only one file - folder = await Folder(name=f"files_only_folder_{uuid.uuid4()}").store_async( + folder = Folder(name=f"files_only_folder_{uuid.uuid4()}").store( parent=project_object, synapse_client=self.syn ) self.schedule_for_cleanup(folder.id) - file = await File( + file = File( path=utils.make_bogus_uuid_file(), name=f"only_file_{uuid.uuid4()}" - ).store_async(parent=folder, synapse_client=self.syn) + ).store(parent=folder, synapse_client=self.syn) self.schedule_for_cleanup(file.id) # Set permissions on all entities - await self._set_custom_permissions(folder) - await self._set_custom_permissions(file) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(folder) + self._set_custom_permissions(file) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion + self._verify_list_acl_functionality( entity=folder, expected_entity_count=2, # folder and file recursive=True, @@ -1574,30 +1635,29 @@ async def test_delete_permissions_folder_with_only_files( ) # THEN all permissions should be deleted - await self._verify_permissions_deleted(folder) - await self._verify_permissions_deleted(file) + self._verify_permissions_deleted(folder) + self._verify_permissions_deleted(file) - async def test_delete_permissions_folder_with_only_folders( + def test_delete_permissions_folder_with_only_folders( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a folder that contains only sub-folders.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a folder with only sub-folders - parent_folder = await Folder( - name=f"folders_only_parent_{uuid.uuid4()}" - ).store_async(parent=project_object, synapse_client=self.syn) + parent_folder = Folder(name=f"folders_only_parent_{uuid.uuid4()}").store( + parent=project_object, synapse_client=self.syn + ) self.schedule_for_cleanup(parent_folder.id) # Create sub-folders in parallel - sub_folder_tasks = [ - Folder(name=f"only_subfolder_{i}_{uuid.uuid4()}").store_async( + sub_folders = [ + Folder(name=f"only_subfolder_{i}_{uuid.uuid4()}").store( parent=parent_folder, synapse_client=self.syn ) for i in range(3) ] - sub_folders = await asyncio.gather(*sub_folder_tasks) # Schedule cleanup for sub-folders for sub_folder in sub_folders: @@ -1606,11 +1666,11 @@ async def test_delete_permissions_folder_with_only_folders( # Set permissions on all entities entities_to_set = [parent_folder] + sub_folders for entity in entities_to_set: - await self._set_custom_permissions(entity) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(entity) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion + self._verify_list_acl_functionality( entity=parent_folder, expected_entity_count=4, # parent + 3 sub-folders recursive=True, @@ -1632,29 +1692,29 @@ async def test_delete_permissions_folder_with_only_folders( # THEN all permissions should be deleted for entity in entities_to_set: - await self._verify_permissions_deleted(entity) + self._verify_permissions_deleted(entity) - async def test_delete_permissions_target_files_only_complex( + def test_delete_permissions_target_files_only_complex( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions targeting only files in a complex structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a complex structure with permissions - structure = await self.create_complex_mixed_structure(project_object) + structure = self.create_complex_mixed_structure(project_object) # Set permissions on all entities - await self._set_custom_permissions(structure["shallow_folder"]) - await self._set_custom_permissions(structure["shallow_file"]) - await self._set_custom_permissions(structure["deep_branch"]) - await self._set_custom_permissions(structure["sub_deep"]) + self._set_custom_permissions(structure["shallow_folder"]) + self._set_custom_permissions(structure["shallow_file"]) + self._set_custom_permissions(structure["deep_branch"]) + self._set_custom_permissions(structure["sub_deep"]) for file in structure["deep_files"]: - await self._set_custom_permissions(file) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(file) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async with target_entity_types for files only - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl with target_entity_types for files only + self._verify_list_acl_functionality( entity=project_object, expected_entity_count=4, # shallow_file + 3 deep_files recursive=True, @@ -1677,35 +1737,33 @@ async def test_delete_permissions_target_files_only_complex( ) # THEN only file permissions should be deleted - await self._verify_permissions_deleted(structure["shallow_file"]) + self._verify_permissions_deleted(structure["shallow_file"]) for file in structure["deep_files"]: - await self._verify_permissions_deleted(file) + self._verify_permissions_deleted(file) # BUT folder permissions should remain - await asyncio.gather( - self._verify_permissions_not_deleted(structure["shallow_folder"]), - self._verify_permissions_not_deleted(structure["deep_branch"]), - self._verify_permissions_not_deleted(structure["sub_deep"]), - ) + assert self._verify_permissions_not_deleted(structure["shallow_folder"]) + assert self._verify_permissions_not_deleted(structure["deep_branch"]) + assert self._verify_permissions_not_deleted(structure["sub_deep"]) # Include container content vs recursive tests - async def test_delete_permissions_include_container_only_deep_structure( + def test_delete_permissions_include_container_only_deep_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test include_container_content=True without recursive on deep structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a deep nested structure with permissions - structure = await self.create_deep_nested_structure(project_object) + structure = self.create_deep_nested_structure(project_object) # Set permissions on all entities for entity in structure.values(): - await self._set_custom_permissions(entity) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(entity) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async with include_container_content=True - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl with include_container_content=True + self._verify_list_acl_functionality( entity=structure["level_1"], expected_entity_count=3, # level_1, file_at_1, level_2 (non-recursive) recursive=False, @@ -1727,37 +1785,37 @@ async def test_delete_permissions_include_container_only_deep_structure( ) # THEN only level_1 and its direct children should have permissions deleted - await self._verify_permissions_deleted(structure["level_1"]) - await self._verify_permissions_deleted(structure["file_at_1"]) - await self._verify_permissions_deleted(structure["level_2"]) + self._verify_permissions_deleted(structure["level_1"]) + self._verify_permissions_deleted(structure["file_at_1"]) + self._verify_permissions_deleted(structure["level_2"]) # BUT deeper nested entities should retain permissions - await self._verify_permissions_not_deleted(structure["level_3"]) - await self._verify_permissions_not_deleted(structure["level_4"]) - await self._verify_permissions_not_deleted(structure["file_at_2"]) - await self._verify_permissions_not_deleted(structure["file_at_3"]) - await self._verify_permissions_not_deleted(structure["file_at_4"]) + self._verify_permissions_not_deleted(structure["level_3"]) + self._verify_permissions_not_deleted(structure["level_4"]) + self._verify_permissions_not_deleted(structure["file_at_2"]) + self._verify_permissions_not_deleted(structure["file_at_3"]) + self._verify_permissions_not_deleted(structure["file_at_4"]) - async def test_delete_permissions_skip_self_complex_structure( + def test_delete_permissions_skip_self_complex_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test include_self=False on a complex structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a complex mixed structure with permissions - structure = await self.create_complex_mixed_structure(project_object) + structure = self.create_complex_mixed_structure(project_object) # Set permissions on all entities - await self._set_custom_permissions(structure["mixed_folder"]) + self._set_custom_permissions(structure["mixed_folder"]) for folder in structure["mixed_sub_folders"]: - await self._set_custom_permissions(folder) + self._set_custom_permissions(folder) for file in structure["mixed_files"]: - await self._set_custom_permissions(file) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(file) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion (should show all entities) - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion (should show all entities) + self._verify_list_acl_functionality( entity=structure["mixed_folder"], expected_entity_count=4, # mixed_folder + 2 sub_folders + 1 mixed_file recursive=True, @@ -1779,40 +1837,36 @@ async def test_delete_permissions_skip_self_complex_structure( ) # THEN the mixed_folder permissions should remain - await self._verify_permissions_not_deleted(structure["mixed_folder"]) + self._verify_permissions_not_deleted(structure["mixed_folder"]) # BUT child permissions should be deleted - await asyncio.gather( - *[ - self._verify_permissions_deleted(folder) - for folder in structure["mixed_sub_folders"] - ], - *[ - self._verify_permissions_deleted(file) - for file in structure["mixed_files"] - ], - ) + for folder in structure["mixed_sub_folders"]: + self._verify_permissions_deleted(folder) + + for file in structure["mixed_files"]: + self._verify_permissions_deleted(file) # Dry run functionality tests - async def test_delete_permissions_dry_run_no_changes( + + def test_delete_permissions_dry_run_no_changes( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test that dry_run=True makes no actual changes.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a simple structure with permissions - structure = await self.create_simple_tree_structure(project_object) + structure = self.create_simple_tree_structure(project_object) folder_a = structure["folder_a"] file_1 = structure["file_1"] # Set permissions on all entities - await self._set_custom_permissions(folder_a) - await self._set_custom_permissions(file_1) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(folder_a) + self._set_custom_permissions(file_1) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before dry run - initial_acl_result = await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before dry run + initial_acl_result = self._verify_list_acl_functionality( entity=folder_a, expected_entity_count=2, # folder_a and file_1 recursive=True, @@ -1833,8 +1887,8 @@ async def test_delete_permissions_dry_run_no_changes( ) # THEN no permissions should be deleted - await self._verify_permissions_not_deleted(folder_a) - await self._verify_permissions_not_deleted(file_1) + self._verify_permissions_not_deleted(folder_a) + self._verify_permissions_not_deleted(file_1) # AND dry run messages should be logged self._verify_log_messages( @@ -1845,9 +1899,9 @@ async def test_delete_permissions_dry_run_no_changes( tree_logging=False, ) - # WHEN - Verify list_acl_async after dry run (should be identical) + # WHEN - Verify list_acl after dry run (should be identical) caplog.clear() - final_acl_result = await self._verify_list_acl_functionality( + final_acl_result = self._verify_list_acl_functionality( entity=folder_a, expected_entity_count=2, # should be same as before recursive=True, @@ -1860,26 +1914,24 @@ async def test_delete_permissions_dry_run_no_changes( final_acl_result.all_entity_acls ) - async def test_delete_permissions_dry_run_complex_logging( + def test_delete_permissions_dry_run_complex_logging( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test dry run logging for complex structures.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a complex structure with permissions - structure = await self.create_complex_mixed_structure(project_object) + structure = self.create_complex_mixed_structure(project_object) # Set permissions on a subset of entities - await asyncio.gather( - self._set_custom_permissions(structure["deep_branch"]), - self._set_custom_permissions(structure["sub_deep"]), - self._set_custom_permissions(structure["deep_files"][0]), - ) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(structure["deep_branch"]), + self._set_custom_permissions(structure["sub_deep"]), + self._set_custom_permissions(structure["deep_files"][0]), + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async with detailed logging before dry run - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl with detailed logging before dry run + self._verify_list_acl_functionality( entity=structure["deep_branch"], expected_entity_count=3, # deep_branch, sub_deep, and one deep_file recursive=True, @@ -1902,11 +1954,9 @@ async def test_delete_permissions_dry_run_complex_logging( ) # THEN no permissions should be deleted - await asyncio.gather( - self._verify_permissions_not_deleted(structure["deep_branch"]), - self._verify_permissions_not_deleted(structure["sub_deep"]), - self._verify_permissions_not_deleted(structure["deep_files"][0]), - ) + self._verify_permissions_not_deleted(structure["deep_branch"]), + self._verify_permissions_not_deleted(structure["sub_deep"]), + self._verify_permissions_not_deleted(structure["deep_files"][0]), # AND comprehensive dry run analysis should be logged self._verify_log_messages( @@ -1922,27 +1972,26 @@ async def test_delete_permissions_dry_run_complex_logging( assert "End of Dry Run Analysis" in caplog.text # Performance and stress tests - async def test_delete_permissions_large_flat_structure( + def test_delete_permissions_large_flat_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on a large flat structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a folder with many files - large_folder = await Folder(name=f"large_folder_{uuid.uuid4()}").store_async( + large_folder = Folder(name=f"large_folder_{uuid.uuid4()}").store( parent=project_object, synapse_client=self.syn ) self.schedule_for_cleanup(large_folder.id) # Create files in parallel - file_tasks = [ + files = [ File( path=utils.make_bogus_uuid_file(), name=f"large_file_{i}_{uuid.uuid4()}" - ).store_async(parent=large_folder, synapse_client=self.syn) + ).store(parent=large_folder, synapse_client=self.syn) for i in range(10) # Reduced from larger number for test performance ] - files = await asyncio.gather(*file_tasks) # Schedule cleanup for files for file in files: @@ -1951,11 +2000,11 @@ async def test_delete_permissions_large_flat_structure( # Set permissions on all entities entities_to_set = [large_folder] + files for entity in entities_to_set: - await self._set_custom_permissions(entity) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(entity) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async performance with large structure - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl performance with large structure + self._verify_list_acl_functionality( entity=large_folder, expected_entity_count=11, # large_folder + 10 files recursive=True, @@ -1977,23 +2026,22 @@ async def test_delete_permissions_large_flat_structure( # THEN all permissions should be deleted for entity in entities_to_set: - await self._verify_permissions_deleted(entity) + self._verify_permissions_deleted(entity) - async def test_delete_permissions_multiple_nested_branches( + def test_delete_permissions_multiple_nested_branches( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions on multiple nested branches simultaneously.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN multiple complex nested branches - branch_tasks = [ - Folder(name=f"branch_{branch_name}_{uuid.uuid4()}").store_async( + branches = [ + Folder(name=f"branch_{branch_name}_{uuid.uuid4()}").store( parent=project_object, synapse_client=self.syn ) for branch_name in ["alpha", "beta"] ] - branches = await asyncio.gather(*branch_tasks) all_entities = list(branches) @@ -2002,19 +2050,16 @@ async def test_delete_permissions_multiple_nested_branches( self.schedule_for_cleanup(branch.id) # Create nested structure in each branch in parallel - nested_tasks = [] + nested_folders = [] for branch_name, branch in zip(["alpha", "beta"], branches): current_parent = branch for level in range(2): # Create sub-folder and file tasks for this level sub_folder_task = Folder( name=f"{branch_name}_level_{level}_{uuid.uuid4()}" - ).store_async(parent=current_parent, synapse_client=self.syn) + ).store(parent=current_parent, synapse_client=self.syn) - nested_tasks.append(sub_folder_task) - - # Execute all nested folder creation tasks in parallel - nested_folders = await asyncio.gather(*nested_tasks) + nested_folders.append(sub_folder_task) # Add nested folders to all_entities and schedule cleanup all_entities.extend(nested_folders) @@ -2022,7 +2067,7 @@ async def test_delete_permissions_multiple_nested_branches( self.schedule_for_cleanup(folder.id) # Now create files for each nested folder in parallel - file_tasks = [] + files = [] folder_index = 0 for branch_name in ["alpha", "beta"]: for level in range(2): @@ -2030,13 +2075,10 @@ async def test_delete_permissions_multiple_nested_branches( file_task = File( path=utils.make_bogus_uuid_file(), name=f"{branch_name}_file_{level}_{uuid.uuid4()}", - ).store_async(parent=parent_folder, synapse_client=self.syn) - file_tasks.append(file_task) + ).store(parent=parent_folder, synapse_client=self.syn) + files.append(file_task) folder_index += 1 - # Execute all file creation tasks in parallel - files = await asyncio.gather(*file_tasks) - # Add files to all_entities and schedule cleanup all_entities.extend(files) for file in files: @@ -2044,11 +2086,11 @@ async def test_delete_permissions_multiple_nested_branches( # Set permissions on all entities for entity in all_entities: - await self._set_custom_permissions(entity) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(entity) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before deletion (complex multiple branches) - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before deletion (complex multiple branches) + self._verify_list_acl_functionality( entity=project_object, expected_entity_count=11, recursive=True, @@ -2061,7 +2103,7 @@ async def test_delete_permissions_multiple_nested_branches( caplog.clear() # WHEN I delete permissions recursively from the project - await project_object.delete_permissions_async( + project_object.delete_permissions( recursive=True, include_container_content=True, dry_run=False, @@ -2070,55 +2112,49 @@ async def test_delete_permissions_multiple_nested_branches( # THEN all permissions should be deleted for entity in all_entities: - await self._verify_permissions_deleted(entity) + self._verify_permissions_deleted(entity) - async def test_delete_permissions_selective_branches( + def test_delete_permissions_selective_branches( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test selectively deleting permissions from specific branches.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN multiple branches with permissions - # Create branches in parallel - branch_tasks = [ - Folder(name=f"branch_a_{uuid.uuid4()}").store_async( - parent=project_object, synapse_client=self.syn - ), - Folder(name=f"branch_b_{uuid.uuid4()}").store_async( - parent=project_object, synapse_client=self.syn - ), - ] - branch_a, branch_b = await asyncio.gather(*branch_tasks) + # Create branches + branch_a = Folder(name=f"branch_a_{uuid.uuid4()}").store( + parent=project_object, synapse_client=self.syn + ) + branch_b = Folder(name=f"branch_b_{uuid.uuid4()}").store( + parent=project_object, synapse_client=self.syn + ) # Schedule cleanup for branches self.schedule_for_cleanup(branch_a.id) self.schedule_for_cleanup(branch_b.id) - # Create files in each branch in parallel - file_tasks = [ - File( - path=utils.make_bogus_uuid_file(), name=f"file_a_{uuid.uuid4()}" - ).store_async(parent=branch_a, synapse_client=self.syn), - File( - path=utils.make_bogus_uuid_file(), name=f"file_b_{uuid.uuid4()}" - ).store_async(parent=branch_b, synapse_client=self.syn), - ] - file_a, file_b = await asyncio.gather(*file_tasks) + # Create files in each branch + file_a = File( + path=utils.make_bogus_uuid_file(), name=f"file_a_{uuid.uuid4()}" + ).store(parent=branch_a, synapse_client=self.syn) + file_b = File( + path=utils.make_bogus_uuid_file(), name=f"file_b_{uuid.uuid4()}" + ).store(parent=branch_b, synapse_client=self.syn) # Schedule cleanup for files self.schedule_for_cleanup(file_a.id) self.schedule_for_cleanup(file_b.id) # Set permissions on all entities - await self._set_custom_permissions(branch_a) - await self._set_custom_permissions(branch_b) - await self._set_custom_permissions(file_a) - await self._set_custom_permissions(file_b) - await asyncio.sleep(random.randint(10, 20)) - - # WHEN - Verify list_acl_async before selective deletion - await self._verify_list_acl_functionality( + self._set_custom_permissions(branch_a) + self._set_custom_permissions(branch_b) + self._set_custom_permissions(file_a) + self._set_custom_permissions(file_b) + time.sleep(random.randint(10, 20)) + + # WHEN - Verify list_acl before selective deletion + self._verify_list_acl_functionality( entity=branch_a, expected_entity_count=2, # branch_a and file_a recursive=True, @@ -2139,35 +2175,33 @@ async def test_delete_permissions_selective_branches( ) # THEN only branch_a and its contents should have permissions deleted - await self._verify_permissions_deleted(branch_a) - await self._verify_permissions_deleted(file_a) + self._verify_permissions_deleted(branch_a) + self._verify_permissions_deleted(file_a) # BUT branch_b should retain permissions - await self._verify_permissions_not_deleted(branch_b) - await self._verify_permissions_not_deleted(file_b) + self._verify_permissions_not_deleted(branch_b) + self._verify_permissions_not_deleted(file_b) - async def test_delete_permissions_mixed_entity_types_in_structure( + def test_delete_permissions_mixed_entity_types_in_structure( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions with mixed entity types in complex structure.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a structure with both files and folders at multiple levels - structure = await self.create_complex_mixed_structure(project_object) + structure = self.create_complex_mixed_structure(project_object) # Set permissions on a mix of entities - await asyncio.gather( - self._set_custom_permissions(structure["shallow_folder"]), - self._set_custom_permissions(structure["shallow_file"]), - self._set_custom_permissions(structure["deep_branch"]), - self._set_custom_permissions(structure["deep_files"][1]), - self._set_custom_permissions(structure["mixed_sub_folders"][0]), - ) - await asyncio.sleep(random.randint(10, 20)) - - # WHEN - Verify list_acl_async with mixed entity types - await self._verify_list_acl_functionality( + self._set_custom_permissions(structure["shallow_folder"]), + self._set_custom_permissions(structure["shallow_file"]), + self._set_custom_permissions(structure["deep_branch"]), + self._set_custom_permissions(structure["deep_files"][1]), + self._set_custom_permissions(structure["mixed_sub_folders"][0]), + time.sleep(random.randint(10, 20)) + + # WHEN - Verify list_acl with mixed entity types + self._verify_list_acl_functionality( entity=project_object, expected_entity_count=5, # All the entities we set permissions on recursive=True, @@ -2190,39 +2224,37 @@ async def test_delete_permissions_mixed_entity_types_in_structure( ) # THEN all targeted entities should have permissions deleted - await asyncio.gather( - self._verify_permissions_deleted(structure["shallow_folder"]), - self._verify_permissions_deleted(structure["shallow_file"]), - self._verify_permissions_deleted(structure["deep_branch"]), - self._verify_permissions_deleted(structure["deep_files"][1]), - self._verify_permissions_deleted(structure["mixed_sub_folders"][0]), - ) + self._verify_permissions_deleted(structure["shallow_folder"]), + self._verify_permissions_deleted(structure["shallow_file"]), + self._verify_permissions_deleted(structure["deep_branch"]), + self._verify_permissions_deleted(structure["deep_files"][1]), + self._verify_permissions_deleted(structure["mixed_sub_folders"][0]), - async def test_delete_permissions_no_container_content_but_has_children( + def test_delete_permissions_no_container_content_but_has_children( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test deleting permissions without include_container_content when children exist.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a folder with children and custom permissions - parent_folder = await Folder(name=f"parent_folder_{uuid.uuid4()}").store_async( + parent_folder = Folder(name=f"parent_folder_{uuid.uuid4()}").store( parent=project_object, synapse_client=self.syn ) self.schedule_for_cleanup(parent_folder.id) - child_file = await File( + child_file = File( path=utils.make_bogus_uuid_file(), name=f"child_file_{uuid.uuid4()}" - ).store_async(parent=parent_folder, synapse_client=self.syn) + ).store(parent=parent_folder, synapse_client=self.syn) self.schedule_for_cleanup(child_file.id) # Set permissions on both entities - await self._set_custom_permissions(parent_folder) - await self._set_custom_permissions(child_file) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(parent_folder) + self._set_custom_permissions(child_file) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async before testing container content exclusion - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl before testing container content exclusion + self._verify_list_acl_functionality( entity=parent_folder, expected_entity_count=1, # Only parent_folder, child excluded due to include_container_content=False recursive=False, @@ -2243,30 +2275,30 @@ async def test_delete_permissions_no_container_content_but_has_children( ) # THEN only parent permissions should be deleted - await self._verify_permissions_deleted(parent_folder) + self._verify_permissions_deleted(parent_folder) # AND child permissions should remain - await self._verify_permissions_not_deleted(child_file) + self._verify_permissions_not_deleted(child_file) - async def test_delete_permissions_case_insensitive_entity_types( + def test_delete_permissions_case_insensitive_entity_types( self, project_object: Project, caplog: pytest.LogCaptureFixture ) -> None: """Test that target_entity_types are case-insensitive.""" - await project_object.store_async(synapse_client=self.syn) + project_object.store(synapse_client=self.syn) self.schedule_for_cleanup(project_object.id) # GIVEN a simple structure with permissions - structure = await self.create_simple_tree_structure(project_object) + structure = self.create_simple_tree_structure(project_object) folder_a = structure["folder_a"] file_1 = structure["file_1"] # Set permissions on all entities - await self._set_custom_permissions(folder_a) - await self._set_custom_permissions(file_1) - await asyncio.sleep(random.randint(10, 20)) + self._set_custom_permissions(folder_a) + self._set_custom_permissions(file_1) + time.sleep(random.randint(10, 20)) - # WHEN - Verify list_acl_async with case-insensitive entity types - await self._verify_list_acl_functionality( + # WHEN - Verify list_acl with case-insensitive entity types + self._verify_list_acl_functionality( entity=folder_a, expected_entity_count=2, # folder_a and file_1 (case-insensitive filtering) recursive=True, @@ -2292,8 +2324,8 @@ async def test_delete_permissions_case_insensitive_entity_types( ) # THEN all permissions should be deleted - await self._verify_permissions_deleted(folder_a) - await self._verify_permissions_deleted(file_1) + self._verify_permissions_deleted(folder_a) + self._verify_permissions_deleted(file_1) class TestAllEntityTypesPermissions: @@ -2308,7 +2340,7 @@ def init( self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def create_all_entity_types(self, project_model: Project) -> Dict[str, any]: + def create_all_entity_types(self, project_model: Project) -> Dict[str, any]: """Create all supported entity types for testing.""" project_model = project_model.store(synapse_client=self.syn) self.schedule_for_cleanup(project_model.id) @@ -2396,11 +2428,11 @@ async def create_all_entity_types(self, project_model: Project) -> Dict[str, any return entities - async def create_all_entity_types_with_acl( + def create_all_entity_types_with_acl( self, project_model: Project ) -> Dict[str, any]: """Create all entity types with local ACL permissions for testing.""" - entities = await self.create_all_entity_types(project_model) + entities = self.create_all_entity_types(project_model) for entity_type, entity in entities.items(): if entity_type != "project": @@ -2410,14 +2442,14 @@ async def create_all_entity_types_with_acl( synapse_client=self.syn, ) - await asyncio.sleep(10) + time.sleep(10) return entities - async def test_list_acl_all_entity_types(self) -> None: + def test_list_acl_all_entity_types(self) -> None: """Test list_acl functionality with all supported entity types.""" # GIVEN a project with all supported entity types and local ACL permissions project = Project(name=f"test_project_{uuid.uuid4()}") - entities = await self.create_all_entity_types_with_acl(project) + entities = self.create_all_entity_types_with_acl(project) # WHEN I call list_acl on the project with all entity types result = entities["project"].list_acl( @@ -2476,11 +2508,11 @@ async def test_list_acl_all_entity_types(self) -> None: entity.id in entities_with_read_permissions ), f"Entity {entity.id} ({entity_type}) should appear in AclListResult with READ permissions" - async def test_list_acl_specific_entity_types(self) -> None: + def test_list_acl_specific_entity_types(self) -> None: """Test list_acl functionality with specific entity types.""" # GIVEN a project with all supported entity types project = Project(name=f"test_project_{uuid.uuid4()}") - entities = await self.create_all_entity_types_with_acl(project) + entities = self.create_all_entity_types_with_acl(project) # WHEN I call list_acl with only table-related entity types result = entities["project"].list_acl( @@ -2579,11 +2611,11 @@ async def test_list_acl_specific_entity_types(self) -> None: has_read_permission ), f"Entity {entity_id} should have READ permissions for AUTHENTICATED_USERS" - async def test_delete_permissions_all_entity_types(self) -> None: + def test_delete_permissions_all_entity_types(self) -> None: """Test delete_permissions functionality with all supported entity types.""" # GIVEN a project with all supported entity types and local ACL permissions project = Project(name=f"test_project_{uuid.uuid4()}") - entities = await self.create_all_entity_types_with_acl(project) + entities = self.create_all_entity_types_with_acl(project) # AND I verify AUTHENTICATED_USERS has READ permissions before deletion for entity_type, entity in entities.items(): @@ -2624,11 +2656,11 @@ async def test_delete_permissions_all_entity_types(self) -> None: "READ" in acl_after ), f"AUTHENTICATED_USERS should still have READ access on {entity_type} after dry run" - async def test_delete_permissions_all_entity_types_actual_deletion(self) -> None: + def test_delete_permissions_all_entity_types_actual_deletion(self) -> None: """Test delete_permissions functionality with actual deletion (dry_run=False).""" # GIVEN a project with all supported entity types and local ACL permissions project = Project(name=f"test_project_{uuid.uuid4()}") - entities = await self.create_all_entity_types_with_acl(project) + entities = self.create_all_entity_types_with_acl(project) # AND I verify AUTHENTICATED_USERS has READ permissions before deletion initial_acl_result = entities["project"].list_acl( @@ -2717,11 +2749,11 @@ async def test_delete_permissions_all_entity_types_actual_deletion(self) -> None ) assert not acl_after, "Local ACL should be removed" - async def test_mixed_case_entity_types_actual_deletion(self) -> None: + def test_mixed_case_entity_types_actual_deletion(self) -> None: """Test that entity types are case-insensitive with actual deletion.""" # GIVEN a project with all supported entity types project = Project(name=f"test_project_{uuid.uuid4()}") - entities = await self.create_all_entity_types_with_acl(project) + entities = self.create_all_entity_types_with_acl(project) # AND I verify initial ACL state initial_result = entities["project"].list_acl( diff --git a/tests/integration/synapseclient/models/synchronous/test_project.py b/tests/integration/synapseclient/models/synchronous/test_project.py index 64b59187f..b6649739c 100644 --- a/tests/integration/synapseclient/models/synchronous/test_project.py +++ b/tests/integration/synapseclient/models/synchronous/test_project.py @@ -116,12 +116,12 @@ def verify_project_properties( ): assert not project.annotations and isinstance(project.annotations, dict) - async def test_store_project_basic(self, project: Project) -> None: + def test_store_project_basic(self, project: Project) -> None: # Test Case 1: Basic project storage # GIVEN a Project object # WHEN I store the Project on Synapse - stored_project = project.store() + stored_project = project.store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # THEN I expect the stored Project to have the expected properties @@ -142,7 +142,9 @@ async def test_store_project_basic(self, project: Project) -> None: project_with_annotations.annotations = annotations # WHEN I store the Project on Synapse - stored_project_with_annotations = project_with_annotations.store() + stored_project_with_annotations = project_with_annotations.store( + synapse_client=self.syn + ) self.schedule_for_cleanup(project_with_annotations.id) # THEN I expect the stored Project to have the expected properties and annotations @@ -152,13 +154,13 @@ async def test_store_project_basic(self, project: Project) -> None: Project(id=stored_project_with_annotations.id).get(synapse_client=self.syn) ).annotations == annotations - async def test_store_project_with_files(self, file: File, project: Project) -> None: + def test_store_project_with_files(self, file: File, project: Project) -> None: # Test Case 1: Project with a single file # GIVEN a File on the project project.files.append(file) # WHEN I store the Project on Synapse - stored_project = project.store() + stored_project = project.store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # THEN I expect the stored Project to have the expected properties and files @@ -173,7 +175,9 @@ async def test_store_project_with_files(self, file: File, project: Project) -> N project_multiple_files.files = files # WHEN I store the Project on Synapse - stored_project_multiple_files = project_multiple_files.store() + stored_project_multiple_files = project_multiple_files.store( + synapse_client=self.syn + ) self.schedule_for_cleanup(project_multiple_files.id) # THEN I expect the stored Project to have the expected properties and files @@ -181,7 +185,7 @@ async def test_store_project_with_files(self, file: File, project: Project) -> N stored_project_multiple_files, expected_files=files ) - async def test_store_project_with_nested_structure( + def test_store_project_with_nested_structure( self, file: File, project: Project ) -> None: # GIVEN a project with files and folders @@ -199,7 +203,7 @@ async def test_store_project_with_nested_structure( project.folders = folders # WHEN I store the Project on Synapse - stored_project = project.store() + stored_project = project.store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # THEN I expect the stored Project to have the expected properties, files, and folders @@ -212,7 +216,7 @@ async def test_store_project_with_nested_structure( existing_project = Project( name=str(uuid.uuid4()), description=DESCRIPTION_PROJECT ) - existing_project = existing_project.store() + existing_project = existing_project.store(synapse_client=self.syn) self.schedule_for_cleanup(existing_project.id) # AND a Folder with a File under the project @@ -221,7 +225,7 @@ async def test_store_project_with_nested_structure( existing_project.folders.append(folder) # WHEN I store the Project on Synapse - stored_existing_project = existing_project.store() + stored_existing_project = existing_project.store(synapse_client=self.syn) # THEN I expect the stored Project to have the expected properties self.verify_project_properties( @@ -268,9 +272,9 @@ def verify_project_properties(self, project: Project): assert project.folders == [] assert not project.annotations and isinstance(project.annotations, dict) - async def test_get_project_methods(self, project: Project) -> None: + def test_get_project_methods(self, project: Project) -> None: # GIVEN a Project object stored in Synapse - stored_project = project.store() + stored_project = project.store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # Test Case 1: Get project by ID @@ -287,13 +291,13 @@ async def test_get_project_methods(self, project: Project) -> None: # THEN I expect the retrieved Project to have the expected properties self.verify_project_properties(project_by_name) - async def test_delete_project(self, project: Project) -> None: + def test_delete_project(self, project: Project) -> None: # GIVEN a Project object stored in Synapse - stored_project = project.store() + stored_project = project.store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # WHEN I delete the Project from Synapse - stored_project.delete() + stored_project.delete(synapse_client=self.syn) # THEN I expect the project to have been deleted with pytest.raises(SynapseHTTPError) as e: @@ -395,26 +399,28 @@ def verify_copied_project( assert sub_file.name is not None assert sub_file.parent_id == folder.id - async def test_copy_project_variations(self) -> None: + def test_copy_project_variations(self) -> None: # GIVEN a nested source project and a destination project source_project = self.create_nested_project() - stored_source_project = source_project.store() + stored_source_project = source_project.store(synapse_client=self.syn) self.schedule_for_cleanup(stored_source_project.id) # Test Case 1: Copy project with all contents # Create first destination project destination_project_1 = Project( name=str(uuid.uuid4()), description="Destination for project copy 1" - ).store() + ).store(synapse_client=self.syn) self.schedule_for_cleanup(destination_project_1.id) # WHEN I copy the project to the destination project copied_project = stored_source_project.copy( - destination_id=destination_project_1.id + destination_id=destination_project_1.id, synapse_client=self.syn ) # AND I sync the destination project from Synapse - destination_project_1.sync_from_synapse(recursive=False, download_file=False) + destination_project_1.sync_from_synapse( + recursive=False, download_file=False, synapse_client=self.syn + ) # THEN I expect the copied Project to have the expected properties assert len(destination_project_1.files) == 3 @@ -425,12 +431,14 @@ async def test_copy_project_variations(self) -> None: # Create a second destination project for the second test case destination_project_2 = Project( name=str(uuid.uuid4()), description="Destination for project copy 2" - ).store() + ).store(synapse_client=self.syn) self.schedule_for_cleanup(destination_project_2.id) # WHEN I copy the project to the destination project excluding files copied_project_no_files = stored_source_project.copy( - destination_id=destination_project_2.id, exclude_types=["file"] + destination_id=destination_project_2.id, + exclude_types=["file"], + synapse_client=self.syn, ) # THEN I expect the copied Project to have the expected properties but no files @@ -438,18 +446,20 @@ async def test_copy_project_variations(self) -> None: copied_project_no_files, stored_source_project, expected_files_empty=True ) - async def test_sync_from_synapse(self, file: File) -> None: + def test_sync_from_synapse(self, file: File) -> None: # GIVEN a nested project structure root_directory_path = os.path.dirname(file.path) project = self.create_nested_project() # WHEN I store the Project on Synapse - stored_project = project.store() + stored_project = project.store(synapse_client=self.syn) self.schedule_for_cleanup(project.id) # AND I sync the project from Synapse - copied_project = stored_project.sync_from_synapse(path=root_directory_path) + copied_project = stored_project.sync_from_synapse( + path=root_directory_path, synapse_client=self.syn + ) # THEN I expect that the project and its contents are synced from Synapse to disk # Verify files in root folder @@ -475,7 +485,7 @@ async def test_sync_from_synapse(self, file: File) -> None: == sub_file.file_handle.content_md5 ) - async def test_sync_all_entity_types(self) -> None: + def test_sync_all_entity_types(self) -> None: """Test syncing a project with all supported entity types.""" # GIVEN a project with one of each entity type @@ -557,7 +567,7 @@ async def test_sync_all_entity_types(self) -> None: # WHEN I sync the project from Synapse synced_project = project_model.sync_from_synapse( - recursive=False, download_file=False + recursive=False, download_file=False, synapse_client=self.syn ) # THEN all entity types should be present @@ -614,24 +624,24 @@ def create_test_hierarchy(self, project: Project) -> dict: root_folder = Folder( name=f"root_folder_{str(uuid.uuid4())[:8]}", parent_id=project.id ) - root_folder = root_folder.store() + root_folder = root_folder.store(synapse_client=self.syn) self.schedule_for_cleanup(root_folder.id) root_file = self.create_file_instance(self.schedule_for_cleanup) root_file.parent_id = project.id - root_file = root_file.store() + root_file = root_file.store(synapse_client=self.syn) self.schedule_for_cleanup(root_file.id) # Create nested folder and file nested_folder = Folder( name=f"nested_folder_{str(uuid.uuid4())[:8]}", parent_id=root_folder.id ) - nested_folder = nested_folder.store() + nested_folder = nested_folder.store(synapse_client=self.syn) self.schedule_for_cleanup(nested_folder.id) nested_file = self.create_file_instance(self.schedule_for_cleanup) nested_file.parent_id = nested_folder.id - nested_file = nested_file.store() + nested_file = nested_file.store(synapse_client=self.syn) self.schedule_for_cleanup(nested_file.id) return { @@ -642,19 +652,19 @@ def create_test_hierarchy(self, project: Project) -> dict: "nested_file": nested_file, } - async def test_walk_recursive_true(self) -> None: + def test_walk_recursive_true(self) -> None: """Test walk method with recursive=True.""" # GIVEN: A unique project with a hierarchical structure project_model = Project( name=f"integration_test_project{str(uuid.uuid4())}", description=DESCRIPTION_PROJECT, ) - project_model = project_model.store() + project_model = project_model.store(synapse_client=self.syn) self.schedule_for_cleanup(project_model.id) hierarchy = self.create_test_hierarchy(project_model) # WHEN: Walking through the project with recursive=True - results = list(project_model.walk(recursive=True)) + results = list(project_model.walk(recursive=True, synapse_client=self.syn)) # THEN: Should get 3 results (project root, root_folder, nested_folder) assert len(results) == 3 @@ -687,19 +697,19 @@ async def test_walk_recursive_true(self) -> None: assert hasattr(nested_nondirs[0], "id") assert hasattr(nested_nondirs[0], "type") - async def test_walk_recursive_false(self) -> None: + def test_walk_recursive_false(self) -> None: """Test walk method with recursive=False.""" # GIVEN: A unique project with a hierarchical structure project_model = Project( name=f"integration_test_project{str(uuid.uuid4())}", description=DESCRIPTION_PROJECT, ) - project_model = project_model.store() + project_model = project_model.store(synapse_client=self.syn) self.schedule_for_cleanup(project_model.id) hierarchy = self.create_test_hierarchy(project_model) # WHEN: Walking through the project with recursive=False - results = list(project_model.walk(recursive=False)) + results = list(project_model.walk(recursive=False, synapse_client=self.syn)) # THEN: Should get only 1 result (project root only) assert len(results) == 1 diff --git a/tests/integration/synapseclient/models/synchronous/test_recordset.py b/tests/integration/synapseclient/models/synchronous/test_recordset.py index 05fbd5590..10337ec95 100644 --- a/tests/integration/synapseclient/models/synchronous/test_recordset.py +++ b/tests/integration/synapseclient/models/synchronous/test_recordset.py @@ -1,7 +1,5 @@ """Integration tests for the synapseclient.models.RecordSet class.""" -import os -import tempfile import uuid from typing import Callable @@ -43,7 +41,7 @@ def record_set_fixture( upsert_keys=["id", "name"], ) - async def test_store_in_project( + def test_store_in_project( self, project_model: Project, record_set_fixture: RecordSet ) -> None: # GIVEN a RecordSet @@ -66,7 +64,7 @@ async def test_store_in_project( assert stored_record_set.created_on is not None assert stored_record_set.created_by is not None - async def test_store_in_folder( + def test_store_in_folder( self, project_model: Project, record_set_fixture: RecordSet ) -> None: # GIVEN a folder within a project @@ -91,7 +89,7 @@ async def test_store_in_folder( assert stored_record_set.parent_id == folder.id assert stored_record_set.etag is not None - async def test_store_with_activity( + def test_store_with_activity( self, project_model: Project, record_set_fixture: RecordSet ) -> None: # GIVEN a RecordSet with activity @@ -119,7 +117,7 @@ async def test_store_with_activity( assert stored_record_set.activity.description == "Test activity for RecordSet" assert len(stored_record_set.activity.used) == 2 - async def test_store_with_annotations( + def test_store_with_annotations( self, project_model: Project, record_set_fixture: RecordSet ) -> None: # GIVEN a RecordSet with annotations @@ -146,7 +144,7 @@ async def test_store_with_annotations( assert stored_record_set.annotations["numeric_annotation"] == [42] assert stored_record_set.annotations["boolean_annotation"] == [True] - async def test_store_update_existing_record_set( + def test_store_update_existing_record_set( self, project_model: Project, record_set_fixture: RecordSet ) -> None: # GIVEN an existing RecordSet @@ -168,7 +166,7 @@ async def test_store_update_existing_record_set( assert updated_record_set.version_comment == "Updated version comment" assert updated_record_set.version_number >= original_record_set.version_number - async def test_store_validation_errors(self) -> None: + def test_store_validation_errors(self) -> None: # GIVEN a RecordSet without required fields record_set = RecordSet() @@ -203,7 +201,7 @@ def stored_record_set(self, project_model: Project) -> RecordSet: self.schedule_for_cleanup(record_set.id) return record_set - async def test_get_record_set_by_id(self, stored_record_set: RecordSet) -> None: + def test_get_record_set_by_id(self, stored_record_set: RecordSet) -> None: # GIVEN an existing RecordSet original_id = stored_record_set.id @@ -220,7 +218,7 @@ async def test_get_record_set_by_id(self, stored_record_set: RecordSet) -> None: assert retrieved_record_set.etag == stored_record_set.etag assert retrieved_record_set.version_number == stored_record_set.version_number - async def test_get_record_set_with_activity(self, project_model: Project) -> None: + def test_get_record_set_with_activity(self, project_model: Project) -> None: # GIVEN a RecordSet with activity filename = utils.make_bogus_uuid_file() self.schedule_for_cleanup(filename) @@ -254,7 +252,7 @@ async def test_get_record_set_with_activity(self, project_model: Project) -> Non assert len(retrieved_record_set.activity.used) == 1 assert retrieved_record_set.path is not None - async def test_get_validation_error(self) -> None: + def test_get_validation_error(self) -> None: # GIVEN a RecordSet without an ID record_set = RecordSet() @@ -263,7 +261,7 @@ async def test_get_validation_error(self) -> None: with pytest.raises(ValueError): record_set.get(synapse_client=self.syn) - async def test_get_non_existent_record_set(self) -> None: + def test_get_non_existent_record_set(self) -> None: # GIVEN a non-existent RecordSet ID record_set = RecordSet(id="syn999999999") @@ -281,7 +279,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_delete_entire_record_set(self, project_model: Project) -> None: + def test_delete_entire_record_set(self, project_model: Project) -> None: # GIVEN an existing RecordSet filename = utils.make_bogus_uuid_file() self.schedule_for_cleanup(filename) @@ -303,7 +301,7 @@ async def test_delete_entire_record_set(self, project_model: Project) -> None: with pytest.raises(SynapseHTTPError): RecordSet(id=record_set_id).get(synapse_client=self.syn) - async def test_delete_specific_version(self, project_model: Project) -> None: + def test_delete_specific_version(self, project_model: Project) -> None: # GIVEN an existing RecordSet with multiple versions filename1 = utils.make_bogus_uuid_file() filename2 = utils.make_bogus_uuid_file() @@ -334,7 +332,7 @@ async def test_delete_specific_version(self, project_model: Project) -> None: assert current_record_set.version_number == 1 # Should be back to version 1 assert current_record_set.description == "RecordSet version 1" - async def test_delete_validation_errors(self) -> None: + def test_delete_validation_errors(self) -> None: # GIVEN a RecordSet without an ID record_set = RecordSet() diff --git a/tests/integration/synapseclient/models/synchronous/test_schema_organization.py b/tests/integration/synapseclient/models/synchronous/test_schema_organization.py index 552960dfb..4dea1e3e6 100644 --- a/tests/integration/synapseclient/models/synchronous/test_schema_organization.py +++ b/tests/integration/synapseclient/models/synchronous/test_schema_organization.py @@ -1,9 +1,9 @@ """Integration tests for SchemaOrganization and JSONSchema classes""" +import time import uuid from typing import Any, Optional import pytest -import pytest_asyncio from synapseclient import Synapse from synapseclient.core.exceptions import SynapseHTTPError @@ -21,7 +21,7 @@ def create_test_entity_name(): return f"SYNPY.TEST.{random_string}" -async def org_exists(name: str, synapse_client: Optional[Synapse] = None) -> bool: +def org_exists(name: str, synapse_client: Optional[Synapse] = None) -> bool: """ Checks if any organizations exists with the given name @@ -41,17 +41,17 @@ async def org_exists(name: str, synapse_client: Optional[Synapse] = None) -> boo @pytest.fixture(name="module_organization", scope="module") -def fixture_module_organization(request) -> SchemaOrganization: +def fixture_module_organization(syn: Synapse, request) -> SchemaOrganization: """ Returns a created organization at the module scope. Used to hold JSON Schemas created by tests. """ org = SchemaOrganization(create_test_entity_name()) - org.store() + org.store(synapse_client=syn) def delete_org(): - for schema in org.get_json_schemas(): - schema.delete() - org.delete() + for schema in org.get_json_schemas(synapse_client=syn): + schema.delete(synapse_client=syn) + org.delete(synapse_client=syn) request.addfinalizer(delete_org) @@ -67,18 +67,18 @@ def fixture_json_schema(module_organization: SchemaOrganization) -> JSONSchema: return js -@pytest_asyncio.fixture(name="organization", loop_scope="function", scope="function") -async def fixture_organization(syn: Synapse, request) -> SchemaOrganization: +@pytest.fixture(name="organization", scope="function") +def fixture_organization(syn: Synapse, request) -> SchemaOrganization: """ Returns a Synapse organization. """ name = create_test_entity_name() org = SchemaOrganization(name) - async def delete_org(): - exists = await org_exists(name, syn) + def delete_org(): + exists = org_exists(name=name, synapse_client=syn) if exists: - org.delete() + org.delete(synapse_client=syn) request.addfinalizer(delete_org) @@ -86,24 +86,24 @@ async def delete_org(): @pytest.fixture(name="organization_with_schema", scope="function") -def fixture_organization_with_schema(request) -> SchemaOrganization: +def fixture_organization_with_schema(syn: Synapse, request) -> SchemaOrganization: """ Returns a Synapse organization. As Cleanup it checks for JSON Schemas and deletes them""" name = create_test_entity_name() org = SchemaOrganization(name) - org.store() + org.store(synapse_client=syn) js1 = JSONSchema("schema1", name) js2 = JSONSchema("schema2", name) js3 = JSONSchema("schema3", name) - js1.store({}) - js2.store({}) - js3.store({}) + js1.store(schema_body={}, synapse_client=syn) + js2.store(schema_body={}, synapse_client=syn) + js3.store(schema_body={}, synapse_client=syn) def delete_org(): - for schema in org.get_json_schemas(): - schema.delete() - org.delete() + for schema in org.get_json_schemas(synapse_client=syn): + schema.delete(synapse_client=syn) + org.delete(synapse_client=syn) request.addfinalizer(delete_org) @@ -117,7 +117,7 @@ class TestSchemaOrganization: def init(self, syn: Synapse) -> None: self.syn = syn - async def test_create_and_get(self, organization: SchemaOrganization) -> None: + def test_create_and_get(self, organization: SchemaOrganization) -> None: # GIVEN an initialized organization object that hasn't been stored in Synapse # THEN it shouldn't have any metadata besides it's name assert organization.name is not None @@ -125,16 +125,17 @@ async def test_create_and_get(self, organization: SchemaOrganization) -> None: assert organization.created_by is None assert organization.created_on is None # AND it shouldn't exist in Synapse - exists = await org_exists(organization.name, synapse_client=self.syn) + exists = org_exists(organization.name, synapse_client=self.syn) assert not exists # WHEN I store the organization the metadata will be saved organization.store(synapse_client=self.syn) + time.sleep(10) assert organization.name is not None assert organization.id is not None assert organization.created_by is not None assert organization.created_on is not None # AND it should exist in Synapse - exists = await org_exists(organization.name, synapse_client=self.syn) + exists = org_exists(organization.name, synapse_client=self.syn) assert exists # AND it should be getable by future instances with the same name org2 = SchemaOrganization(organization.name) @@ -146,15 +147,16 @@ async def test_create_and_get(self, organization: SchemaOrganization) -> None: # WHEN I try to store an organization that exists in Synapse # THEN I should get an exception with pytest.raises(SynapseHTTPError): - org2.store() + org2.store(synapse_client=self.syn) - async def test_get_json_schemas( + def test_get_json_schemas( self, organization: SchemaOrganization, organization_with_schema: SchemaOrganization, ) -> None: # GIVEN an organization with no schemas and one with 3 schemas organization.store(synapse_client=self.syn) + time.sleep(10) # THEN get_json_schema_list should return the correct list of schemas assert len(list(organization.get_json_schemas(synapse_client=self.syn))) == 0 assert ( @@ -164,9 +166,7 @@ async def test_get_json_schemas( == 3 ) - async def test_get_acl_and_update_acl( - self, organization: SchemaOrganization - ) -> None: + def test_get_acl_and_update_acl(self, organization: SchemaOrganization) -> None: # GIVEN an organization that has been initialized, but not created # THEN get_acl should raise an error with pytest.raises( @@ -175,6 +175,7 @@ async def test_get_acl_and_update_acl( organization.get_acl(synapse_client=self.syn) # GIVEN an organization that has been created organization.store(synapse_client=self.syn) + time.sleep(10) acl = organization.get_acl(synapse_client=self.syn) resource_access: list[dict[str, Any]] = acl["resourceAccess"] # THEN the resource access should be have one principal @@ -194,7 +195,7 @@ class TestJSONSchema: def init(self, syn: Synapse) -> None: self.syn = syn - async def test_store_and_get(self, json_schema: JSONSchema) -> None: + def test_store_and_get(self, json_schema: JSONSchema) -> None: # GIVEN an initialized schema object that hasn't been stored in Synapse # THEN it shouldn't have any metadata besides it's name and organization name, and uri assert json_schema.name @@ -224,41 +225,45 @@ async def test_store_and_get(self, json_schema: JSONSchema) -> None: assert js2.created_by assert js2.created_on - async def test_delete(self, organization_with_schema: SchemaOrganization) -> None: + def test_delete(self, organization_with_schema: SchemaOrganization) -> None: # GIVEN an organization with 3 schema - schemas = list(organization_with_schema.get_json_schemas()) + schemas = list( + organization_with_schema.get_json_schemas(synapse_client=self.syn) + ) assert len(schemas) == 3 # WHEN deleting one of those schemas schema = schemas[0] - schema.delete() + schema.delete(synapse_client=self.syn) # THEN there should be only two left - schemas = list(organization_with_schema.get_json_schemas()) + schemas = list( + organization_with_schema.get_json_schemas(synapse_client=self.syn) + ) assert len(schemas) == 2 - async def test_delete_version(self, json_schema: JSONSchema) -> None: + def test_delete_version(self, json_schema: JSONSchema) -> None: # GIVEN an organization and a JSONSchema - json_schema.store(schema_body={}, version="0.0.1") + json_schema.store(schema_body={}, version="0.0.1", synapse_client=self.syn) # THEN that schema should have one version - js_versions = list(json_schema.get_versions()) + js_versions = list(json_schema.get_versions(synapse_client=self.syn)) assert len(js_versions) == 1 # WHEN storing a second version - json_schema.store(schema_body={}, version="0.0.2") + json_schema.store(schema_body={}, version="0.0.2", synapse_client=self.syn) # THEN that schema should have two versions - js_versions = list(json_schema.get_versions()) + js_versions = list(json_schema.get_versions(synapse_client=self.syn)) assert len(js_versions) == 2 # AND they should be the ones stored versions = [js_version.semantic_version for js_version in js_versions] assert versions == ["0.0.1", "0.0.2"] # WHEN deleting the first schema version - json_schema.delete(version="0.0.1") + json_schema.delete(version="0.0.1", synapse_client=self.syn) # THEN there should only be one version left - js_versions = list(json_schema.get_versions()) + js_versions = list(json_schema.get_versions(synapse_client=self.syn)) assert len(js_versions) == 1 # AND it should be the second version versions = [js_version.semantic_version for js_version in js_versions] assert versions == ["0.0.2"] - async def test_get_versions(self, json_schema: JSONSchema) -> None: + def test_get_versions(self, json_schema: JSONSchema) -> None: # GIVEN an schema that hasn't been created # THEN get_versions should return an empty list assert len(list(json_schema.get_versions(synapse_client=self.syn))) == 0 @@ -273,7 +278,7 @@ async def test_get_versions(self, json_schema: JSONSchema) -> None: assert len(schemas) == 1 assert schemas[0].semantic_version == "0.0.1" - async def test_get_body(self, json_schema: JSONSchema) -> None: + def test_get_body(self, json_schema: JSONSchema) -> None: # GIVEN an schema that hasn't been created # WHEN creating a schema with 2 version first_body = {} diff --git a/tests/integration/synapseclient/models/synchronous/test_submissionview.py b/tests/integration/synapseclient/models/synchronous/test_submissionview.py index 50fcf9908..b522541da 100644 --- a/tests/integration/synapseclient/models/synchronous/test_submissionview.py +++ b/tests/integration/synapseclient/models/synchronous/test_submissionview.py @@ -1,5 +1,5 @@ -import asyncio import tempfile +import time import uuid from typing import Callable @@ -26,9 +26,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_create_submissionview_with_columns( - self, project_model: Project - ) -> None: + def test_create_submissionview_with_columns(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope evaluation = self.syn.store( @@ -53,7 +51,7 @@ async def test_create_submissionview_with_columns( # WHEN I store the submissionview submissionview = submissionview.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # THEN the submissionview should be created assert submissionview.id is not None @@ -85,7 +83,7 @@ async def test_create_submissionview_with_columns( # WHEN I store the submissionview submissionview2 = submissionview2.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview2.id) + self.schedule_for_cleanup(submissionview2) # THEN the submissionview should be created assert submissionview2.id is not None @@ -121,7 +119,7 @@ async def test_create_submissionview_with_columns( # WHEN I store the submissionview submissionview3 = submissionview3.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview3.id) + self.schedule_for_cleanup(submissionview3) # THEN I can retrieve that submissionview with both columns new_submissionview_instance3 = SubmissionView(id=submissionview3.id).get( @@ -138,9 +136,7 @@ async def test_create_submissionview_with_columns( == ColumnType.INTEGER ) - async def test_create_submissionview_special_cases( - self, project_model: Project - ) -> None: + def test_create_submissionview_special_cases(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope evaluation = self.syn.store( @@ -190,7 +186,7 @@ async def test_create_submissionview_special_cases( # WHEN I store the submissionview submissionview2 = submissionview2.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview2.id) + self.schedule_for_cleanup(submissionview2) # THEN the submissionview should be created but with empty scope retrieved_view = SubmissionView(id=submissionview2.id).get( @@ -217,7 +213,7 @@ async def test_create_submissionview_special_cases( # WHEN I store the submissionview submissionview3 = submissionview3.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview3.id) + self.schedule_for_cleanup(submissionview3) # THEN the submissionview should only contain our custom columns retrieved_view3 = SubmissionView(id=submissionview3.id).get( @@ -237,7 +233,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_column_modifications(self, project_model: Project) -> None: + def test_column_modifications(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope evaluation = self.syn.store( @@ -263,7 +259,7 @@ async def test_column_modifications(self, project_model: Project) -> None: scope_ids=[evaluation.id], ) submissionview = submissionview.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # Test Case 1: Rename column # WHEN I rename the column @@ -300,7 +296,7 @@ async def test_column_modifications(self, project_model: Project) -> None: assert new_column_name not in updated_view2.columns assert column_to_keep in updated_view2.columns - async def test_scope_modifications(self, project_model: Project) -> None: + def test_scope_modifications(self, project_model: Project) -> None: # GIVEN a project to work with # AND two evaluations for testing scope changes evaluation1 = self.syn.store( @@ -329,7 +325,7 @@ async def test_scope_modifications(self, project_model: Project) -> None: scope_ids=[evaluation1.id], ) submissionview = submissionview.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # Test Case 1: Update scope to include multiple evaluations # WHEN I update the scope to include both evaluations @@ -370,7 +366,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_query_submissionview(self, project_model: Project) -> None: + def test_query_submissionview(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope evaluation = self.syn.store( @@ -390,7 +386,7 @@ async def test_query_submissionview(self, project_model: Project) -> None: scope_ids=[evaluation.id], ) submissionview = submissionview.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # Test Case 1: Simple query # WHEN I query the submissionview with a standard query @@ -429,7 +425,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_submissionview_snapshots(self, project_model: Project) -> None: + def test_submissionview_snapshots(self, project_model: Project) -> None: # GIVEN a project to work with # AND an evaluation to use in the scope evaluation = self.syn.store( @@ -458,7 +454,7 @@ async def test_submissionview_snapshots(self, project_model: Project) -> None: # AND the submissionview is stored in Synapse submissionview = submissionview.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # WHEN I snapshot the submissionview snapshot = submissionview.snapshot( @@ -525,7 +521,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_submission_lifecycle(self, project_model: Project) -> None: + def test_submission_lifecycle(self, project_model: Project) -> None: """Test submission lifecycle in a submission view: adding and removing submissions.""" # GIVEN an evaluation evaluation = self.syn.store( @@ -545,7 +541,7 @@ async def test_submission_lifecycle(self, project_model: Project) -> None: scope_ids=[evaluation.id], ) submissionview = submissionview.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # AND a file for submission with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f: @@ -580,7 +576,7 @@ async def test_submission_lifecycle(self, project_model: Project) -> None: success = True break - asyncio.sleep(wait_seconds) + time.sleep(wait_seconds) wait_seconds *= 1.5 assert success, "Submission did not appear in the view" @@ -604,12 +600,12 @@ async def test_submission_lifecycle(self, project_model: Project) -> None: success = True break - asyncio.sleep(wait_seconds) + time.sleep(wait_seconds) wait_seconds *= 1.5 assert success, "Deleted submission still appears in the view" - async def test_multiple_submissions(self, project_model: Project) -> None: + def test_multiple_submissions(self, project_model: Project) -> None: """Test that multiple submissions to an evaluation appear in a submission view.""" # GIVEN an evaluation evaluation = self.syn.store( @@ -629,7 +625,7 @@ async def test_multiple_submissions(self, project_model: Project) -> None: scope_ids=[evaluation.id], ) submissionview = submissionview.store(synapse_client=self.syn) - self.schedule_for_cleanup(submissionview.id) + self.schedule_for_cleanup(submissionview) # WHEN I create and upload multiple test files for submission files = [] @@ -676,7 +672,7 @@ async def test_multiple_submissions(self, project_model: Project) -> None: success = True break - asyncio.sleep(wait_seconds) + time.sleep(wait_seconds) wait_seconds *= 1.5 assert ( diff --git a/tests/integration/synapseclient/models/synchronous/test_table.py b/tests/integration/synapseclient/models/synchronous/test_table.py index cc2dd26af..a1b30e1d3 100644 --- a/tests/integration/synapseclient/models/synchronous/test_table.py +++ b/tests/integration/synapseclient/models/synchronous/test_table.py @@ -37,7 +37,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_create_table_with_different_column_configurations( + def test_create_table_with_different_column_configurations( self, project_model: Project ) -> None: """Test creating tables with different column configurations.""" @@ -119,9 +119,7 @@ async def test_create_table_with_different_column_configurations( new_table_instance.columns["test_column2"].column_type == ColumnType.INTEGER ) - async def test_create_table_with_many_column_types( - self, project_model: Project - ) -> None: + def test_create_table_with_many_column_types(self, project_model: Project) -> None: """Test creating a table with many column types with different allowed characters.""" # GIVEN a table with many columns with various naming patterns table_name = str(uuid.uuid4()) @@ -165,9 +163,7 @@ async def test_create_table_with_many_column_types( assert name in new_table_instance.columns assert new_table_instance.columns[name].column_type == ColumnType.STRING - async def test_create_table_with_invalid_column( - self, project_model: Project - ) -> None: + def test_create_table_with_invalid_column(self, project_model: Project) -> None: """Test creating a table with an invalid column configuration.""" # GIVEN a table with an invalid column (maximum_size too large) table_name = str(uuid.uuid4()) @@ -194,9 +190,7 @@ async def test_create_table_with_invalid_column( in str(e.value) ) - async def test_table_creation_with_data_sources( - self, project_model: Project - ) -> None: + def test_table_creation_with_data_sources(self, project_model: Project) -> None: """Test creating tables with different data sources.""" # Test with dictionary data # GIVEN a table with no columns defined and dictionary data @@ -266,9 +260,7 @@ async def test_table_creation_with_data_sources( results["column_string"], csv_data["column_string"] ) - async def test_create_table_with_string_column( - self, project_model: Project - ) -> None: + def test_create_table_with_string_column(self, project_model: Project) -> None: """Test creating tables with string column configurations.""" # GIVEN a table with columns table_name = str(uuid.uuid4()) @@ -313,7 +305,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_store_rows_from_csv_infer_columns( + def test_store_rows_from_csv_infer_columns( self, mocker: MockerFixture, project_model: Project ) -> None: # SPYs @@ -366,7 +358,7 @@ async def test_store_rows_from_csv_infer_columns( results["float_string"], data_for_table["float_string"] ) - async def test_update_rows_from_csv_infer_columns_no_column_updates( + def test_update_rows_from_csv_infer_columns_no_column_updates( self, project_model: Project ) -> None: # GIVEN a table with no columns defined @@ -421,7 +413,7 @@ async def test_update_rows_from_csv_infer_columns_no_column_updates( updated_results_from_table["column_string"], query_results["column_string"] ) - async def test_store_rows_from_csv_no_columns(self, project_model: Project) -> None: + def test_store_rows_from_csv_no_columns(self, project_model: Project) -> None: # GIVEN a table with no columns defined table_name = str(uuid.uuid4()) table = Table(name=table_name, parent_id=project_model.id) @@ -452,7 +444,7 @@ async def test_store_rows_from_csv_no_columns(self, project_model: Project) -> N in str(e.value) ) - async def test_store_rows_from_manually_defined_columns( + def test_store_rows_from_manually_defined_columns( self, mocker: MockerFixture, project_model: Project ) -> None: # SPYs @@ -511,7 +503,7 @@ async def test_store_rows_from_manually_defined_columns( results["float_column"], data_for_table["float_column"] ) - async def test_store_rows_on_existing_table_with_schema_storage_strategy( + def test_store_rows_on_existing_table_with_schema_storage_strategy( self, mocker: MockerFixture, project_model: Project ) -> None: # SPYs @@ -565,7 +557,7 @@ async def test_store_rows_on_existing_table_with_schema_storage_strategy( results["column_string"], data_for_table["column_string"] ) - async def test_store_rows_on_existing_table_with_expanding_string_column( + def test_store_rows_on_existing_table_with_expanding_string_column( self, mocker: MockerFixture, project_model: Project ) -> None: # SPYs @@ -635,7 +627,7 @@ async def test_store_rows_on_existing_table_with_expanding_string_column( # AND the column should have been expanded assert table.columns["column_string"].maximum_size == 54 - async def test_store_rows_on_existing_table_adding_column( + def test_store_rows_on_existing_table_adding_column( self, mocker: MockerFixture, project_model: Project ) -> None: # SPYs @@ -694,7 +686,7 @@ async def test_store_rows_on_existing_table_adding_column( results["column_key_2"], data_for_table["column_key_2"] ) - async def test_store_rows_on_existing_table_no_schema_storage_strategy( + def test_store_rows_on_existing_table_no_schema_storage_strategy( self, project_model: Project ) -> None: # GIVEN a table with a column defined @@ -729,7 +721,7 @@ async def test_store_rows_on_existing_table_no_schema_storage_strategy( in str(e.value) ) - async def test_store_rows_as_csv_being_split_and_uploaded( + def test_store_rows_as_csv_being_split_and_uploaded( self, project_model: Project, mocker: MockerFixture ) -> None: # GIVEN a table in Synapse @@ -795,7 +787,7 @@ async def test_store_rows_as_csv_being_split_and_uploaded( # AND The spy should have been called in multiple batches assert spy_send_job.call_count == 5 - async def test_store_rows_as_df_being_split_and_uploaded( + def test_store_rows_as_df_being_split_and_uploaded( self, project_model: Project, mocker: MockerFixture ) -> None: # GIVEN a table in Synapse @@ -860,7 +852,7 @@ async def test_store_rows_as_df_being_split_and_uploaded( assert spy_send_job.call_count == 3 @skip("Skip in normal testing because the large size makes it slow") - async def test_store_rows_as_large_df_being_split_and_uploaded( + def test_store_rows_as_large_df_being_split_and_uploaded( self, project_model: Project, mocker: MockerFixture ) -> None: # GIVEN a table in Synapse @@ -879,7 +871,7 @@ async def test_store_rows_as_large_df_being_split_and_uploaded( ) table = table.store(synapse_client=self.syn) self.schedule_for_cleanup(table.id) - spy_send_job = mocker.spy(asynchronous_job_module, "send_job_async") + spy_send_job = mocker.spy(asynchronous_job_module, "send_job") # AND data that will be split into multiple parts rows_in_table = 20 @@ -930,7 +922,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_upsert_operations_with_various_data_sources( + def test_upsert_operations_with_various_data_sources( self, project_model: Project, mocker: MockerFixture ) -> None: """Test various upsert operations with different data sources and options.""" @@ -1069,7 +1061,7 @@ async def test_upsert_operations_with_various_data_sources( # The spy should not have been called assert spy_table_update.call_count == 0 - async def test_upsert_with_multi_value_key(self, project_model: Project) -> None: + def test_upsert_with_multi_value_key(self, project_model: Project) -> None: """Test upserting rows using multiple columns as the primary key.""" # GIVEN a table in Synapse table_name = str(uuid.uuid4()) @@ -1154,7 +1146,7 @@ async def test_upsert_with_multi_value_key(self, project_model: Project) -> None # Should have 9 rows now (6 from before + 3 new) assert len(results) == 9 - async def test_upsert_with_large_data_and_batching( + def test_upsert_with_large_data_and_batching( self, project_model: Project, mocker: MockerFixture ) -> None: """Test upserting with large data strings that require batching.""" @@ -1844,7 +1836,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_delete_single_row(self, project_model: Project) -> None: + def test_delete_single_row(self, project_model: Project) -> None: # GIVEN a table in Synapse table_name = str(uuid.uuid4()) table = Table( @@ -1879,7 +1871,7 @@ async def test_delete_single_row(self, project_model: Project) -> None: # AND only 2 rows should exist on the table assert len(results) == 2 - async def test_delete_multiple_rows(self, project_model: Project) -> None: + def test_delete_multiple_rows(self, project_model: Project) -> None: # GIVEN a table in Synapse table_name = str(uuid.uuid4()) table = Table( @@ -1914,7 +1906,7 @@ async def test_delete_multiple_rows(self, project_model: Project) -> None: # AND only 1 row should exist on the table assert len(results) == 1 - async def test_delete_no_rows(self, project_model: Project) -> None: + def test_delete_no_rows(self, project_model: Project) -> None: # GIVEN a table in Synapse table_name = str(uuid.uuid4()) table = Table( @@ -1955,7 +1947,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_column_rename(self, project_model: Project) -> None: + def test_column_rename(self, project_model: Project) -> None: # GIVEN a table in Synapse table_name = str(uuid.uuid4()) old_column_name = "column_string" @@ -1985,7 +1977,7 @@ async def test_column_rename(self, project_model: Project) -> None: assert new_table_instance.columns[new_column_name] is not None assert old_column_name not in new_table_instance.columns - async def test_delete_column(self, project_model: Project) -> None: + def test_delete_column(self, project_model: Project) -> None: # GIVEN a table in Synapse table_name = str(uuid.uuid4()) old_column_name = "column_string" @@ -2031,7 +2023,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_query_to_csv(self, project_model: Project) -> None: + def test_query_to_csv(self, project_model: Project) -> None: # GIVEN a table with a column defined table_name = str(uuid.uuid4()) table = Table( @@ -2081,7 +2073,7 @@ async def test_query_to_csv(self, project_model: Project) -> None: as_dataframe["float_column"], data_for_table["float_column"] ) - async def test_part_mask_query_everything(self, project_model: Project) -> None: + def test_part_mask_query_everything(self, project_model: Project) -> None: # GIVEN a table with a column defined table_name = str(uuid.uuid4()) table = Table( @@ -2139,7 +2131,7 @@ async def test_part_mask_query_everything(self, project_model: Project) -> None: assert results.sum_file_sizes.sum_file_size_bytes is not None assert results.last_updated_on is not None - async def test_part_mask_query_results_only(self, project_model: Project) -> None: + def test_part_mask_query_results_only(self, project_model: Project) -> None: # GIVEN a table with a column defined table_name = str(uuid.uuid4()) table = Table( @@ -2197,7 +2189,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_snapshot_basic(self, project_model: Project) -> None: + def test_snapshot_basic(self, project_model: Project) -> None: """Test creating a basic snapshot of a table.""" # GIVEN a table with some data table = Table( @@ -2208,7 +2200,7 @@ async def test_snapshot_basic(self, project_model: Project) -> None: Column(name="col2", column_type=ColumnType.INTEGER), ], ) - table = await table.store_async(synapse_client=self.syn) + table = table.store(synapse_client=self.syn) self.schedule_for_cleanup(table.id) # Store some data @@ -2243,7 +2235,7 @@ async def test_snapshot_basic(self, project_model: Project) -> None: assert latest_table.version_comment == "in progress" assert latest_table.version_number > 1 - async def test_snapshot_with_activity(self, project_model: Project) -> None: + def test_snapshot_with_activity(self, project_model: Project) -> None: """Test creating a snapshot with activity (provenance).""" # GIVEN a table with some data and an activity table = Table( @@ -2254,7 +2246,7 @@ async def test_snapshot_with_activity(self, project_model: Project) -> None: Column(name="col2", column_type=ColumnType.INTEGER), ], ) - table = await table.store_async(synapse_client=self.syn) + table = table.store(synapse_client=self.syn) self.schedule_for_cleanup(table.id) # Create and store an activity @@ -2301,7 +2293,7 @@ async def test_snapshot_with_activity(self, project_model: Project) -> None: assert latest_table.version_comment == "in progress" assert latest_table.version_number > 1 - async def test_snapshot_without_activity(self, project_model: Project) -> None: + def test_snapshot_without_activity(self, project_model: Project) -> None: """Test creating a snapshot without including activity.""" # GIVEN a table with some data and an activity table = Table( @@ -2312,7 +2304,7 @@ async def test_snapshot_without_activity(self, project_model: Project) -> None: Column(name="col2", column_type=ColumnType.INTEGER), ], ) - table = await table.store_async(synapse_client=self.syn) + table = table.store(synapse_client=self.syn) self.schedule_for_cleanup(table.id) # Create and store an activity @@ -2358,7 +2350,7 @@ async def test_snapshot_without_activity(self, project_model: Project) -> None: assert latest_table.version_comment == "in progress" assert latest_table.version_number > 1 - async def test_snapshot_minimal_args(self, project_model: Project) -> None: + def test_snapshot_minimal_args(self, project_model: Project) -> None: """Test creating a snapshot with minimal arguments.""" # GIVEN a table with some data table = Table( @@ -2369,7 +2361,7 @@ async def test_snapshot_minimal_args(self, project_model: Project) -> None: Column(name="col2", column_type=ColumnType.INTEGER), ], ) - table = await table.store_async(synapse_client=self.syn) + table = table.store(synapse_client=self.syn) self.schedule_for_cleanup(table.id) # Store some data diff --git a/tests/integration/synapseclient/models/synchronous/test_team.py b/tests/integration/synapseclient/models/synchronous/test_team.py index b43841b1c..72e90b88c 100644 --- a/tests/integration/synapseclient/models/synchronous/test_team.py +++ b/tests/integration/synapseclient/models/synchronous/test_team.py @@ -1,6 +1,6 @@ """Integration tests for Team.""" -import asyncio +import time import uuid import pytest @@ -40,12 +40,12 @@ def verify_team_properties(self, actual_team, expected_team): assert actual_team.created_by == expected_team.created_by assert actual_team.modified_by == expected_team.modified_by - async def test_team_lifecycle(self) -> None: + def test_team_lifecycle(self) -> None: """Test create, retrieve (by ID, name), and delete operations""" # GIVEN a team object # WHEN I create the team on Synapse - test_team = self.team.create() + test_team = self.team.create(synapse_client=self.syn) # THEN I expect the created team to be returned with correct properties assert test_team.id is not None @@ -68,13 +68,13 @@ async def test_team_lifecycle(self) -> None: self.verify_team_properties(id_team, test_team) # WHEN I retrieve the team using the static from_id method - from_id_team = Team.from_id(id=test_team.id) + from_id_team = Team.from_id(id=test_team.id, synapse_client=self.syn) # THEN the retrieved team should match the created team self.verify_team_properties(from_id_team, test_team) # Name-based retrieval is eventually consistent, so we need to wait - await asyncio.sleep(10) + time.sleep(10) # WHEN I retrieve the team using a Team object with name name_team = Team(name=test_team.name) @@ -84,30 +84,30 @@ async def test_team_lifecycle(self) -> None: self.verify_team_properties(name_team, test_team) # WHEN I retrieve the team using the static from_name method - from_name_team = Team.from_name(name=test_team.name) + from_name_team = Team.from_name(name=test_team.name, synapse_client=self.syn) # THEN the retrieved team should match the created team self.verify_team_properties(from_name_team, test_team) # WHEN I delete the team - test_team.delete() + test_team.delete(synapse_client=self.syn) # THEN the team should no longer exist with pytest.raises( SynapseHTTPError, match=f"404 Client Error: Team id: '{test_team.id}' does not exist", ): - Team.from_id(id=test_team.id) + Team.from_id(id=test_team.id, synapse_client=self.syn) - async def test_team_membership_and_invitations(self) -> None: + def test_team_membership_and_invitations(self) -> None: """Test team membership and invitation functionality""" # GIVEN a team object # WHEN I create the team on Synapse - test_team = self.team.create() + test_team = self.team.create(synapse_client=self.syn) # AND check the team members - members = test_team.members() + members = test_team.members(synapse_client=self.syn) # THEN the team should have exactly one member (the creator), who is an admin assert len(members) == 1 @@ -117,8 +117,7 @@ async def test_team_membership_and_invitations(self) -> None: # WHEN I invite a user to the team invite = test_team.invite( - user=self.TEST_USER, - message=self.TEST_MESSAGE, + user=self.TEST_USER, message=self.TEST_MESSAGE, synapse_client=self.syn ) # THEN the invite should be created successfully @@ -130,7 +129,7 @@ async def test_team_membership_and_invitations(self) -> None: assert invite["createdBy"] is not None # WHEN I check the open invitations - invitations = test_team.open_invitations() + invitations = test_team.open_invitations(synapse_client=self.syn) # THEN I should see the invitation I just created assert len(invitations) == 1 @@ -142,18 +141,18 @@ async def test_team_membership_and_invitations(self) -> None: assert invitations[0]["createdBy"] is not None # Clean up - test_team.delete() + test_team.delete(synapse_client=self.syn) - async def test_team_membership_status(self) -> None: + def test_team_membership_status(self) -> None: """Test getting user membership status in a team""" # GIVEN a team object # WHEN I create the team on Synapse - test_team = self.team.create() + test_team = self.team.create(synapse_client=self.syn) # AND I get the membership status for the creator (who should be a member) creator_status = test_team.get_user_membership_status( - user_id=self.syn.getUserProfile().ownerId + user_id=self.syn.getUserProfile().ownerId, synapse_client=self.syn ) # THEN the creator should have membership status indicating they are a member @@ -167,13 +166,15 @@ async def test_team_membership_status(self) -> None: assert creator_status.can_send_email is True # WHEN I invite a test user to the team - invite = await test_team.invite_async( + test_team.invite( user=self.TEST_USER, message=self.TEST_MESSAGE, + synapse_client=self.syn, ) # Check the invited user's status - invited_status = await test_team.get_user_membership_status_async( - user_id=self.syn.getUserProfile(self.TEST_USER).ownerId + invited_status = test_team.get_user_membership_status( + user_id=self.syn.getUserProfile(self.TEST_USER).ownerId, + synapse_client=self.syn, ) # THEN the invited user should show they have an open invitation @@ -185,4 +186,4 @@ async def test_team_membership_status(self) -> None: assert invited_status.is_member is False # Clean up - test_team.delete() + test_team.delete(synapse_client=self.syn) diff --git a/tests/integration/synapseclient/models/synchronous/test_user.py b/tests/integration/synapseclient/models/synchronous/test_user.py index aff2e9de4..d7987fc11 100644 --- a/tests/integration/synapseclient/models/synchronous/test_user.py +++ b/tests/integration/synapseclient/models/synchronous/test_user.py @@ -16,27 +16,31 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup - async def test_from_id(self) -> None: + def test_from_id(self) -> None: # GIVEN our test profile integration_test_profile = UserProfile().get(synapse_client=self.syn) # WHEN we get the profile by ID - profile = UserProfile.from_id(integration_test_profile.id) + profile = UserProfile.from_id( + integration_test_profile.id, synapse_client=self.syn + ) # THEN we expect the profile to be the same as the one we got from the fixture assert profile == integration_test_profile - async def test_from_username(self) -> None: + def test_from_username(self) -> None: # GIVEN our test profile integration_test_profile = UserProfile().get(synapse_client=self.syn) # WHEN we get the profile by username - profile = UserProfile.from_username(integration_test_profile.username) + profile = UserProfile.from_username( + integration_test_profile.username, synapse_client=self.syn + ) # THEN we expect the profile to be the same as the one we got from the fixture assert profile == integration_test_profile - async def test_is_certified_id(self) -> None: + def test_is_certified_id(self) -> None: # GIVEN out test profile integration_test_profile = UserProfile().get(synapse_client=self.syn) @@ -44,12 +48,12 @@ async def test_is_certified_id(self) -> None: profile_copy = UserProfile(id=integration_test_profile.id) # WHEN we check if the profile is certified - is_certified = profile_copy.is_certified() + is_certified = profile_copy.is_certified(synapse_client=self.syn) # THEN we expect the profile to not be certified assert is_certified is False - async def test_is_certified_username(self) -> None: + def test_is_certified_username(self) -> None: # GIVEN out test profile integration_test_profile = UserProfile().get(synapse_client=self.syn) @@ -57,7 +61,7 @@ async def test_is_certified_username(self) -> None: profile_copy = UserProfile(username=integration_test_profile.username) # WHEN we check if the profile is certified - is_certified = profile_copy.is_certified() + is_certified = profile_copy.is_certified(synapse_client=self.syn) # THEN we expect the profile to not be certified assert is_certified is False diff --git a/tests/integration/synapseclient/models/synchronous/test_virtualtable.py b/tests/integration/synapseclient/models/synchronous/test_virtualtable.py index 72ff39640..fe0eb1f2b 100644 --- a/tests/integration/synapseclient/models/synchronous/test_virtualtable.py +++ b/tests/integration/synapseclient/models/synchronous/test_virtualtable.py @@ -1,4 +1,4 @@ -import asyncio +import time import uuid from typing import Callable @@ -32,9 +32,7 @@ def base_table_with_columns(self, project_model: Project) -> Table: self.schedule_for_cleanup(table.id) return table - async def test_virtual_table_validation_scenarios( - self, project_model: Project - ) -> None: + def test_virtual_table_validation_scenarios(self, project_model: Project) -> None: # GIVEN different virtual table scenarios with validation issues # Test case 1: Empty defining SQL @@ -89,9 +87,7 @@ async def test_virtual_table_validation_scenarios( ): invalid_sql_virtual_table.store(synapse_client=self.syn) - async def test_virtual_table_lifecycle( - self, base_table_with_columns: Table - ) -> None: + def test_virtual_table_lifecycle(self, base_table_with_columns: Table) -> None: # GIVEN a table with columns and a new virtual table table = base_table_with_columns virtual_table_name = str(uuid.uuid4()) @@ -158,7 +154,7 @@ def init(self, syn: Synapse, schedule_for_cleanup: Callable[..., None]) -> None: self.schedule_for_cleanup = schedule_for_cleanup @pytest.fixture(scope="function") - async def base_table_with_data(self, project_model: Project) -> Table: + def base_table_with_data(self, project_model: Project) -> Table: # Create a table with columns and data table_name = str(uuid.uuid4()) table = Table( @@ -185,7 +181,7 @@ async def base_table_with_data(self, project_model: Project) -> Table: return table - async def test_virtual_table_data_queries( + def test_virtual_table_data_queries( self, project_model: Project, base_table_with_data: Table ) -> None: # GIVEN a table with data and various virtual tables with different SQL transformations @@ -228,7 +224,7 @@ async def test_virtual_table_data_queries( self.schedule_for_cleanup(virtual_table_ordered.id) # Wait for the virtual tables to be ready - await asyncio.sleep(2) + time.sleep(2) # WHEN querying the full-data virtual table all_result = virtual_table_all.query( @@ -274,9 +270,7 @@ async def test_virtual_table_data_queries( assert ordered_result["age"].tolist() == [35, 30, 25] assert ordered_result["name"].tolist() == ["Charlie", "Alice", "Bob"] - async def test_virtual_table_data_synchronization( - self, project_model: Project - ) -> None: + def test_virtual_table_data_synchronization(self, project_model: Project) -> None: # GIVEN a table with columns but no initial data table_name = str(uuid.uuid4()) table = Table( @@ -300,7 +294,7 @@ async def test_virtual_table_data_synchronization( self.schedule_for_cleanup(virtual_table.id) # Wait for the virtual table to be ready - await asyncio.sleep(2) + time.sleep(2) # WHEN querying the virtual table with empty source table empty_result = virtual_table.query( @@ -315,7 +309,7 @@ async def test_virtual_table_data_synchronization( table.store_rows(data, synapse_client=self.syn) # Wait for the updates to propagate - await asyncio.sleep(2) + time.sleep(2) # AND querying the virtual table again added_data_result = virtual_table.query( @@ -333,7 +327,7 @@ async def test_virtual_table_data_synchronization( ) # Wait for changes to propagate - await asyncio.sleep(2) + time.sleep(2) # AND querying the virtual table again removed_data_result = virtual_table.query( @@ -343,7 +337,7 @@ async def test_virtual_table_data_synchronization( # THEN the virtual table should reflect the removed data assert len(removed_data_result) == 0 - async def test_virtual_table_sql_updates( + def test_virtual_table_sql_updates( self, project_model: Project, base_table_with_data: Table ) -> None: # GIVEN a table with data and a virtual table with initial SQL @@ -357,7 +351,7 @@ async def test_virtual_table_sql_updates( self.schedule_for_cleanup(virtual_table.id) # Wait for the virtual table to be ready - await asyncio.sleep(2) + time.sleep(2) # WHEN querying the virtual table with initial SQL initial_result = virtual_table.query( @@ -375,7 +369,7 @@ async def test_virtual_table_sql_updates( virtual_table = virtual_table.store(synapse_client=self.syn) # Wait for the update to propagate - await asyncio.sleep(2) + time.sleep(2) # AND querying the virtual table again updated_result = virtual_table.query( @@ -396,7 +390,7 @@ async def test_virtual_table_sql_updates( assert "city" in retrieved_virtual_table.columns.keys() assert "age" not in retrieved_virtual_table.columns.keys() - async def test_virtual_table_with_aggregation(self, project_model: Project) -> None: + def test_virtual_table_with_aggregation(self, project_model: Project) -> None: # GIVEN a table with data suitable for aggregation table_name = str(uuid.uuid4()) table = Table( @@ -435,7 +429,7 @@ async def test_virtual_table_with_aggregation(self, project_model: Project) -> N self.schedule_for_cleanup(virtual_table.id) # Wait for virtual table to be ready - await asyncio.sleep(3) + time.sleep(3) # WHEN querying the aggregation virtual table query_result = virtual_table.query( diff --git a/tests/integration/synapseclient/operations/synchronous/test_factory_operations.py b/tests/integration/synapseclient/operations/synchronous/test_factory_operations.py index 9da882a38..a6ead3f0e 100644 --- a/tests/integration/synapseclient/operations/synchronous/test_factory_operations.py +++ b/tests/integration/synapseclient/operations/synchronous/test_factory_operations.py @@ -66,7 +66,7 @@ def create_activity(self) -> Activity: ], ) - async def test_get_project_by_id(self, project_model: Project) -> None: + def test_get_project_by_id(self, project_model: Project) -> None: """Test retrieving a Project entity by Synapse ID.""" # GIVEN a project exists project_id = project_model.id @@ -86,7 +86,7 @@ async def test_get_project_by_id(self, project_model: Project) -> None: assert retrieved_project.created_by is not None assert retrieved_project.modified_by is not None - async def test_get_project_by_name(self, project_model: Project) -> None: + def test_get_project_by_name(self, project_model: Project) -> None: """Test retrieving a Project entity by name.""" # GIVEN a project exists project_name = project_model.name @@ -101,7 +101,7 @@ async def test_get_project_by_name(self, project_model: Project) -> None: assert retrieved_project.id == project_model.id assert retrieved_project.name == project_name - async def test_get_folder_by_id(self, project_model: Project) -> None: + def test_get_folder_by_id(self, project_model: Project) -> None: """Test retrieving a Folder entity by Synapse ID.""" # GIVEN a folder in a project folder = Folder( @@ -124,7 +124,7 @@ async def test_get_folder_by_id(self, project_model: Project) -> None: assert retrieved_folder.etag is not None assert retrieved_folder.created_on is not None - async def test_get_folder_by_name(self, project_model: Project) -> None: + def test_get_folder_by_name(self, project_model: Project) -> None: """Test retrieving a Folder entity by name and parent ID.""" # GIVEN a folder in a project folder_name = f"test_folder_{str(uuid.uuid4())[:8]}" @@ -146,7 +146,7 @@ async def test_get_folder_by_name(self, project_model: Project) -> None: assert retrieved_folder.id == stored_folder.id assert retrieved_folder.name == folder_name - async def test_get_file_by_id_default_options(self, project_model: Project) -> None: + def test_get_file_by_id_default_options(self, project_model: Project) -> None: """Test retrieving a File entity by Synapse ID with default options.""" # GIVEN a file in a project file = self.create_file_instance() @@ -166,9 +166,7 @@ async def test_get_file_by_id_default_options(self, project_model: Project) -> N assert retrieved_file.data_file_handle_id is not None assert retrieved_file.file_handle is not None - async def test_get_file_by_id_with_file_options( - self, project_model: Project - ) -> None: + def test_get_file_by_id_with_file_options(self, project_model: Project) -> None: """Test retrieving a File entity by Synapse ID with custom FileOptions.""" # GIVEN a file in a project file = self.create_file_instance() @@ -200,7 +198,7 @@ async def test_get_file_by_id_with_file_options( retrieved_file.path ) - async def test_get_file_by_id_metadata_only(self, project_model: Project) -> None: + def test_get_file_by_id_metadata_only(self, project_model: Project) -> None: """Test retrieving a File entity metadata without downloading.""" # GIVEN a file in a project file = self.create_file_instance() @@ -224,7 +222,7 @@ async def test_get_file_by_id_metadata_only(self, project_model: Project) -> Non assert retrieved_file.download_file is False assert retrieved_file.data_file_handle_id is not None - async def test_get_file_by_id_with_activity(self, project_model: Project) -> None: + def test_get_file_by_id_with_activity(self, project_model: Project) -> None: """Test retrieving a File entity with activity information.""" # GIVEN a file with activity in a project file = self.create_file_instance() @@ -253,9 +251,7 @@ async def test_get_file_by_id_with_activity(self, project_model: Project) -> Non == "Activity for testing factory operations" ) - async def test_get_file_by_id_specific_version( - self, project_model: Project - ) -> None: + def test_get_file_by_id_specific_version(self, project_model: Project) -> None: """Test retrieving a specific version of a File entity.""" # GIVEN a file in a project file = self.create_file_instance() @@ -279,9 +275,7 @@ async def test_get_file_by_id_specific_version( assert retrieved_file.version_number == 1 assert retrieved_file.version_comment == "Version 1" - async def test_get_table_by_id_default_options( - self, project_model: Project - ) -> None: + def test_get_table_by_id_default_options(self, project_model: Project) -> None: """Test retrieving a Table entity by Synapse ID with default options.""" # GIVEN a table in a project columns = [ @@ -308,9 +302,7 @@ async def test_get_table_by_id_default_options( assert any(col.name == "col1" for col in retrieved_table.columns.values()) assert any(col.name == "col2" for col in retrieved_table.columns.values()) - async def test_get_table_by_id_with_table_options( - self, project_model: Project - ) -> None: + def test_get_table_by_id_with_table_options(self, project_model: Project) -> None: """Test retrieving a Table entity with custom TableOptions.""" # GIVEN a table in a project columns = [ @@ -341,7 +333,7 @@ async def test_get_table_by_id_with_table_options( assert retrieved_table.id == stored_table.id assert len(retrieved_table.columns) == 0 - async def test_get_table_by_id_with_activity(self, project_model: Project) -> None: + def test_get_table_by_id_with_activity(self, project_model: Project) -> None: """Test retrieving a Table entity with activity information.""" # GIVEN a table with activity in a project columns = [ @@ -373,7 +365,7 @@ async def test_get_table_by_id_with_activity(self, project_model: Project) -> No assert retrieved_table.activity is not None assert retrieved_table.activity.name == "Test Activity" - async def test_get_dataset_by_id(self, project_model: Project) -> None: + def test_get_dataset_by_id(self, project_model: Project) -> None: """Test retrieving a Dataset entity by Synapse ID.""" # GIVEN a dataset in a project columns = [ @@ -399,7 +391,7 @@ async def test_get_dataset_by_id(self, project_model: Project) -> None: assert retrieved_dataset.name == stored_dataset.name assert len(retrieved_dataset.columns) == 2 - async def test_get_dataset_collection_by_id(self, project_model: Project) -> None: + def test_get_dataset_collection_by_id(self, project_model: Project) -> None: """Test retrieving a DatasetCollection entity by Synapse ID.""" # GIVEN a dataset collection in a project dataset_collection = DatasetCollection( @@ -421,7 +413,7 @@ async def test_get_dataset_collection_by_id(self, project_model: Project) -> Non assert retrieved_collection.id == stored_collection.id assert retrieved_collection.name == stored_collection.name - async def test_get_entity_view_by_id(self, project_model: Project) -> None: + def test_get_entity_view_by_id(self, project_model: Project) -> None: """Test retrieving an EntityView entity by Synapse ID.""" # GIVEN an entity view in a project columns = [ @@ -449,7 +441,7 @@ async def test_get_entity_view_by_id(self, project_model: Project) -> None: assert retrieved_view.name == stored_view.name assert len(retrieved_view.columns) >= 2 # May include default columns - async def test_get_submission_view_by_id(self, project_model: Project) -> None: + def test_get_submission_view_by_id(self, project_model: Project) -> None: """Test retrieving a SubmissionView entity by Synapse ID.""" # GIVEN a submission view in a project columns = [ @@ -475,7 +467,7 @@ async def test_get_submission_view_by_id(self, project_model: Project) -> None: assert retrieved_view.id == stored_view.id assert retrieved_view.name == stored_view.name - async def test_get_materialized_view_by_id(self, project_model: Project) -> None: + def test_get_materialized_view_by_id(self, project_model: Project) -> None: """Test retrieving a MaterializedView entity by Synapse ID.""" # GIVEN a simple table to create materialized view from columns = [ @@ -509,7 +501,7 @@ async def test_get_materialized_view_by_id(self, project_model: Project) -> None assert retrieved_view.name == stored_view.name assert retrieved_view.defining_sql is not None - async def test_get_virtual_table_by_id(self, project_model: Project) -> None: + def test_get_virtual_table_by_id(self, project_model: Project) -> None: """Test retrieving a VirtualTable entity by Synapse ID.""" # GIVEN a simple table to create virtual table from columns = [ @@ -543,9 +535,7 @@ async def test_get_virtual_table_by_id(self, project_model: Project) -> None: assert retrieved_virtual.name == stored_virtual.name assert retrieved_virtual.defining_sql is not None - async def test_get_link_by_id_without_following( - self, project_model: Project - ) -> None: + def test_get_link_by_id_without_following(self, project_model: Project) -> None: """Test retrieving a Link entity by Synapse ID without following the link.""" # GIVEN a file and a link to that file file = self.create_file_instance() @@ -578,9 +568,7 @@ async def test_get_link_by_id_without_following( assert retrieved_link.name == stored_link.name assert retrieved_link.target_id == stored_file.id - async def test_get_link_by_id_default_follows_link( - self, project_model: Project - ) -> None: + def test_get_link_by_id_default_follows_link(self, project_model: Project) -> None: """Test that getting a Link by ID follows the link by default (no LinkOptions provided).""" # GIVEN a file and a link to that file file = self.create_file_instance() @@ -608,7 +596,7 @@ async def test_get_link_by_id_default_follows_link( assert retrieved_entity.id == stored_file.id assert retrieved_entity.name == stored_file.name - async def test_get_link_by_id_with_following(self, project_model: Project) -> None: + def test_get_link_by_id_with_following(self, project_model: Project) -> None: """Test retrieving a Link entity by Synapse ID and following to the target (default behavior).""" # GIVEN a file and a link to that file file = self.create_file_instance() @@ -636,7 +624,7 @@ async def test_get_link_by_id_with_following(self, project_model: Project) -> No assert retrieved_entity.id == stored_file.id assert retrieved_entity.name == stored_file.name - async def test_get_link_by_id_with_following_explicit( + def test_get_link_by_id_with_following_explicit( self, project_model: Project ) -> None: """Test retrieving a Link entity by Synapse ID with explicit follow_link=True.""" @@ -670,9 +658,7 @@ async def test_get_link_by_id_with_following_explicit( assert retrieved_entity.id == stored_file.id assert retrieved_entity.name == stored_file.name - async def test_get_link_by_id_with_file_options( - self, project_model: Project - ) -> None: + def test_get_link_by_id_with_file_options(self, project_model: Project) -> None: """Test retrieving a Link entity that points to a File with custom file options.""" # GIVEN a file and a link to that file file = self.create_file_instance() @@ -715,7 +701,7 @@ async def test_get_link_by_id_with_file_options( ) assert retrieved_entity.download_file is True - async def test_get_with_entity_instance(self, project_model: Project) -> None: + def test_get_with_entity_instance(self, project_model: Project) -> None: """Test get when passing an entity instance directly.""" # GIVEN an existing File entity instance file = self.create_file_instance() @@ -736,7 +722,7 @@ async def test_get_with_entity_instance(self, project_model: Project) -> None: assert refreshed_file.id == stored_file.id assert refreshed_file.download_file is False - async def test_get_combined_options(self, project_model: Project) -> None: + def test_get_combined_options(self, project_model: Project) -> None: """Test get with multiple option types combined.""" # GIVEN a file with activity file = self.create_file_instance() @@ -764,7 +750,7 @@ async def test_get_combined_options(self, project_model: Project) -> None: assert retrieved_file.activity is not None assert retrieved_file.activity.name == "Test Activity" - async def test_get_invalid_synapse_id_raises_error(self) -> None: + def test_get_invalid_synapse_id_raises_error(self) -> None: """Test that get raises appropriate error for invalid Synapse ID.""" # GIVEN an invalid synapse ID invalid_id = "syn999999999999" @@ -774,9 +760,7 @@ async def test_get_invalid_synapse_id_raises_error(self) -> None: with pytest.raises(Exception): # Could be SynapseNotFoundError or similar get(synapse_id=invalid_id, synapse_client=self.syn) - async def test_get_invalid_entity_name_raises_error( - self, project_model: Project - ) -> None: + def test_get_invalid_entity_name_raises_error(self, project_model: Project) -> None: """Test that get raises appropriate error for invalid entity name.""" # GIVEN an invalid entity name invalid_name = f"nonexistent_entity_{str(uuid.uuid4())}" @@ -790,7 +774,7 @@ async def test_get_invalid_entity_name_raises_error( synapse_client=self.syn, ) - async def test_get_validation_errors(self) -> None: + def test_get_validation_errors(self) -> None: """Test validation errors for invalid parameter combinations.""" # WHEN I provide both synapse_id and entity_name # THEN ValueError is raised diff --git a/tests/integration/synapseclient/test_command_line_client.py b/tests/integration/synapseclient/test_command_line_client.py index 86b097924..aa3ca019e 100644 --- a/tests/integration/synapseclient/test_command_line_client.py +++ b/tests/integration/synapseclient/test_command_line_client.py @@ -1,5 +1,4 @@ """Integration tests for the CLI.""" -import asyncio import filecmp import json import logging @@ -8,12 +7,12 @@ import shutil import sys import tempfile +import time import uuid from io import StringIO from unittest.mock import patch import pytest -import pytest_asyncio import synapseclient.__main__ as cmdline import synapseclient.core.utils as utils @@ -32,8 +31,8 @@ ) -@pytest_asyncio.fixture(loop_scope="function", scope="function") -async def test_state(syn: Synapse, project: Project, schedule_for_cleanup): +@pytest.fixture(scope="function") +def test_state(syn: Synapse, project: Project, schedule_for_cleanup): class State: def __init__(self): self.syn = syn @@ -89,7 +88,7 @@ def parse(regex, output): raise Exception('ERROR parsing output: "' + str(output) + '"') -async def test_command_line_client(test_state): +def test_command_line_client(test_state): print("TESTING CMD LINE CLIENT") # Create a Project output = run( @@ -271,7 +270,7 @@ async def test_command_line_client(test_state): run(test_state, "synapse" "--skip-checks", "delete", project_id) -async def test_command_line_client_annotations(test_state): +def test_command_line_client_annotations(test_state): # Create a Project output = run( test_state, @@ -429,7 +428,7 @@ async def test_command_line_client_annotations(test_state): assert annotations["foo"] == [456] -async def test_command_line_store_and_submit(test_state): +def test_command_line_store_and_submit(test_state): # Create a Project output = run( test_state, @@ -582,7 +581,7 @@ async def test_command_line_store_and_submit(test_state): run(test_state, "synapse" "--skip-checks", "delete", project_id) -async def test_command_get_recursive_and_query(test_state): +def test_command_get_recursive_and_query(test_state): """Tests the 'synapse get -r' and 'synapse get -q' functions""" project_entity = test_state.project @@ -619,7 +618,7 @@ async def test_command_get_recursive_and_query(test_state): # get -r uses syncFromSynapse() which uses getChildren(), which is not immediately consistent, # but faster than chunked queries. - await asyncio.sleep(2) + time.sleep(2) # Test recursive get run(test_state, "synapse" "--skip-checks", "get", "-r", folder_entity.id) # Verify that we downloaded files: @@ -651,7 +650,7 @@ async def test_command_get_recursive_and_query(test_state): test_state.syn.store(RowSet(schema=schema1, rows=[Row(r) for r in data1])) - await asyncio.sleep(3) # get -q are eventually consistent + time.sleep(3) # get -q are eventually consistent # Test Table/View query get run( test_state, @@ -672,7 +671,7 @@ async def test_command_get_recursive_and_query(test_state): test_state.schedule_for_cleanup(new_paths[0]) -async def test_command_copy(test_state): +def test_command_copy(test_state): """Tests the 'synapse cp' function""" # Create a Project @@ -771,7 +770,7 @@ async def test_command_copy(test_state): ) -async def test_command_line_using_paths(test_state): +def test_command_line_using_paths(test_state): # Create a Project project_entity = test_state.syn.store(Project(name=str(uuid.uuid4()))) test_state.schedule_for_cleanup(project_entity.id) @@ -876,7 +875,7 @@ async def test_command_line_using_paths(test_state): run(test_state, "synapse" "--skip-checks", "show", filename) -async def test_table_query(test_state): +def test_table_query(test_state): """Test command line ability to do table query.""" cols = [ @@ -929,7 +928,7 @@ async def test_table_query(test_state): ) -async def test_login(test_state): +def test_login(test_state): alt_syn = Synapse(cache_client=False) username = "username" auth_token = "my_auth_token" @@ -954,7 +953,7 @@ async def test_login(test_state): mock_get_user_profile.assert_called_once_with() -async def test_configPath(test_state): +def test_configPath(test_state): """Test using a user-specified configPath for Synapse configuration file.""" tmp_config_file = tempfile.NamedTemporaryFile(suffix=".synapseConfig", delete=False) @@ -1002,7 +1001,7 @@ def _create_temp_file_with_cleanup(schedule_for_cleanup, specific_file_text=None return filename -async def test_create__with_description(test_state): +def test_create__with_description(test_state): output = run( test_state, "synapse", @@ -1018,7 +1017,7 @@ async def test_create__with_description(test_state): _description_wiki_check(test_state.syn, output, test_state.description_text) -async def test_store__with_description(test_state): +def test_store__with_description(test_state): output = run( test_state, "synapse", @@ -1034,7 +1033,7 @@ async def test_store__with_description(test_state): _description_wiki_check(test_state.syn, output, test_state.description_text) -async def test_add__with_description(test_state): +def test_add__with_description(test_state): output = run( test_state, "synapse", @@ -1050,7 +1049,7 @@ async def test_add__with_description(test_state): _description_wiki_check(test_state.syn, output, test_state.description_text) -async def test_create__with_descriptionFile(test_state): +def test_create__with_descriptionFile(test_state): output = run( test_state, "synapse", @@ -1066,7 +1065,7 @@ async def test_create__with_descriptionFile(test_state): _description_wiki_check(test_state.syn, output, test_state.description_text) -async def test_store__with_descriptionFile(test_state): +def test_store__with_descriptionFile(test_state): output = run( test_state, "synapse", @@ -1082,7 +1081,7 @@ async def test_store__with_descriptionFile(test_state): _description_wiki_check(test_state.syn, output, test_state.description_text) -async def test_add__with_descriptionFile(test_state): +def test_add__with_descriptionFile(test_state): output = run( test_state, "synapse", @@ -1098,7 +1097,7 @@ async def test_add__with_descriptionFile(test_state): _description_wiki_check(test_state.syn, output, test_state.description_text) -async def test_create__update_description(test_state): +def test_create__update_description(test_state): name = str(uuid.uuid4()) output = run( test_state, @@ -1128,7 +1127,7 @@ async def test_create__update_description(test_state): _description_wiki_check(test_state.syn, output, test_state.update_description_text) -async def test_store__update_description(test_state): +def test_store__update_description(test_state): name = str(uuid.uuid4()) output = run( test_state, @@ -1158,7 +1157,7 @@ async def test_store__update_description(test_state): _description_wiki_check(test_state.syn, output, test_state.update_description_text) -async def test_add__update_description(test_state): +def test_add__update_description(test_state): name = str(uuid.uuid4()) output = run( test_state, @@ -1188,7 +1187,7 @@ async def test_add__update_description(test_state): _description_wiki_check(test_state.syn, output, test_state.update_description_text) -async def test_create__same_project_name(test_state): +def test_create__same_project_name(test_state): """Test creating project that already exists returns the existing project.""" name = str(uuid.uuid4()) @@ -1206,7 +1205,7 @@ async def test_create__same_project_name(test_state): @patch.object(utils.sys.stdin, "isatty") -async def test_storeTable_csv(mock_sys, test_state): +def test_storeTable_csv(mock_sys, test_state): # when running on windows os with multiple CPU, the sys.stdin.isatty will return True # Thus we mock the utils.sys.stdin. mock_sys.return_value = False diff --git a/tests/integration/synapseclient/test_evaluations.py b/tests/integration/synapseclient/test_evaluations.py index 0d4c55917..931e25771 100644 --- a/tests/integration/synapseclient/test_evaluations.py +++ b/tests/integration/synapseclient/test_evaluations.py @@ -3,16 +3,17 @@ import tempfile import time import uuid -from unittest import skip import pytest from synapseclient import Evaluation, File, Project, SubmissionViewSchema, Synapse, Team from synapseclient.core.exceptions import SynapseHTTPError +# from unittest import skip -@skip("Skip integration tests for soon to be removed code") -async def test_evaluations(syn: Synapse, project: Project): + +# @skip("Skip integration tests for soon to be removed code") +def test_evaluations(syn: Synapse, project: Project): # Create an Evaluation name = "Test Evaluation %s" % str(uuid.uuid4()) ev = Evaluation( @@ -173,8 +174,8 @@ async def test_evaluations(syn: Synapse, project: Project): pytest.raises(SynapseHTTPError, syn.getEvaluation, ev) -@skip("Skip integration tests for soon to be removed code") -async def test_teams(syn: Synapse, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_teams(syn: Synapse, schedule_for_cleanup): name = "My Uniquely Named Team " + str(uuid.uuid4()) team = syn.store(Team(name=name, description="A fake team for testing...")) schedule_for_cleanup(team) diff --git a/tests/integration/synapseclient/test_json_schema_services.py b/tests/integration/synapseclient/test_json_schema_services.py index 229e31507..dcad56af1 100644 --- a/tests/integration/synapseclient/test_json_schema_services.py +++ b/tests/integration/synapseclient/test_json_schema_services.py @@ -1,16 +1,17 @@ import uuid from random import randint from time import sleep -from unittest import skip import pytest import synapseclient from synapseclient.core.exceptions import SynapseHTTPError +# from unittest import skip -@skip("Skip integration tests for soon to be removed code") -async def test_available_services(syn): + +# @skip("Skip integration tests for soon to be removed code") +def test_available_services(syn): services = syn.get_available_services() # Output: ['json_schema'] available_services = ["json_schema"] assert set(services) == set(available_services) @@ -21,8 +22,8 @@ def js(syn: synapseclient.Synapse): return syn.service("json_schema") -@skip("Skip integration tests for soon to be removed code") -async def test_json_schema_organization(js): +# @skip("Skip integration tests for soon to be removed code") +def test_json_schema_organization(js): # Schema organizations must start with a string js_org = "a" + str(uuid.uuid4()).replace("-", "") # Create, manage, and delete a JSON Schema organization @@ -68,7 +69,7 @@ def setup(self, js): self.rint = randint(0, 100000) self.simple_schema = { "$id": "https://example.com/person.schema.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "Person", "type": "object", "properties": { @@ -91,8 +92,8 @@ def setup(self, js): def teardown_method(self): self.my_org.delete() - @skip("Skip integration tests for soon to be removed code") - async def test_json_schema_schemas_org_create_schema(self): + # @skip("Skip integration tests for soon to be removed code") + def test_json_schema_schemas_org_create_schema(self): # Create json schema new_version = self.my_org.create_json_schema( self.simple_schema, self.schema_name, f"0.{self.rint}.1" @@ -110,8 +111,8 @@ async def test_json_schema_schemas_org_create_schema(self): assert full_body["properties"] == self.simple_schema["properties"] new_version.delete() - @skip("Skip integration tests for soon to be removed code") - async def test_json_schema_schemas_js_create_schema(self, js): + # @skip("Skip integration tests for soon to be removed code") + def test_json_schema_schemas_js_create_schema(self, js): # Create json schema # Version 2 of creating json schema my_schema = js.JsonSchema(self.my_org, self.schema_name) @@ -122,8 +123,8 @@ async def test_json_schema_schemas_js_create_schema(self, js): assert schema1 is schema2 new_version.delete() - @skip("Skip integration tests for soon to be removed code") - async def test_json_schema_schemas_js_version_create_schema(self, js): + # @skip("Skip integration tests for soon to be removed code") + def test_json_schema_schemas_js_version_create_schema(self, js): # Create json schema # Version 3 of creating json schema my_version = js.JsonSchemaVersion( @@ -136,8 +137,8 @@ async def test_json_schema_schemas_js_version_create_schema(self, js): assert schema1 is schema2 new_version.delete() - @skip("Skip integration tests for soon to be removed code") - async def test_json_schema_validate(self, js, syn, schedule_for_cleanup): + # @skip("Skip integration tests for soon to be removed code") + def test_json_schema_validate(self, js, syn, schedule_for_cleanup): project_name = str(uuid.uuid4()).replace("-", "") project = synapseclient.Project(name=project_name) project.firstName = "test" diff --git a/tests/integration/synapseclient/test_tables.py b/tests/integration/synapseclient/test_tables.py index 73108da93..e50021afa 100644 --- a/tests/integration/synapseclient/test_tables.py +++ b/tests/integration/synapseclient/test_tables.py @@ -7,7 +7,6 @@ import uuid from datetime import datetime, timezone from typing import Callable -from unittest import skip import numpy as np import pandas as pd @@ -34,6 +33,8 @@ ) from tests.integration import QUERY_TIMEOUT_SEC +# from unittest import skip + @pytest.fixture(scope="module", autouse=True) def _init_query_timeout(request, syn): @@ -46,8 +47,8 @@ def revert_timeout(): request.addfinalizer(revert_timeout) -@skip("Skip integration tests for soon to be removed code") -async def test_create_and_update_file_view( +# @skip("Skip integration tests for soon to be removed code") +def test_create_and_update_file_view( syn: Synapse, project: Project, schedule_for_cleanup ): # Create a folder @@ -166,8 +167,8 @@ async def test_create_and_update_file_view( assert new_view_dict[0]["fileFormat"] == "PNG" -@skip("Skip integration tests for soon to be removed code") -async def test_entity_view_add_annotation_columns(syn, project, schedule_for_cleanup): +# @skip("Skip integration tests for soon to be removed code") +def test_entity_view_add_annotation_columns(syn, project, schedule_for_cleanup): folder1 = syn.store( Folder( name=str(uuid.uuid4()) + "test_entity_view_add_annotation_columns_proj1", @@ -221,8 +222,8 @@ async def test_entity_view_add_annotation_columns(syn, project, schedule_for_cle syn.store(entity_view) -@skip("Skip integration tests for soon to be removed code") -async def test_rowset_tables(syn, project): +# @skip("Skip integration tests for soon to be removed code") +def test_rowset_tables(syn, project): cols = [ Column(name="name", columnType="STRING", maximumSize=1000), Column(name="foo", columnType="STRING", enumValues=["foo", "bar", "bat"]), @@ -246,8 +247,8 @@ async def test_rowset_tables(syn, project): assert len(row_reference_set1["rows"]) == 4 -@skip("Skip integration tests for soon to be removed code") -async def test_materialized_view(syn, project): +# @skip("Skip integration tests for soon to be removed code") +def test_materialized_view(syn, project): """Test creation of materialized view""" # Define schema cols = [ @@ -312,8 +313,8 @@ async def test_materialized_view(syn, project): ) -@skip("Skip integration tests for soon to be removed code") -async def test_dataset(syn, project): +# @skip("Skip integration tests for soon to be removed code") +def test_dataset(syn, project): cols = [ Column(name="id", columnType="ENTITYID"), Column(name="name", columnType="STRING"), @@ -332,8 +333,8 @@ async def test_dataset(syn, project): assert all(dataset_df.columns == ["id", "name"]) -@skip("Skip integration tests for soon to be removed code") -async def test_tables_csv(syn, project): +# @skip("Skip integration tests for soon to be removed code") +def test_tables_csv(syn, project): # Define schema cols = [ Column(name="Name", columnType="STRING"), @@ -370,8 +371,8 @@ async def test_tables_csv(syn, project): assert expected_row == row, "expected %s but got %s" % (expected_row, row) -@skip("Skip integration tests for soon to be removed code") -async def test_tables_pandas(syn, project): +# @skip("Skip integration tests for soon to be removed code") +def test_tables_pandas(syn, project): # create a pandas DataFrame df = pd.DataFrame( { @@ -512,8 +513,8 @@ def dontruntest_big_csvs(syn, project, schedule_for_cleanup): CsvFileTable.from_table_query(syn, "select * from %s" % schema1.id) -@skip("Skip integration tests for soon to be removed code") -async def test_synapse_integer_columns_with_missing_values_from_dataframe( +# @skip("Skip integration tests for soon to be removed code") +def test_synapse_integer_columns_with_missing_values_from_dataframe( syn, project, schedule_for_cleanup ): # SYNPY-267 @@ -552,8 +553,8 @@ async def test_synapse_integer_columns_with_missing_values_from_dataframe( assert_frame_equal(df, df2) -@skip("Skip integration tests for soon to be removed code") -async def test_store_table_datetime(syn, project): +# @skip("Skip integration tests for soon to be removed code") +def test_store_table_datetime(syn, project): current_datetime = datetime.fromtimestamp(round(time.time(), 3)).replace( tzinfo=timezone.utc ) @@ -637,8 +638,8 @@ def __init__(self): class TestPartialRowSet: """Testing for Partial Rows.""" - @skip("Skip integration tests for soon to be removed code") - async def test_partial_row_view_csv_query_table( + # @skip("Skip integration tests for soon to be removed code") + def test_partial_row_view_csv_query_table( self, syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: """ @@ -647,7 +648,7 @@ async def test_partial_row_view_csv_query_table( test_state = partial_rowset_test_state( syn=syn, project=project, schedule_for_cleanup=schedule_for_cleanup ) - await self._test_method( + self._test_method( syn, test_state.table_schema, "csv", @@ -655,8 +656,8 @@ async def test_partial_row_view_csv_query_table( test_state.expected_table_cells, ) - @skip("Skip integration tests for soon to be removed code") - async def test_partial_row_view_csv_query_entity_view( + # @skip("Skip integration tests for soon to be removed code") + def test_partial_row_view_csv_query_entity_view( self, syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: """ @@ -665,7 +666,7 @@ async def test_partial_row_view_csv_query_entity_view( test_state = partial_rowset_test_state( syn=syn, project=project, schedule_for_cleanup=schedule_for_cleanup ) - await self._test_method( + self._test_method( syn, test_state.view_schema, "csv", @@ -673,8 +674,8 @@ async def test_partial_row_view_csv_query_entity_view( test_state.expected_view_cells, ) - @skip("Skip integration tests for soon to be removed code") - async def test_parital_row_rowset_query_table( + # @skip("Skip integration tests for soon to be removed code") + def test_parital_row_rowset_query_table( self, syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: """ @@ -683,7 +684,7 @@ async def test_parital_row_rowset_query_table( test_state = partial_rowset_test_state( syn=syn, project=project, schedule_for_cleanup=schedule_for_cleanup ) - await self._test_method( + self._test_method( syn, test_state.table_schema, "rowset", @@ -691,8 +692,8 @@ async def test_parital_row_rowset_query_table( test_state.expected_table_cells, ) - @skip("Skip integration tests for soon to be removed code") - async def test_parital_row_rowset_query_entity_view( + # @skip("Skip integration tests for soon to be removed code") + def test_parital_row_rowset_query_entity_view( self, syn: Synapse, project: Project, schedule_for_cleanup: Callable[..., None] ) -> None: """ @@ -701,7 +702,7 @@ async def test_parital_row_rowset_query_entity_view( test_state = partial_rowset_test_state( syn=syn, project=project, schedule_for_cleanup=schedule_for_cleanup ) - await self._test_method( + self._test_method( syn, test_state.view_schema, "rowset", @@ -709,9 +710,7 @@ async def test_parital_row_rowset_query_entity_view( test_state.expected_view_cells, ) - async def _test_method( - self, syn, schema, resultsAs, partial_changes, expected_results - ): + def _test_method(self, syn, schema, resultsAs, partial_changes, expected_results): query_results = self._query_with_retry( syn, "SELECT * FROM %s" % utils.id_of(schema), diff --git a/tests/integration/synapseclient/test_wikis.py b/tests/integration/synapseclient/test_wikis.py index d6d57de28..2575b6590 100644 --- a/tests/integration/synapseclient/test_wikis.py +++ b/tests/integration/synapseclient/test_wikis.py @@ -1,6 +1,5 @@ import os import uuid -from unittest import skip import pytest @@ -9,11 +8,11 @@ from synapseclient.core.exceptions import SynapseHTTPError from synapseclient.core.upload.upload_functions import upload_synapse_s3 +# from unittest import skip -@skip("Skip integration tests for soon to be removed code") -async def test_wikiAttachment( - syn: Synapse, project: Project, schedule_for_cleanup -) -> None: + +# @skip("Skip integration tests for soon to be removed code") +def test_wikiAttachment(syn: Synapse, project: Project, schedule_for_cleanup) -> None: # Upload a file to be attached to a Wiki filename = utils.make_bogus_data_file() attachname = utils.make_bogus_data_file() @@ -83,8 +82,8 @@ async def test_wikiAttachment( pytest.raises(SynapseHTTPError, syn.getWiki, project) -@skip("Skip integration tests for soon to be removed code") -async def test_create_or_update_wiki(syn: Synapse, project: Project) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_create_or_update_wiki(syn: Synapse, project: Project) -> None: # create wiki once syn.store( Wiki( @@ -107,8 +106,8 @@ async def test_create_or_update_wiki(syn: Synapse, project: Project) -> None: assert new_title == syn.getWiki(wiki.ownerId)["title"] -@skip("Skip integration tests for soon to be removed code") -async def test_wiki_version(syn: Synapse, project: Project) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_wiki_version(syn: Synapse, project: Project) -> None: # create a new project to avoid artifacts from previous tests project = syn.store(Project(name=str(uuid.uuid4()))) wiki = syn.store( @@ -133,10 +132,8 @@ async def test_wiki_version(syn: Synapse, project: Project) -> None: assert "version 2" in w2.markdown -@skip("Skip integration tests for soon to be removed code") -async def test_wiki_with_empty_string_parent_wiki_id( - syn: Synapse, project: Project -) -> None: +# @skip("Skip integration tests for soon to be removed code") +def test_wiki_with_empty_string_parent_wiki_id(syn: Synapse, project: Project) -> None: # GIVEN a wiki is created with an empty string parentWikiId # WHEN it is stored wiki_stored = syn.store( diff --git a/tests/integration/synapseutils/test_synapseutils_copy.py b/tests/integration/synapseutils/test_synapseutils_copy.py index ebe8673e9..702a540f4 100644 --- a/tests/integration/synapseutils/test_synapseutils_copy.py +++ b/tests/integration/synapseutils/test_synapseutils_copy.py @@ -28,7 +28,7 @@ # Add Test for UPDATE # Add test for existing provenance but the orig doesn't have provenance -async def test_copy(syn: Synapse, schedule_for_cleanup): +def test_copy(syn: Synapse, schedule_for_cleanup): try: execute_test_copy(syn, schedule_for_cleanup) except FunctionTimedOut: @@ -288,8 +288,8 @@ def execute_test_copy(syn: Synapse, schedule_for_cleanup): class TestCopyWiki: - @pytest_asyncio.fixture(autouse=True, loop_scope="function", scope="function") - async def init(self, syn, schedule_for_cleanup): + @pytest.fixture(autouse=True, scope="function") + def init(self, syn, schedule_for_cleanup): self.syn = syn self.schedule_for_cleanup = schedule_for_cleanup @@ -360,11 +360,11 @@ async def init(self, syn, schedule_for_cleanup): self.first_headers = self.syn.getWikiHeaders(self.project_entity) - async def test_copy_Wiki(self): + def test_copy_Wiki(self): second_headers = synapseutils.copyWiki( - self.syn, - self.project_entity.id, - self.second_project.id, + syn=self.syn, + entity=self.project_entity.id, + destinationId=self.second_project.id, entityMap=self.fileMapping, ) @@ -406,12 +406,12 @@ async def test_copy_Wiki(self): # check that attachment file names are the same assert orig_file == new_file - async def test_entitySubPageId_and_destinationSubPageId(self): + def test_entitySubPageId_and_destinationSubPageId(self): # Test: entitySubPageId second_header = synapseutils.copyWiki( - self.syn, - self.project_entity.id, - self.second_project.id, + syn=self.syn, + entity=self.project_entity.id, + destinationId=self.second_project.id, entitySubPageId=self.sub_subwiki.id, destinationSubPageId=None, updateLinks=False, @@ -450,7 +450,7 @@ async def test_entitySubPageId_and_destinationSubPageId(self): class TestCopyFileHandles: @pytest_asyncio.fixture(autouse=True, loop_scope="function", scope="function") - async def init(self, syn, schedule_for_cleanup): + def init(self, syn, schedule_for_cleanup): self.syn = syn # create external file handles for https://www.synapse.org/images/logo.svg, @@ -475,7 +475,7 @@ async def init(self, syn, schedule_for_cleanup): test_entity_1 = self.syn.store(test_entity_1) self.obj_id_1 = str(test_entity_1["id"][3:]) - async def test_copy_file_handles(self): + def test_copy_file_handles(self): # define inputs file_handles = [self.file_handle_id_1] associate_object_types = ["FileEntity"] diff --git a/tests/integration/synapseutils/test_synapseutils_migrate.py b/tests/integration/synapseutils/test_synapseutils_migrate.py index cccd38bd4..4caa6d0b6 100644 --- a/tests/integration/synapseutils/test_synapseutils_migrate.py +++ b/tests/integration/synapseutils/test_synapseutils_migrate.py @@ -54,7 +54,7 @@ def _assert_storage_location(file_handles, storage_location_id): assert fh["storageLocationId"] == storage_location_id -async def test_migrate_project( +def test_migrate_project( request, syn: Synapse, schedule_for_cleanup, storage_location_id ): test_name = request.node.name diff --git a/tests/integration/synapseutils/test_synapseutils_sync.py b/tests/integration/synapseutils/test_synapseutils_sync.py index da6d8aaf2..3469ce151 100644 --- a/tests/integration/synapseutils/test_synapseutils_sync.py +++ b/tests/integration/synapseutils/test_synapseutils_sync.py @@ -5,11 +5,9 @@ import tempfile import uuid from typing import Callable -from unittest import skip import pandas as pd import pytest -import pytest_asyncio import synapseclient.core.utils as utils import synapseutils @@ -22,6 +20,9 @@ from synapseclient.core.exceptions import SynapseHTTPError from synapseclient.models import File, Folder, Project +# from unittest import skip + + BOGUS_ACTIVITY = "bogus_activity" BOGUS_DESCRIPTION = "bogus_description" SYNAPSE_URL = "https://www.synapse.org" @@ -78,8 +79,8 @@ MODIFIED_ON = "modifiedOn" -@pytest_asyncio.fixture(loop_scope="function", scope="function", autouse=True) -async def test_state(syn: Synapse, schedule_for_cleanup: Callable[..., None]): +@pytest.fixture(scope="function", autouse=True) +def test_state(syn: Synapse, schedule_for_cleanup: Callable[..., None]): class TestState: def __init__(self): self.syn = syn @@ -132,8 +133,8 @@ def _makeManifest(content, schedule_for_cleanup: Callable[..., None]): return filepath -@skip("Skip integration tests for soon to be removed code") -async def test_readManifest(test_state): +# @skip("Skip integration tests for soon to be removed code") +def test_readManifest(test_state): """Creates multiple manifests and verifies that they validate correctly""" # Test manifest with missing columns manifest = _makeManifest( @@ -175,17 +176,17 @@ async def test_readManifest(test_state): class TestSyncToSynapse: """Testing the .syncToSynapse() function""" - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_file_only( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_file_only( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], project_model: Project, ) -> None: # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 2 temporary files on disk: @@ -211,24 +212,24 @@ async def test_sync_to_synapse_file_only( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 2 # AND each of the files are the ones we uploaded for file in folder.files: assert file.path in temp_files - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_files_with_annotations( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_files_with_annotations( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], project_model: Project, ) -> None: # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 2 temporary files on disk: @@ -273,7 +274,7 @@ async def test_sync_to_synapse_files_with_annotations( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 2 # AND each of the files are the ones we uploaded @@ -319,17 +320,17 @@ async def test_sync_to_synapse_files_with_annotations( assert file.annotations["foo"] == ["baz"] assert len(file.annotations) == 1 - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_with_activities( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_with_activities( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], project_model: Project, ) -> None: # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 2 temporary files on disk: @@ -355,7 +356,7 @@ async def test_sync_to_synapse_with_activities( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 2 # AND each of the files are the ones we uploaded @@ -368,8 +369,8 @@ async def test_sync_to_synapse_with_activities( assert file.activity.name == BOGUS_ACTIVITY assert file.activity.description == BOGUS_DESCRIPTION - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_activities_pointing_to_files( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_activities_pointing_to_files( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -383,9 +384,9 @@ async def test_sync_to_synapse_activities_pointing_to_files( file1 <- file2 <- file3 """ # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 3 temporary files on disk: @@ -416,7 +417,7 @@ async def test_sync_to_synapse_activities_pointing_to_files( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND each of the files are the ones we uploaded @@ -435,17 +436,17 @@ async def test_sync_to_synapse_activities_pointing_to_files( assert len(file.activity.executed) == 1 assert file.activity.executed[0].target_id in file_ids - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_activities_added_then_removed_from_manifest( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_activities_added_then_removed_from_manifest( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], project_model: Project, ) -> None: # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND temporary file on disk: @@ -470,7 +471,7 @@ async def test_sync_to_synapse_activities_added_then_removed_from_manifest( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -499,7 +500,7 @@ async def test_sync_to_synapse_activities_added_then_removed_from_manifest( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -512,17 +513,17 @@ async def test_sync_to_synapse_activities_added_then_removed_from_manifest( assert len(folder.files[0].activity.used) == 1 assert folder.files[0].activity.used[0].url == SYNAPSE_URL - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_activities_added_then_removed_from_manifest_but_copied_to_new_version( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_activities_added_then_removed_from_manifest_but_copied_to_new_version( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], project_model: Project, ) -> None: # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND temporary file on disk: @@ -547,7 +548,7 @@ async def test_sync_to_synapse_activities_added_then_removed_from_manifest_but_c synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -586,7 +587,7 @@ async def test_sync_to_synapse_activities_added_then_removed_from_manifest_but_c ) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -599,17 +600,17 @@ async def test_sync_to_synapse_activities_added_then_removed_from_manifest_but_c assert len(folder.files[0].activity.used) == 1 assert folder.files[0].activity.used[0].url == SYNAPSE_URL - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_field_not_available_in_manifest_persisted( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_field_not_available_in_manifest_persisted( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], project_model: Project, ) -> None: # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND temporary file on disk: @@ -634,7 +635,7 @@ async def test_sync_to_synapse_field_not_available_in_manifest_persisted( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -647,7 +648,7 @@ async def test_sync_to_synapse_field_not_available_in_manifest_persisted( # WHEN I update a metadata field on the File not available in the manifest folder.files[0].description = "new file description" - await folder.files[0].store_async(synapse_client=syn) + folder.files[0].store(synapse_client=syn) assert folder.files[0].version_number == 2 # WHEN I update the manifest file to remove the activities @@ -668,7 +669,7 @@ async def test_sync_to_synapse_field_not_available_in_manifest_persisted( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -679,17 +680,17 @@ async def test_sync_to_synapse_field_not_available_in_manifest_persisted( # AND The metadata field updated is still present assert folder.files[0].description == "new file description" - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_activities_added_then_removed_with_version_updates( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_activities_added_then_removed_with_version_updates( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], project_model: Project, ) -> None: # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND temporary file on disk: @@ -714,7 +715,7 @@ async def test_sync_to_synapse_activities_added_then_removed_with_version_update synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -744,7 +745,7 @@ async def test_sync_to_synapse_activities_added_then_removed_with_version_update synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 1 # AND the file is the one we uploaded @@ -754,9 +755,9 @@ async def test_sync_to_synapse_activities_added_then_removed_with_version_update assert folder.files[0].activity is None # AND the first version of the file still has the activity - first_file_version = await File( - id=folder.files[0].id, version_number=1 - ).get_async(include_activity=True, synapse_client=syn) + first_file_version = File(id=folder.files[0].id, version_number=1).get( + include_activity=True, synapse_client=syn + ) assert first_file_version is not None assert first_file_version.activity is not None assert first_file_version.activity.name == BOGUS_ACTIVITY @@ -764,8 +765,8 @@ async def test_sync_to_synapse_activities_added_then_removed_with_version_update assert len(first_file_version.activity.used) == 1 assert first_file_version.activity.used[0].url == SYNAPSE_URL - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_annotations_added_then_removed( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_annotations_added_then_removed( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -778,9 +779,9 @@ async def test_sync_to_synapse_annotations_added_then_removed( be persisted on the files. """ # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 3 temporary files on disk: @@ -808,7 +809,7 @@ async def test_sync_to_synapse_annotations_added_then_removed( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND each of the files are the ones we uploaded @@ -836,7 +837,7 @@ async def test_sync_to_synapse_annotations_added_then_removed( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND each of the files are the ones we uploaded @@ -848,8 +849,8 @@ async def test_sync_to_synapse_annotations_added_then_removed( assert len(file.annotations.keys()) == 1 assert list(file.annotations.values())[0][0] in annotations - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_annotations_added_then_removed_with_no_annotation_merge( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_annotations_added_then_removed_with_no_annotation_merge( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -862,9 +863,9 @@ async def test_sync_to_synapse_annotations_added_then_removed_with_no_annotation be removed from the files. """ # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 3 temporary files on disk: @@ -897,7 +898,7 @@ async def test_sync_to_synapse_annotations_added_then_removed_with_no_annotation ) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND each of the files are the ones we uploaded @@ -931,7 +932,7 @@ async def test_sync_to_synapse_annotations_added_then_removed_with_no_annotation ) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND each of the files are the ones we uploaded @@ -942,8 +943,8 @@ async def test_sync_to_synapse_annotations_added_then_removed_with_no_annotation # AND none of the files have annotations assert len(file.annotations.keys()) == 0 - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_activities_pointing_to_files_and_urls( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_activities_pointing_to_files_and_urls( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -957,9 +958,9 @@ async def test_sync_to_synapse_activities_pointing_to_files_and_urls( file1 <- file2 <- file3 <- file4 <- file5 """ # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 3 temporary files on disk: @@ -997,7 +998,7 @@ async def test_sync_to_synapse_activities_pointing_to_files_and_urls( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND each of the files are the ones we uploaded @@ -1021,8 +1022,8 @@ async def test_sync_to_synapse_activities_pointing_to_files_and_urls( assert file.activity.executed[0].url == SUB_SYNAPSE_URL assert file.activity.executed[1].target_id in file_ids - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_all_activities_pointing_to_single_file( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_all_activities_pointing_to_single_file( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1034,9 +1035,9 @@ async def test_sync_to_synapse_all_activities_pointing_to_single_file( file1 <- file3 """ # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 3 temporary files on disk: @@ -1067,7 +1068,7 @@ async def test_sync_to_synapse_all_activities_pointing_to_single_file( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND the root file of the saving process is present @@ -1092,8 +1093,8 @@ async def test_sync_to_synapse_all_activities_pointing_to_single_file( assert len(file.activity.executed) == 1 assert file.activity.executed[0].target_id in file_ids - @skip("Skip integration tests for soon to be removed code") - async def test_sync_to_synapse_single_file_pointing_to_all_other_files( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_to_synapse_single_file_pointing_to_all_other_files( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1105,9 +1106,9 @@ async def test_sync_to_synapse_single_file_pointing_to_all_other_files( file1 -> file3 """ # GIVEN a folder to sync to - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 3 temporary files on disk: @@ -1136,7 +1137,7 @@ async def test_sync_to_synapse_single_file_pointing_to_all_other_files( synapseutils.syncToSynapse(syn, file_name, sendMessages=SEND_MESSAGE, retries=2) # THEN I expect that the folder has all of the files - await folder.sync_from_synapse_async(download_file=False) + folder.sync_from_synapse(download_file=False, synapse_client=syn) assert len(folder.files) == 3 # AND the root file of the saving process is present @@ -1164,8 +1165,8 @@ async def test_sync_to_synapse_single_file_pointing_to_all_other_files( assert file.activity is None -@skip("Skip integration tests for soon to be removed code") -async def test_write_manifest_data_unicode_characters_in_rows(test_state): +# @skip("Skip integration tests for soon to be removed code") +def test_write_manifest_data_unicode_characters_in_rows(test_state): # SYNPY-693 named_temp_file = tempfile.NamedTemporaryFile("w") @@ -1189,8 +1190,8 @@ async def test_write_manifest_data_unicode_characters_in_rows(test_state): class TestSyncFromSynapse: """Testing the .syncFromSynapse() method""" - @skip("Skip integration tests for soon to be removed code") - async def test_folder_sync_from_synapse_files_only( + # @skip("Skip integration tests for soon to be removed code") + def test_folder_sync_from_synapse_files_only( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1205,9 +1206,9 @@ async def test_folder_sync_from_synapse_files_only( ├── file2 (uploaded) """ # GIVEN a folder to sync from - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 2 temporary files on disk: @@ -1269,8 +1270,8 @@ async def test_folder_sync_from_synapse_files_only( assert pd.isna(matching_row[ACTIVITY_NAME_COLUMN].values[0]) assert pd.isna(matching_row[ACTIVITY_DESCRIPTION_COLUMN].values[0]) - @skip("Skip integration tests for soon to be removed code") - async def test_folder_sync_from_synapse_files_with_annotations( + # @skip("Skip integration tests for soon to be removed code") + def test_folder_sync_from_synapse_files_with_annotations( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1285,9 +1286,9 @@ async def test_folder_sync_from_synapse_files_with_annotations( ├── file2 (uploaded) """ # GIVEN a folder to sync from - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 2 temporary files on disk: @@ -1380,8 +1381,8 @@ async def test_folder_sync_from_synapse_files_with_annotations( ) assert matching_row[BOOL_ANNO].values[0] == BOOL_ANNO_VALUE_IN_MANIFEST - @skip("Skip integration tests for soon to be removed code") - async def test_folder_sync_from_synapse_files_with_activity( + # @skip("Skip integration tests for soon to be removed code") + def test_folder_sync_from_synapse_files_with_activity( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1396,9 +1397,9 @@ async def test_folder_sync_from_synapse_files_with_activity( ├── file2 (uploaded) """ # GIVEN a folder to sync from - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 2 temporary files on disk: @@ -1488,8 +1489,8 @@ async def test_folder_sync_from_synapse_files_with_activity( == ACTIVITY_DESCRIPTION ) - @skip("Skip integration tests for soon to be removed code") - async def test_folder_sync_from_synapse_mix_of_entities( + # @skip("Skip integration tests for soon to be removed code") + def test_folder_sync_from_synapse_mix_of_entities( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1504,9 +1505,9 @@ async def test_folder_sync_from_synapse_mix_of_entities( └── table_test_syncFromSynapse (uploaded, not synced) """ # GIVEN a folder to sync from - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 1 temporary file on disk: @@ -1572,7 +1573,7 @@ async def test_folder_sync_from_synapse_mix_of_entities( assert pd.isna(matching_row[ACTIVITY_NAME_COLUMN].values[0]) assert pd.isna(matching_row[ACTIVITY_DESCRIPTION_COLUMN].values[0]) - async def test_folder_sync_from_synapse_files_contained_within_sub_folder( + def test_folder_sync_from_synapse_files_contained_within_sub_folder( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1588,15 +1589,15 @@ async def test_folder_sync_from_synapse_files_contained_within_sub_folder( │ └── file2 (uploaded) """ # GIVEN a folder - parent_folder = await Folder( + parent_folder = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(parent_folder.id) # AND a sub folder to sync from - sub_folder = await Folder( - name=str(uuid.uuid4()), parent_id=parent_folder.id - ).store_async() + sub_folder = Folder(name=str(uuid.uuid4()), parent_id=parent_folder.id).store( + synapse_client=syn + ) schedule_for_cleanup(sub_folder.id) # AND 2 temporary files on disk: @@ -1671,8 +1672,8 @@ def verify_manifest(path: str) -> None: sub_directory = os.path.join(temp_dir, sub_folder.name) verify_manifest(path=os.path.join(sub_directory, MANIFEST_FILE)) - @skip("Skip integration tests for soon to be removed code") - async def test_folder_sync_from_synapse_files_contained_within_sub_folder_root_manifest_only( + # @skip("Skip integration tests for soon to be removed code") + def test_folder_sync_from_synapse_files_contained_within_sub_folder_root_manifest_only( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1690,15 +1691,15 @@ async def test_folder_sync_from_synapse_files_contained_within_sub_folder_root_m Verifies that only the root manifest is created. """ # GIVEN a folder - parent_folder = await Folder( + parent_folder = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(parent_folder.id) # AND a sub folder to sync from - sub_folder = await Folder( - name=str(uuid.uuid4()), parent_id=parent_folder.id - ).store_async() + sub_folder = Folder(name=str(uuid.uuid4()), parent_id=parent_folder.id).store( + synapse_client=syn + ) schedule_for_cleanup(sub_folder.id) # AND 2 temporary files on disk: @@ -1765,8 +1766,8 @@ async def test_folder_sync_from_synapse_files_contained_within_sub_folder_root_m sub_directory = os.path.join(temp_dir, sub_folder.name) assert not os.path.exists(os.path.join(sub_directory, MANIFEST_FILE)) - @skip("Skip integration tests for soon to be removed code") - async def test_folder_sync_from_synapse_files_contained_within_sub_folder_suppress_manifest( + # @skip("Skip integration tests for soon to be removed code") + def test_folder_sync_from_synapse_files_contained_within_sub_folder_suppress_manifest( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1784,15 +1785,15 @@ async def test_folder_sync_from_synapse_files_contained_within_sub_folder_suppre Verifies that the manifest is not created at all. """ # GIVEN a folder - parent_folder = await Folder( + parent_folder = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(parent_folder.id) # AND a sub folder to sync from - sub_folder = await Folder( - name=str(uuid.uuid4()), parent_id=parent_folder.id - ).store_async() + sub_folder = Folder(name=str(uuid.uuid4()), parent_id=parent_folder.id).store( + synapse_client=syn + ) schedule_for_cleanup(sub_folder.id) # AND 2 temporary files on disk: @@ -1826,8 +1827,8 @@ async def test_folder_sync_from_synapse_files_contained_within_sub_folder_suppre assert not os.path.exists(os.path.join(temp_dir, MANIFEST_FILE)) assert not os.path.exists(os.path.join(sub_directory, MANIFEST_FILE)) - @skip("Skip integration tests for soon to be removed code") - async def test_folder_sync_from_synapse_files_spread_across_folders( + # @skip("Skip integration tests for soon to be removed code") + def test_folder_sync_from_synapse_files_spread_across_folders( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -1845,21 +1846,21 @@ async def test_folder_sync_from_synapse_files_spread_across_folders( │ └── file3 """ # GIVEN a folder - parent_folder = await Folder( + parent_folder = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(parent_folder.id) # AND a sub folder to sync from - sub_folder_1 = await Folder( - name=str(uuid.uuid4()), parent_id=parent_folder.id - ).store_async() + sub_folder_1 = Folder(name=str(uuid.uuid4()), parent_id=parent_folder.id).store( + synapse_client=syn + ) schedule_for_cleanup(sub_folder_1.id) # AND another sub folder to sync from - sub_folder_2 = await Folder( - name=str(uuid.uuid4()), parent_id=parent_folder.id - ).store_async() + sub_folder_2 = Folder(name=str(uuid.uuid4()), parent_id=parent_folder.id).store( + synapse_client=syn + ) schedule_for_cleanup(sub_folder_2.id) # AND 3 temporary files on disk: @@ -2015,8 +2016,8 @@ async def test_folder_sync_from_synapse_files_spread_across_folders( assert pd.isna(matching_row[ACTIVITY_DESCRIPTION_COLUMN].values[0]) assert found_matching_file - @skip("Skip integration tests for soon to be removed code") - async def test_sync_from_synapse_follow_links_files( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_from_synapse_follow_links_files( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -2034,15 +2035,15 @@ async def test_sync_from_synapse_follow_links_files( └── link_to_file2 -> ../folder_with_files/file2 """ # GIVEN a folder - folder_with_files = await Folder( + folder_with_files = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_files.id) # AND a second folder to sync from - folder_with_links = await Folder( + folder_with_links = Folder( name=str(uuid.uuid4()), parent_id=folder_with_files.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_links.id) # AND 2 temporary files on disk: @@ -2106,8 +2107,8 @@ async def test_sync_from_synapse_follow_links_files( assert pd.isna(matching_row[ACTIVITY_NAME_COLUMN].values[0]) assert pd.isna(matching_row[ACTIVITY_DESCRIPTION_COLUMN].values[0]) - @skip("Skip integration tests for soon to be removed code") - async def test_sync_from_synapse_follow_links_folder( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_from_synapse_follow_links_folder( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -2124,9 +2125,9 @@ async def test_sync_from_synapse_follow_links_folder( └── link_to_folder_with_files -> ../folder_with_files """ # GIVEN a folder - folder_with_files = await Folder( + folder_with_files = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_files.id) # AND two files in the folder @@ -2139,9 +2140,9 @@ async def test_sync_from_synapse_follow_links_folder( file_entities.append(file_entity) # AND a second folder to sync from - folder_with_links = await Folder( + folder_with_links = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_links.id) # AND a link to folder_with_files in folder_with_links @@ -2196,8 +2197,8 @@ async def test_sync_from_synapse_follow_links_folder( assert pd.isna(matching_row[ACTIVITY_NAME_COLUMN].values[0]) assert pd.isna(matching_row[ACTIVITY_DESCRIPTION_COLUMN].values[0]) - @skip("Skip integration tests for soon to be removed code") - async def test_sync_from_synapse_follow_links_sync_contains_all_folders( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_from_synapse_follow_links_sync_contains_all_folders( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -2220,21 +2221,21 @@ async def test_sync_from_synapse_follow_links_sync_contains_all_folders( In this case a FileEntity is returned for each of the files and links (4) total. """ # GIVEN a parent folder - parent_folder = await Folder( + parent_folder = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(parent_folder.id) # AND a folder for files - folder_with_files = await Folder( + folder_with_files = Folder( name=str(uuid.uuid4()), parent_id=parent_folder.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_files.id) # AND a second folder to sync from - folder_with_links = await Folder( + folder_with_links = Folder( name=str(uuid.uuid4()), parent_id=parent_folder.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_links.id) # AND 2 temporary files on disk: @@ -2387,8 +2388,8 @@ async def test_sync_from_synapse_follow_links_sync_contains_all_folders( assert pd.isna(matching_row[ACTIVITY_DESCRIPTION_COLUMN].values[0]) assert found_matching_file - @skip("Skip integration tests for soon to be removed code") - async def test_sync_from_synapse_dont_follow_links( + # @skip("Skip integration tests for soon to be removed code") + def test_sync_from_synapse_dont_follow_links( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -2406,15 +2407,15 @@ async def test_sync_from_synapse_dont_follow_links( └── link_to_file2 -> ../folder_with_files/file2 """ # GIVEN a folder - folder_with_files = await Folder( + folder_with_files = Folder( name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_files.id) # AND a second folder to sync from - folder_with_links = await Folder( + folder_with_links = Folder( name=str(uuid.uuid4()), parent_id=folder_with_files.id - ).store_async() + ).store(synapse_client=syn) schedule_for_cleanup(folder_with_links.id) # AND 2 temporary files on disk: @@ -2443,8 +2444,8 @@ async def test_sync_from_synapse_dont_follow_links( # AND the manifest has not been created assert os.path.exists(os.path.join(temp_dir, MANIFEST_FILE)) is False - @skip("Skip integration tests for soon to be removed code") - async def test_file_sync_from_synapse( + # @skip("Skip integration tests for soon to be removed code") + def test_file_sync_from_synapse( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -2455,9 +2456,9 @@ async def test_file_sync_from_synapse( Also verifies that a manifest file is not created if the entity is a file. """ # GIVEN a folder to sync from - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 1 temporary file on disk: @@ -2486,8 +2487,8 @@ async def test_file_sync_from_synapse( # AND the manifest has not been created assert os.path.exists(os.path.join(temp_dir, MANIFEST_FILE)) is False - @skip("Skip integration tests for soon to be removed code") - async def test_file_sync_from_synapse_specific_version( + # @skip("Skip integration tests for soon to be removed code") + def test_file_sync_from_synapse_specific_version( self, syn: Synapse, schedule_for_cleanup: Callable[..., None], @@ -2499,9 +2500,9 @@ async def test_file_sync_from_synapse_specific_version( Also verifies that a manifest file is not created if the entity is a file. """ # GIVEN a folder to sync from - folder = await Folder( - name=str(uuid.uuid4()), parent_id=project_model.id - ).store_async() + folder = Folder(name=str(uuid.uuid4()), parent_id=project_model.id).store( + synapse_client=syn + ) schedule_for_cleanup(folder.id) # AND 1 temporary file on disk: diff --git a/tests/integration/synapseutils/test_synapseutils_walk.py b/tests/integration/synapseutils/test_synapseutils_walk.py index e9f298d32..e2c4a98a6 100644 --- a/tests/integration/synapseutils/test_synapseutils_walk.py +++ b/tests/integration/synapseutils/test_synapseutils_walk.py @@ -1,6 +1,5 @@ import os import uuid -from unittest import skip import pytest from func_timeout import FunctionTimedOut, func_set_timeout @@ -9,8 +8,10 @@ import synapseutils from synapseclient import File, Folder, Project +# from unittest import skip -@skip("Skip integration tests for soon to be removed code") + +# @skip("Skip integration tests for soon to be removed code") async def test_walk(syn, schedule_for_cleanup): try: execute_test_walk(syn, schedule_for_cleanup) diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 574d2768a..b5e7167a2 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -14,7 +14,7 @@ from synapseclient import Synapse from synapseclient.core.logging_setup import SILENT_LOGGER_NAME -Synapse.allow_client_caching = False +Synapse.allow_client_caching(False) def pytest_runtest_setup(): @@ -49,7 +49,7 @@ def syn(): """ Create a Synapse instance that can be shared by all tests in the session. """ - syn = Synapse(debug=False, skip_checks=True) + syn = Synapse(debug=False, skip_checks=True, cache_client=False) syn.logger = logging.getLogger(SILENT_LOGGER_NAME) Synapse.set_client(syn) return syn diff --git a/tests/unit/synapseclient/mixins/unit_test_table_components.py b/tests/unit/synapseclient/mixins/unit_test_table_components.py index e2a91e6dd..9d6428bb9 100644 --- a/tests/unit/synapseclient/mixins/unit_test_table_components.py +++ b/tests/unit/synapseclient/mixins/unit_test_table_components.py @@ -16,7 +16,7 @@ QUERY_TABLE_CSV_REQUEST, ) from synapseclient.core.utils import MB -from synapseclient.models import Activity, Column, ColumnType +from synapseclient.models import Activity, Column from synapseclient.models.mixins.table_components import ( ColumnMixin, DeleteMixin, @@ -1444,6 +1444,7 @@ async def test_query_async(self): header=True, download_location=None, timeout=250, + synapse_client=self.syn, ) # AND csv_to_pandas_df should be called with correct args @@ -1455,6 +1456,8 @@ async def test_query_async(self): row_id_and_version_in_index=False, date_columns=None, list_columns=None, + dtype={"col1": str}, + list_column_types=None, ) # AND the result should match expected DataFrame @@ -1531,6 +1534,7 @@ async def test_query_async_with_date_and_list_columns(self): header=True, download_location=None, timeout=250, + synapse_client=self.syn, ) # AND csv_to_pandas_df should be called with date_columns and list_columns populated @@ -1542,6 +1546,12 @@ async def test_query_async_with_date_and_list_columns(self): row_id_and_version_in_index=False, date_columns=["date_col"], # Should contain the DATE column list_columns=["list_col"], # Should contain the STRING_LIST column + dtype={ + "string_col": str, + }, + list_column_types={ + "list_col": ColumnType.STRING_LIST, + }, ) # AND the result should match expected DataFrame @@ -1622,11 +1632,12 @@ async def test_query_part_mask_async(self): limit=None, offset=None, timeout=250, + synapse_client=self.syn, ) # AND mock_rowset_to_pandas_df should be called with correct args mock_rowset_to_pandas_df.assert_called_once_with( query_result_bundle=mock_query_result_bundle, - synapse=self.syn, + synapse_client=self.syn, row_id_and_version_in_index=False, ) # AND the result should be a QueryResultOutput with expected values @@ -1705,11 +1716,12 @@ async def test_query_part_mask_async_minimal(self): limit=None, offset=None, timeout=250, + synapse_client=self.syn, ) mock_rowset_to_pandas_df.assert_called_once_with( query_result_bundle=mock_query_result_bundle, - synapse=self.syn, + synapse_client=self.syn, row_id_and_version_in_index=False, ) @@ -1933,7 +1945,8 @@ async def test_delete_rows_via_dataframe_fail_missing_columns(self, df, error_ms pd.DataFrame( {"ROW_ID": ["C", "D"], "ROW_VERSION": [2, 2]} ), # Both invalid - "Rows with the following ROW_ID and ROW_VERSION pairs were not found in table syn123: \\(C, 2\\), \\(D, 2\\).", # Special characters must be escaped due to use with regex in test + # Special characters must be escaped due to use with regex in test + "Rows with the following ROW_ID and ROW_VERSION pairs were not found in table syn123: \\(C, 2\\), \\(D, 2\\).", ), ], ) @@ -2070,7 +2083,7 @@ async def test_query_table_csv_basic_functionality( # WHEN calling the function completed_query_job, file_path = await _query_table_csv( - query=sample_query, synapse=mock_synapse + query=sample_query, synapse_client=mock_synapse ) # THEN ensure download file is correct @@ -2117,7 +2130,7 @@ async def test_query_table_csv_with_download_location( # WHEN calling the function with a download location result = await _query_table_csv( query=sample_query, - synapse=mock_synapse, + synapse_client=mock_synapse, download_location=download_location, ) @@ -2715,7 +2728,7 @@ async def test_query_table_row_set_basic( # WHEN calling _query_table_row_set result = await _query_table_row_set( query=query, - synapse=mock_synapse_client, + synapse_client=mock_synapse_client, ) # THEN verify the result @@ -2771,7 +2784,7 @@ async def test_query_table_row_set_with_parameters( # WHEN calling _query_table_row_set with parameters result = await _query_table_row_set( query=query, - synapse=mock_synapse_client, + synapse_client=mock_synapse_client, limit=limit, offset=offset, part_mask=part_mask, @@ -2855,7 +2868,7 @@ async def test_query_table_next_page_basic_functionality( result = _query_table_next_page( next_page_token=sample_next_page_token, table_id=sample_table_id, - synapse=self.syn, + synapse_client=self.syn, ) # Verify API call was made correctly mock_wait_for_async.assert_called_once_with( diff --git a/tests/unit/synapseclient/models/async/unit_test_dataset_async.py b/tests/unit/synapseclient/models/async/unit_test_dataset_async.py index 261c3fcfa..5a581ead4 100644 --- a/tests/unit/synapseclient/models/async/unit_test_dataset_async.py +++ b/tests/unit/synapseclient/models/async/unit_test_dataset_async.py @@ -126,7 +126,7 @@ def test_append_entity_ref(self): # THEN I expect the Dataset to have the EntityRef in its items assert dataset.items == [EntityRef(id="syn1234", version=1)] - async def test_add_item_entity_ref(self): + def test_add_item_entity_ref(self): # GIVEN an empty Dataset dataset = Dataset() # WHEN I add an EntityRef to it @@ -134,7 +134,7 @@ async def test_add_item_entity_ref(self): # THEN I expect the Dataset to have the EntityRef in its items assert dataset.items == [EntityRef(id="syn1234", version=1)] - async def test_add_item_file(self): + def test_add_item_file(self): # GIVEN an empty Dataset dataset = Dataset() # WHEN I add a File to it @@ -142,7 +142,7 @@ async def test_add_item_file(self): # THEN I expect the Dataset to have the File in its items assert dataset.items == [EntityRef(id="syn1234", version=1)] - async def test_add_item_folder(self): + def test_add_item_folder(self): # GIVEN an empty Dataset dataset = Dataset() # WHEN I add a Folder to it @@ -162,7 +162,7 @@ async def test_add_item_folder(self): EntityRef(id="syn1235", version=1), ] - async def test_add_item_invalid_type(self): + def test_add_item_invalid_type(self): # GIVEN an empty Dataset dataset = Dataset() # WHEN I add an invalid type to it @@ -172,7 +172,7 @@ async def test_add_item_invalid_type(self): ): dataset.add_item(1) - async def test_remove_entity_ref(self): + def test_remove_entity_ref(self): # GIVEN a Dataset with an EntityRef dataset = Dataset(items=[EntityRef(id="syn1234", version=1)]) # WHEN I remove the EntityRef from it @@ -180,7 +180,7 @@ async def test_remove_entity_ref(self): # THEN I expect the Dataset to have no items assert dataset.items == [] - async def test_remove_item_entity_ref(self): + def test_remove_item_entity_ref(self): # GIVEN a Dataset with an EntityRef dataset = Dataset(items=[EntityRef(id="syn1234", version=1)]) # WHEN I remove the EntityRef from it @@ -188,7 +188,7 @@ async def test_remove_item_entity_ref(self): # THEN I expect the Dataset to have no items assert dataset.items == [] - async def test_remove_item_entity_ref_version(self): + def test_remove_item_entity_ref_version(self): # GIVEN a Dataset with 2 versions of an EntityRef dataset = Dataset( items=[ @@ -201,7 +201,7 @@ async def test_remove_item_entity_ref_version(self): # THEN I expect the Dataset to only have version 2 of the EntityRef assert dataset.items == [EntityRef(id="syn1234", version=2)] - async def test_remove_item_file(self): + def test_remove_item_file(self): # GIVEN a Dataset with a File dataset = Dataset(items=[EntityRef(id="syn1234", version=1)]) # WHEN I remove the File from it @@ -209,7 +209,7 @@ async def test_remove_item_file(self): # THEN I expect the Dataset to have no items assert dataset.items == [] - async def test_remove_item_folder(self): + def test_remove_item_folder(self): # GIVEN a Dataset with a Folder dataset = Dataset(items=[EntityRef(id="syn1235", version=1)]) with patch( diff --git a/tests/unit/synapseclient/models/unit_test_permissions.py b/tests/unit/synapseclient/models/unit_test_permissions.py index 7a731ec26..1c9573054 100644 --- a/tests/unit/synapseclient/models/unit_test_permissions.py +++ b/tests/unit/synapseclient/models/unit_test_permissions.py @@ -47,7 +47,7 @@ def setup_method(self): self.delete_acl_patcher.stop() self.get_benefactor_patcher.stop() - async def test_delete_permissions_no_id_raises_error(self): + def test_delete_permissions_no_id_raises_error(self): """Test that attempting to delete permissions without an ID raises ValueError.""" # GIVEN a file with no ID file = File() @@ -59,7 +59,7 @@ async def test_delete_permissions_no_id_raises_error(self): ): file.delete_permissions() - async def test_delete_permissions_basic_single_entity(self): + def test_delete_permissions_basic_single_entity(self): """Test basic permission deletion for a single entity.""" # GIVEN a file with an ID file = File(id="syn123", name="test_file.txt") @@ -72,7 +72,7 @@ async def test_delete_permissions_basic_single_entity(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_already_inherits(self): + def test_delete_permissions_already_inherits(self): """Test handling of 403 error when entity already inherits permissions.""" # GIVEN a file with an ID file = File(id="syn123") @@ -99,7 +99,7 @@ async def test_delete_permissions_already_inherits(self): # AND a debug log message should be generated self.synapse_client.logger.debug.assert_called_once() - async def test_delete_permissions_other_http_error(self): + def test_delete_permissions_other_http_error(self): """Test handling of other HTTP errors during deletion.""" # GIVEN a file with an ID file = File(id="syn123") @@ -126,7 +126,7 @@ async def test_delete_permissions_other_http_error(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_general_exception(self): + def test_delete_permissions_general_exception(self): """Test handling of general exceptions during deletion.""" # GIVEN a file with an ID file = File(id="syn123") @@ -143,7 +143,7 @@ async def test_delete_permissions_general_exception(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_skip_self(self): + def test_delete_permissions_skip_self(self): """Test that when include_self=False, the entity's ACL is not deleted.""" # GIVEN a file with an ID file = File(id="syn123") @@ -154,7 +154,7 @@ async def test_delete_permissions_skip_self(self): # THEN the delete_entity_acl function should not be called self.mock_delete_acl.assert_not_called() - async def test_delete_permissions_project_warning(self): + def test_delete_permissions_project_warning(self): """Test that deleting permissions on a Project shows warning and skips deletion.""" # GIVEN a project with an ID project = Project(id="syn123", name="test_project") @@ -168,7 +168,7 @@ async def test_delete_permissions_project_warning(self): # AND delete_entity_acl should not be called self.mock_delete_acl.assert_not_called() - async def test_delete_permissions_folder_recursive_structure(self): + def test_delete_permissions_folder_recursive_structure(self): """Test recursive deletion on a complex folder structure.""" # GIVEN a folder with an ID and child entities folder = Folder(id="syn123", name="parent_folder") @@ -201,7 +201,7 @@ async def test_delete_permissions_folder_recursive_structure(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_target_entity_types_folder_only(self): + def test_delete_permissions_target_entity_types_folder_only(self): """Test filtering deletion by folder entity type only.""" # GIVEN a folder with child folder and file folder = Folder(id="syn123", name="parent_folder") @@ -236,7 +236,7 @@ async def test_delete_permissions_target_entity_types_folder_only(self): # AND delete_entity_acl should be called on the parent folder self.mock_delete_acl.assert_called_once() - async def test_delete_permissions_target_entity_types_file_only(self): + def test_delete_permissions_target_entity_types_file_only(self): """Test filtering deletion by file entity type only.""" # GIVEN a folder with child folder and file folder = Folder(id="syn123", name="parent_folder") @@ -272,7 +272,7 @@ async def test_delete_permissions_target_entity_types_file_only(self): # AND delete_entity_acl should be called on the parent folder self.mock_delete_acl.assert_called_once() - async def test_delete_permissions_case_insensitive_entity_types(self): + def test_delete_permissions_case_insensitive_entity_types(self): """Test that entity type matching is case-insensitive.""" # GIVEN a folder with child entities folder = Folder(id="syn123", name="parent_folder") @@ -297,7 +297,7 @@ async def test_delete_permissions_case_insensitive_entity_types(self): self.mock_delete_acl.assert_called_once() assert child_folder.delete_permissions_async.called - async def test_delete_permissions_invalid_recursive_without_container_content(self): + def test_delete_permissions_invalid_recursive_without_container_content(self): """Test that recursive=True without include_container_content=True raises ValueError.""" # GIVEN a folder with an ID folder = Folder(id="syn123") @@ -311,7 +311,7 @@ async def test_delete_permissions_invalid_recursive_without_container_content(se ): folder.delete_permissions(recursive=True, include_container_content=False) - async def test_delete_permissions_dry_run_mode(self): + def test_delete_permissions_dry_run_mode(self): """Test dry run mode logs changes without executing them.""" # GIVEN a folder with child entities folder = Folder(id="syn123") @@ -328,7 +328,7 @@ async def test_delete_permissions_dry_run_mode(self): # AND actual deletion should not occur self.mock_delete_acl.assert_not_called() - async def test_delete_permissions_benefactor_tracking(self): + def test_delete_permissions_benefactor_tracking(self): """Test that benefactor tracking works correctly during deletion.""" # GIVEN a file with an ID and benefactor tracker file = File(id="syn123") @@ -345,7 +345,7 @@ async def test_delete_permissions_benefactor_tracking(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_empty_target_entity_types(self): + def test_delete_permissions_empty_target_entity_types(self): """Test handling of empty target entity types list.""" # GIVEN a folder with child entities folder = Folder(id="syn123") @@ -363,7 +363,7 @@ async def test_delete_permissions_empty_target_entity_types(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_multiple_entity_types(self): + def test_delete_permissions_multiple_entity_types(self): """Test handling multiple entity types.""" # GIVEN a file with an ID file = File(id="syn123") @@ -376,7 +376,7 @@ async def test_delete_permissions_multiple_entity_types(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_custom_synapse_client(self): + def test_delete_permissions_custom_synapse_client(self): """Test using a custom Synapse client.""" # GIVEN a file and a custom synapse client file = File(id="syn123") @@ -390,7 +390,7 @@ async def test_delete_permissions_custom_synapse_client(self): # THEN delete_entity_acl should be called with the custom client self.mock_delete_acl.assert_called_once() - async def test_delete_permissions_complex_hierarchy_dry_run(self): + def test_delete_permissions_complex_hierarchy_dry_run(self): """Test dry run with complex hierarchy showing detailed logging.""" # GIVEN a complex folder structure root_folder = Folder(id="syn100", name="root") @@ -431,7 +431,7 @@ async def test_delete_permissions_complex_hierarchy_dry_run(self): # AND no actual deletions should occur self.mock_delete_acl.assert_not_called() - async def test_delete_permissions_folder_only_direct_children(self): + def test_delete_permissions_folder_only_direct_children(self): """Test deletion affecting only direct children, not recursive.""" # GIVEN a folder with nested structure parent_folder = Folder(id="syn100") @@ -462,7 +462,7 @@ async def test_delete_permissions_folder_only_direct_children(self): # AND parent should be processed self.mock_delete_acl.assert_called_once() - async def test_delete_permissions_benefactor_impact_logging(self): + def test_delete_permissions_benefactor_impact_logging(self): """Test logging when ACL deletion affects other entities.""" # GIVEN a file with benefactor relationships file = File(id="syn123") @@ -477,7 +477,7 @@ async def test_delete_permissions_benefactor_impact_logging(self): # THEN deletion should complete self.mock_delete_acl.assert_called_once() - async def test_delete_permissions_no_container_support(self): + def test_delete_permissions_no_container_support(self): """Test deletion on entity without container support.""" # GIVEN a file (which doesn't have sync_from_synapse_async) file = File(id="syn123") @@ -490,7 +490,7 @@ async def test_delete_permissions_no_container_support(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_entity_without_sync_method(self): + def test_delete_permissions_entity_without_sync_method(self): """Test handling of entities that don't support sync_from_synapse_async.""" # GIVEN a basic entity without container methods file = File(id="syn123") @@ -507,7 +507,7 @@ async def test_delete_permissions_entity_without_sync_method(self): entity_id="syn123", synapse_client=self.synapse_client ) - async def test_delete_permissions_large_hierarchy_performance(self): + def test_delete_permissions_large_hierarchy_performance(self): """Test performance considerations with large hierarchy.""" # GIVEN a folder with many children parent_folder = Folder(id="syn100") @@ -740,7 +740,7 @@ async def test_list_acl_no_id_raises_error(self): with pytest.raises( ValueError, match="The entity must have an ID to list ACLs." ): - file.list_acl() + await file.list_acl_async() async def test_list_acl_basic_single_entity(self): """Test basic ACL listing for a single entity.""" @@ -759,7 +759,7 @@ async def test_list_acl_basic_single_entity(self): self.mock_get_acl.return_value = mock_acl # WHEN listing ACL - result = file.list_acl() + result = await file.list_acl_async() # THEN ACL should be retrieved self.mock_get_acl.assert_called_once_with( @@ -799,7 +799,7 @@ async def test_list_acl_no_local_acl(self): self.mock_get_acl.side_effect = http_error # WHEN listing ACL - result = file.list_acl() + result = await file.list_acl_async() # THEN result should be empty but valid assert isinstance(result, AclListResult) @@ -819,7 +819,7 @@ async def test_list_acl_empty_resource_access(self): self.mock_get_user_headers.return_value = [] # WHEN listing ACL - result = file.list_acl() + result = await file.list_acl_async() # THEN result should be valid but empty assert isinstance(result, AclListResult) @@ -838,7 +838,7 @@ async def test_list_acl_custom_entity_types_accepted(self): self.mock_get_acl.return_value = {"id": "syn123", "resourceAccess": []} # WHEN listing ACL with custom entity types (even non-standard ones) - result = file.list_acl(target_entity_types=["CustomType", "FOLDER"]) + result = await file.list_acl_async(target_entity_types=["CustomType", "FOLDER"]) # THEN the method should complete successfully (entity types are normalized but not validated) assert isinstance(result, AclListResult) @@ -857,7 +857,7 @@ async def test_list_acl_case_insensitive_entity_types(self): self.mock_get_user_headers.return_value = [] # WHEN listing ACL with mixed-case entity type - result = file.list_acl(target_entity_types=["FiLe"]) + result = await file.list_acl_async(target_entity_types=["FiLe"]) # THEN ACL should be retrieved (case-insensitive match) self.mock_get_acl.assert_called_once() @@ -874,7 +874,7 @@ async def test_list_acl_recursive_validation(self): ValueError, match="When recursive=True, include_container_content must also be True", ): - folder.list_acl(recursive=True, include_container_content=False) + await folder.list_acl_async(recursive=True, include_container_content=False) async def test_list_acl_container_content_only(self): """Test listing ACL for container content without recursion.""" @@ -903,7 +903,9 @@ async def test_list_acl_container_content_only(self): self.mock_get_user_headers.return_value = [] # WHEN listing ACL with include_container_content=True but not recursive - result = folder.list_acl(include_container_content=True, recursive=False) + result = await folder.list_acl_async( + include_container_content=True, recursive=False + ) # THEN sync should be called folder.sync_from_synapse_async.assert_called_once() @@ -932,7 +934,9 @@ async def test_list_acl_recursive_processing(self): self.mock_get_user_headers.return_value = [] # WHEN listing ACL recursively - result = folder.list_acl(recursive=True, include_container_content=True) + result = await folder.list_acl_async( + recursive=True, include_container_content=True + ) # THEN entities should be collected recursively folder._collect_entities.assert_called_once() @@ -963,7 +967,7 @@ async def test_list_acl_entity_type_filtering_recursive(self): self.mock_get_user_headers.return_value = [] # WHEN listing ACL filtered by folder type only - result = folder.list_acl( + result = await folder.list_acl_async( recursive=True, include_container_content=True, target_entity_types=["folder"], @@ -999,7 +1003,7 @@ async def test_list_acl_with_tree_logging(self): ] # WHEN listing ACL with tree logging - result = folder.list_acl( + result = await folder.list_acl_async( log_tree=True, recursive=True, include_container_content=True ) @@ -1035,9 +1039,6 @@ async def test_list_acl_complex_hierarchy(self): # AND mock different ACL responses for each entity acl_responses = {} - def mock_get_acl_side_effect(entity_id, synapse_client): - return acl_responses.get(entity_id, {"resourceAccess": []}) - # Set up different ACLs for different entities acl_responses["syn100"] = { "id": "syn100", @@ -1053,12 +1054,6 @@ def mock_get_acl_side_effect(entity_id, synapse_client): } # syn300 (file) has no local ACL - will get 404 - async def mock_get_current_acl(client): - entity_id = None - if hasattr(self, "id"): - entity_id = self.id - return acl_responses.get(entity_id, {}).get("resourceAccess", []) - # Mock each entity's _get_current_entity_acl method child_folder._get_current_entity_acl = AsyncMock() child_folder._get_current_entity_acl.return_value = { @@ -1075,7 +1070,9 @@ async def mock_get_current_acl(client): ] # WHEN listing ACL recursively - result = root_folder.list_acl(recursive=True, include_container_content=True) + result = await root_folder.list_acl_async( + recursive=True, include_container_content=True + ) # THEN result should include ACLs from multiple entities assert isinstance(result, AclListResult) @@ -1098,7 +1095,7 @@ async def test_list_acl_no_user_headers(self): self.mock_get_user_headers.side_effect = Exception("User service unavailable") # WHEN listing ACL - result = file.list_acl() + result = await file.list_acl_async() # THEN result should still be created without user info assert isinstance(result, AclListResult) @@ -1133,7 +1130,7 @@ async def test_list_acl_large_hierarchy(self): self.mock_get_user_headers.return_value = [] # WHEN listing ACL - result = folder.list_acl(include_container_content=True) + result = await folder.list_acl_async(include_container_content=True) # THEN operation should complete successfully assert isinstance(result, AclListResult) @@ -1166,7 +1163,7 @@ async def test_list_acl_mixed_permissions(self): ] # WHEN listing ACL - result = file.list_acl() + result = await file.list_acl_async() # THEN result should contain all permission combinations assert isinstance(result, AclListResult) diff --git a/tests/unit/synapseclient/unit_test_client.py b/tests/unit/synapseclient/unit_test_client.py index f40f6daa1..f29a7bb20 100644 --- a/tests/unit/synapseclient/unit_test_client.py +++ b/tests/unit/synapseclient/unit_test_client.py @@ -1673,14 +1673,16 @@ def _create_storage_location_test( ) -> None: with patch.object(self.syn, "restPOST") as mock_post, patch.object( self.syn, "setStorageLocation" - ) as mock_set_storage_location, patch.object(self.syn, "store") as syn_store: + ) as mock_set_storage_location, patch.object( + self.syn, "store_async" + ) as syn_store: mock_post.return_value = {"storageLocationId": 456} mock_set_storage_location.return_value = {"id": "foo"} # either passed a folder or expected to create one expected_folder = kwargs.get("folder") if not expected_folder: - expected_folder = syn_store.return_value = Mock() + expected_folder = syn_store.return_value = AsyncMock() result = self.syn.create_s3_storage_location(*args, **kwargs)