cloud-py-api-nc_py_api-d4a32c6/ 0000775 0002322 0002322 00000000000 14766056032 016745 5 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/README.md 0000664 0002322 0002322 00000010776 14766056032 020237 0 ustar debalance debalance
# Nextcloud Python Framework
[](https://github.com/cloud-py-api/nc_py_api/actions/workflows/analysis-coverage.yml)
[](https://cloud-py-api.github.io/nc_py_api/)
[](https://codecov.io/github/cloud-py-api/nc_py_api)




Python library that provides a robust and well-documented API that allows developers to interact with and extend Nextcloud's functionality.
### The key features are:
* **Fast**: High performance, and as low-latency as possible.
* **Intuitive**: Fast to code, easy to use.
* **Reliable**: Minimum number of incompatible changes.
* **Robust**: All code is covered with tests as much as possible.
* **Easy**: Designed to be easy to use with excellent documentation.
* **Sync + Async**: Provides both sync and async APIs.
### Differences between the Nextcloud and NextcloudApp classes
The **Nextcloud** class functions as a standard Nextcloud client,
enabling you to make API requests using a username and password.
On the other hand, the **NextcloudApp** class is designed for creating applications for Nextcloud.
It uses [AppAPI](https://github.com/cloud-py-api/app_api) to provide additional functionality allowing
applications have their own graphical interface, fulfill requests from different users,
and everything else that is necessary to implement full-fledged applications.
Both classes offer most of the same APIs,
but NextcloudApp has a broader selection since applications typically require access to more APIs.
Any code written for the Nextcloud class can easily be adapted for use with the NextcloudApp class,
as long as it doesn't involve calls that require user password verification.
### Nextcloud skeleton app in Python
```python3
from contextlib import asynccontextmanager
from fastapi import FastAPI
from nc_py_api import NextcloudApp
from nc_py_api.ex_app import AppAPIAuthMiddleware, LogLvl, run_app, set_handlers
@asynccontextmanager
async def lifespan(app: FastAPI):
set_handlers(app, enabled_handler)
yield
APP = FastAPI(lifespan=lifespan)
APP.add_middleware(AppAPIAuthMiddleware)
def enabled_handler(enabled: bool, nc: NextcloudApp) -> str:
if enabled:
nc.log(LogLvl.WARNING, "Hello from nc_py_api.")
else:
nc.log(LogLvl.WARNING, "Bye bye from nc_py_api.")
return ""
if __name__ == "__main__":
run_app("main:APP", log_level="trace")
```
### Support
You can support us in several ways:
- ⭐️ Star our work (it really motivates)
- ❗️ Create an Issue or feature request (bring to us an excellent idea)
- 💁 Resolve some Issue or create a Pull Request (contribute to this project)
- 🙏 Write an example of its use or correct a typo in the documentation.
## More Information
- [Documentation](https://cloud-py-api.github.io/nc_py_api/)
- [First steps](https://cloud-py-api.github.io/nc_py_api/FirstSteps.html)
- [More APIs](https://cloud-py-api.github.io/nc_py_api/MoreAPIs.html)
- [Writing a simple Nextcloud Application](https://cloud-py-api.github.io/nc_py_api/NextcloudApp.html)
- [Using Nextcloud Talk Bot API in Application](https://cloud-py-api.github.io/nc_py_api/NextcloudTalkBot.html)
- [Using Language Models In Application](https://cloud-py-api.github.io/nc_py_api/NextcloudTalkBotTransformers.html)
- [Writing a Nextcloud System Application](https://cloud-py-api.github.io/nc_py_api/NextcloudSysApp.html)
- [Examples](https://github.com/cloud-py-api/nc_py_api/tree/main/examples)
- [Contribute](https://github.com/cloud-py-api/nc_py_api/blob/main/.github/CONTRIBUTING.md)
- [Discussions](https://github.com/cloud-py-api/nc_py_api/discussions)
- [Issues](https://github.com/cloud-py-api/nc_py_api/issues)
- [Setting up dev environment](https://cloud-py-api.github.io/nc_py_api/DevSetup.html)
- [Changelog](https://github.com/cloud-py-api/nc_py_api/blob/main/CHANGELOG.md)
cloud-py-api-nc_py_api-d4a32c6/tests/ 0000775 0002322 0002322 00000000000 14766056032 020107 5 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/tests/_install_wait.py 0000664 0002322 0002322 00000001227 14766056032 023314 0 ustar debalance debalance import re
from sys import argv
from time import sleep
from requests import get
def check_heartbeat(url: str, regexp: str, n_tries: int, wait_interval: float) -> int:
for _ in range(n_tries):
try:
result = get(url)
if result.text and re.search(regexp, result.text, re.IGNORECASE) is not None:
return 0
except Exception as _:
_ = _
sleep(wait_interval)
return 2
# params: app heartbeat url, string to check, number of tries, time to sleep between retries
if __name__ == "__main__":
r = check_heartbeat(str(argv[1]), str(argv[2]), int(argv[3]), float(argv[4]))
exit(r)
cloud-py-api-nc_py_api-d4a32c6/tests/_talk_bot.py 0000664 0002322 0002322 00000004126 14766056032 022422 0 ustar debalance debalance import os
from typing import Annotated
import gfixture_set_env # noqa
import pytest
from fastapi import BackgroundTasks, Depends, FastAPI, Request, Response
from nc_py_api import NextcloudApp, talk_bot
from nc_py_api.ex_app import AppAPIAuthMiddleware, nc_app, run_app, talk_bot_msg
APP = FastAPI()
APP.add_middleware(AppAPIAuthMiddleware, disable_for=["reset_bot_secret"])
COVERAGE_BOT = talk_bot.TalkBot("/talk_bot_coverage", "Coverage bot", "Desc")
def coverage_talk_bot_process_request(message: talk_bot.TalkBotMessage, request: Request):
COVERAGE_BOT.react_to_message(message, "🥳")
COVERAGE_BOT.react_to_message(message, "🫡")
COVERAGE_BOT.delete_reaction(message, "🫡")
COVERAGE_BOT.send_message("Hello from bot!", message)
assert isinstance(message.actor_id, str)
assert isinstance(message.actor_display_name, str)
assert isinstance(message.object_name, str)
assert isinstance(message.object_content, dict)
assert message.object_media_type in ("text/markdown", "text/plain")
assert isinstance(message.conversation_name, str)
assert str(message).find("conversation=") != -1
with pytest.raises(ValueError):
COVERAGE_BOT.react_to_message(message.object_id, "🥳")
with pytest.raises(ValueError):
COVERAGE_BOT.delete_reaction(message.object_id, "🥳")
with pytest.raises(ValueError):
COVERAGE_BOT.send_message("🥳", message.object_id)
@APP.post("/talk_bot_coverage")
def talk_bot_coverage(
request: Request,
_nc: Annotated[NextcloudApp, Depends(nc_app)],
message: Annotated[talk_bot.TalkBotMessage, Depends(talk_bot_msg)],
background_tasks: BackgroundTasks,
):
background_tasks.add_task(coverage_talk_bot_process_request, message, request)
return Response()
# in real program this is not needed, as bot enabling handler is called in the bots process itself and will reset it.
@APP.delete("/reset_bot_secret")
def reset_bot_secret():
os.environ.pop(talk_bot.__get_bot_secret("/talk_bot_coverage"), None)
return Response()
if __name__ == "__main__":
run_app("_talk_bot:APP", log_level="trace")
cloud-py-api-nc_py_api-d4a32c6/tests/_talk_bot_async.py 0000664 0002322 0002322 00000004253 14766056032 023620 0 ustar debalance debalance import os
from typing import Annotated
import gfixture_set_env # noqa
import pytest
from fastapi import BackgroundTasks, Depends, FastAPI, Request, Response
from nc_py_api import AsyncNextcloudApp, talk_bot
from nc_py_api.ex_app import AppAPIAuthMiddleware, anc_app, atalk_bot_msg, run_app
APP = FastAPI()
APP.add_middleware(AppAPIAuthMiddleware, disable_for=["reset_bot_secret"])
COVERAGE_BOT = talk_bot.AsyncTalkBot("/talk_bot_coverage", "Coverage bot", "Desc")
async def coverage_talk_bot_process_request(message: talk_bot.TalkBotMessage, request: Request):
await COVERAGE_BOT.react_to_message(message, "🥳")
await COVERAGE_BOT.react_to_message(message, "🫡")
await COVERAGE_BOT.delete_reaction(message, "🫡")
await COVERAGE_BOT.send_message("Hello from bot!", message)
assert isinstance(message.actor_id, str)
assert isinstance(message.actor_display_name, str)
assert isinstance(message.object_name, str)
assert isinstance(message.object_content, dict)
assert message.object_media_type in ("text/markdown", "text/plain")
assert isinstance(message.conversation_name, str)
assert str(message).find("conversation=") != -1
with pytest.raises(ValueError):
await COVERAGE_BOT.react_to_message(message.object_id, "🥳")
with pytest.raises(ValueError):
await COVERAGE_BOT.delete_reaction(message.object_id, "🥳")
with pytest.raises(ValueError):
await COVERAGE_BOT.send_message("🥳", message.object_id)
@APP.post("/talk_bot_coverage")
async def talk_bot_coverage(
request: Request,
_nc: Annotated[AsyncNextcloudApp, Depends(anc_app)],
message: Annotated[talk_bot.TalkBotMessage, Depends(atalk_bot_msg)],
background_tasks: BackgroundTasks,
):
background_tasks.add_task(coverage_talk_bot_process_request, message, request)
return Response()
# in real program this is not needed, as bot enabling handler is called in the bots process itself and will reset it.
@APP.delete("/reset_bot_secret")
async def reset_bot_secret():
os.environ.pop(talk_bot.__get_bot_secret("/talk_bot_coverage"), None)
return Response()
if __name__ == "__main__":
run_app("_talk_bot_async:APP", log_level="trace")
cloud-py-api-nc_py_api-d4a32c6/tests/_install_only_enabled_handler_async.py 0000664 0002322 0002322 00000000730 14766056032 027673 0 ustar debalance debalance from contextlib import asynccontextmanager
from fastapi import FastAPI
from nc_py_api import AsyncNextcloudApp, ex_app
@asynccontextmanager
async def lifespan(_app: FastAPI):
ex_app.set_handlers(APP, enabled_handler)
yield
APP = FastAPI(lifespan=lifespan)
async def enabled_handler(_enabled: bool, _nc: AsyncNextcloudApp) -> str:
return ""
if __name__ == "__main__":
ex_app.run_app("_install_only_enabled_handler_async:APP", log_level="warning")
cloud-py-api-nc_py_api-d4a32c6/tests/__init__.py 0000664 0002322 0002322 00000000000 14766056032 022206 0 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/tests/_tests_at_the_end.py 0000664 0002322 0002322 00000004653 14766056032 024144 0 ustar debalance debalance import os
import sys
from subprocess import Popen
import pytest
from ._install_wait import check_heartbeat
# These tests will be run separate, and at the end of all other tests.
def _test_ex_app_enable_disable(file_to_test):
child_environment = os.environ.copy()
child_environment["APP_PORT"] = os.environ.get("APP_PORT", "9009")
r = Popen(
[sys.executable, os.path.join(os.path.dirname(os.path.abspath(__file__)), file_to_test)],
env=child_environment,
cwd=os.getcwd(),
)
url = f"http://127.0.0.1:{child_environment['APP_PORT']}/heartbeat"
return r, url
@pytest.mark.parametrize("file_to_test", ("_install_only_enabled_handler.py", "_install_only_enabled_handler_async.py"))
def test_ex_app_enable_disable(nc_client, nc_app, file_to_test):
r, url = _test_ex_app_enable_disable(file_to_test)
try:
if check_heartbeat(url, '"status":"ok"', 15, 0.3):
raise RuntimeError(f"`{file_to_test}` can not start.")
if nc_client.apps.ex_app_is_enabled("nc_py_api"):
nc_client.apps.ex_app_disable("nc_py_api")
assert nc_client.apps.ex_app_is_disabled("nc_py_api") is True
assert nc_client.apps.ex_app_is_enabled("nc_py_api") is False
nc_client.apps.ex_app_enable("nc_py_api")
assert nc_client.apps.ex_app_is_disabled("nc_py_api") is False
assert nc_client.apps.ex_app_is_enabled("nc_py_api") is True
finally:
r.terminate()
r.wait(timeout=10)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("file_to_test", ("_install_only_enabled_handler.py", "_install_only_enabled_handler_async.py"))
async def test_ex_app_enable_disable_async(anc_client, anc_app, file_to_test):
r, url = _test_ex_app_enable_disable(file_to_test)
try:
if check_heartbeat(url, '"status":"ok"', 15, 0.3):
raise RuntimeError(f"`{file_to_test}` can not start.")
if await anc_client.apps.ex_app_is_enabled("nc_py_api"):
await anc_client.apps.ex_app_disable("nc_py_api")
assert await anc_client.apps.ex_app_is_disabled("nc_py_api") is True
assert await anc_client.apps.ex_app_is_enabled("nc_py_api") is False
await anc_client.apps.ex_app_enable("nc_py_api")
assert await anc_client.apps.ex_app_is_disabled("nc_py_api") is False
assert await anc_client.apps.ex_app_is_enabled("nc_py_api") is True
finally:
r.terminate()
r.wait(timeout=10)
cloud-py-api-nc_py_api-d4a32c6/tests/_install.py 0000664 0002322 0002322 00000002454 14766056032 022273 0 ustar debalance debalance import typing
from contextlib import asynccontextmanager
from fastapi import BackgroundTasks, Depends, FastAPI
from fastapi.responses import JSONResponse
from nc_py_api import NextcloudApp, ex_app
@asynccontextmanager
async def lifespan(_app: FastAPI):
ex_app.set_handlers(APP, enabled_handler, default_init=False)
yield
APP = FastAPI(lifespan=lifespan)
@APP.put("/sec_check")
def sec_check(
value: int,
_nc: typing.Annotated[NextcloudApp, Depends(ex_app.nc_app)],
):
print(value, flush=True)
return JSONResponse(content={"error": ""}, status_code=200)
def init_handler_background(nc: NextcloudApp):
nc.set_init_status(100)
@APP.post("/init")
def init_handler(
background_tasks: BackgroundTasks,
nc: typing.Annotated[NextcloudApp, Depends(ex_app.nc_app)],
):
background_tasks.add_task(init_handler_background, nc)
return JSONResponse(content={}, status_code=200)
def enabled_handler(enabled: bool, nc: NextcloudApp) -> str:
print(f"enabled_handler: enabled={enabled}", flush=True)
if enabled:
nc.log(ex_app.LogLvl.WARNING, f"Hello from {nc.app_cfg.app_name} :)")
else:
nc.log(ex_app.LogLvl.WARNING, f"Bye bye from {nc.app_cfg.app_name} :(")
return ""
if __name__ == "__main__":
ex_app.run_app("_install:APP", log_level="trace")
cloud-py-api-nc_py_api-d4a32c6/tests/_install_async.py 0000664 0002322 0002322 00000002540 14766056032 023464 0 ustar debalance debalance import typing
from contextlib import asynccontextmanager
from fastapi import BackgroundTasks, Depends, FastAPI
from fastapi.responses import JSONResponse
from nc_py_api import AsyncNextcloudApp, ex_app
@asynccontextmanager
async def lifespan(_app: FastAPI):
ex_app.set_handlers(APP, enabled_handler, default_init=False)
yield
APP = FastAPI(lifespan=lifespan)
APP.add_middleware(ex_app.AppAPIAuthMiddleware)
@APP.put("/sec_check")
async def sec_check(
value: int,
):
print(value, flush=True)
return JSONResponse(content={"error": ""}, status_code=200)
async def init_handler_background(nc: AsyncNextcloudApp):
await nc.set_init_status(100)
@APP.post("/init")
async def init_handler(
background_tasks: BackgroundTasks,
nc: typing.Annotated[AsyncNextcloudApp, Depends(ex_app.anc_app)],
):
background_tasks.add_task(init_handler_background, nc)
return JSONResponse(content={}, status_code=200)
async def enabled_handler(enabled: bool, nc: AsyncNextcloudApp) -> str:
print(f"enabled_handler: enabled={enabled}", flush=True)
if enabled:
await nc.log(ex_app.LogLvl.WARNING, f"Hello from {nc.app_cfg.app_name} :)")
else:
await nc.log(ex_app.LogLvl.WARNING, f"Bye bye from {nc.app_cfg.app_name} :(")
return ""
if __name__ == "__main__":
ex_app.run_app("_install_async:APP", log_level="trace")
cloud-py-api-nc_py_api-d4a32c6/tests/_install_only_enabled_handler.py 0000664 0002322 0002322 00000000702 14766056032 026475 0 ustar debalance debalance from contextlib import asynccontextmanager
from fastapi import FastAPI
from nc_py_api import NextcloudApp, ex_app
@asynccontextmanager
async def lifespan(_app: FastAPI):
ex_app.set_handlers(APP, enabled_handler)
yield
APP = FastAPI(lifespan=lifespan)
def enabled_handler(_enabled: bool, _nc: NextcloudApp) -> str:
return ""
if __name__ == "__main__":
ex_app.run_app("_install_only_enabled_handler:APP", log_level="warning")
cloud-py-api-nc_py_api-d4a32c6/tests/gfixture_set_env.py 0000664 0002322 0002322 00000001115 14766056032 024037 0 ustar debalance debalance from os import environ
if not environ.get("CI", False): # For local tests
environ["NC_AUTH_USER"] = "admin"
environ["NC_AUTH_PASS"] = "admin" # "MrtGY-KfY24-iiDyg-cr4n4-GLsNZ"
environ["NEXTCLOUD_URL"] = environ.get("NEXTCLOUD_URL", "http://stable29.local")
# environ["NEXTCLOUD_URL"] = environ.get("NEXTCLOUD_URL", "http://stable30.local")
# environ["NEXTCLOUD_URL"] = environ.get("NEXTCLOUD_URL", "http://nextcloud.local")
environ["APP_ID"] = "nc_py_api"
environ["APP_VERSION"] = "1.0.0"
environ["APP_SECRET"] = "12345"
environ["APP_PORT"] = "9009"
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/ 0000775 0002322 0002322 00000000000 14766056032 022602 5 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/notifications_test.py 0000664 0002322 0002322 00000014574 14766056032 027077 0 ustar debalance debalance import datetime
import pytest
from nc_py_api.notifications import Notification
def test_available(nc_app):
assert nc_app.notifications.available
@pytest.mark.asyncio(scope="session")
async def test_available_async(anc_app):
assert await anc_app.notifications.available
def test_create_as_client(nc_client):
with pytest.raises(NotImplementedError):
nc_client.notifications.create("caption")
@pytest.mark.asyncio(scope="session")
async def test_create_as_client_async(anc_client):
with pytest.raises(NotImplementedError):
await anc_client.notifications.create("caption")
def _test_create(new_notification: Notification):
assert isinstance(new_notification, Notification)
assert new_notification.subject == "subject0123"
assert new_notification.message == "message456"
assert new_notification.icon
assert not new_notification.link
assert new_notification.time > datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
assert str(new_notification).find("app_name=") != -1
assert isinstance(new_notification.object_type, str)
def test_create(nc_app):
obj_id = nc_app.notifications.create("subject0123", "message456")
new_notification = nc_app.notifications.by_object_id(obj_id)
_test_create(new_notification)
@pytest.mark.asyncio(scope="session")
async def test_create_async(anc_app):
obj_id = await anc_app.notifications.create("subject0123", "message456")
new_notification = await anc_app.notifications.by_object_id(obj_id)
_test_create(new_notification)
def test_create_link_icon(nc_app):
obj_id = nc_app.notifications.create("1", "", link="https://some.link/gg")
new_notification = nc_app.notifications.by_object_id(obj_id)
assert isinstance(new_notification, Notification)
assert new_notification.subject == "1"
assert not new_notification.message
assert new_notification.icon
assert new_notification.link == "https://some.link/gg"
@pytest.mark.asyncio(scope="session")
async def test_create_link_icon_async(anc_app):
obj_id = await anc_app.notifications.create("1", "", link="https://some.link/gg")
new_notification = await anc_app.notifications.by_object_id(obj_id)
assert isinstance(new_notification, Notification)
assert new_notification.subject == "1"
assert not new_notification.message
assert new_notification.icon
assert new_notification.link == "https://some.link/gg"
def test_delete_all(nc_app):
nc_app.notifications.create("subject0123", "message456")
obj_id1 = nc_app.notifications.create("subject0123", "message456")
ntf1 = nc_app.notifications.by_object_id(obj_id1)
assert ntf1
obj_id2 = nc_app.notifications.create("subject0123", "message456")
ntf2 = nc_app.notifications.by_object_id(obj_id2)
assert ntf2
nc_app.notifications.delete_all()
assert nc_app.notifications.by_object_id(obj_id1) is None
assert nc_app.notifications.by_object_id(obj_id2) is None
assert not nc_app.notifications.get_all()
assert not nc_app.notifications.exists([ntf1.notification_id, ntf2.notification_id])
@pytest.mark.asyncio(scope="session")
async def test_delete_all_async(anc_app):
await anc_app.notifications.create("subject0123", "message456")
obj_id1 = await anc_app.notifications.create("subject0123", "message456")
ntf1 = await anc_app.notifications.by_object_id(obj_id1)
assert ntf1
obj_id2 = await anc_app.notifications.create("subject0123", "message456")
ntf2 = await anc_app.notifications.by_object_id(obj_id2)
assert ntf2
await anc_app.notifications.delete_all()
assert await anc_app.notifications.by_object_id(obj_id1) is None
assert await anc_app.notifications.by_object_id(obj_id2) is None
assert not await anc_app.notifications.get_all()
assert not await anc_app.notifications.exists([ntf1.notification_id, ntf2.notification_id])
def test_delete_one(nc_app):
obj_id1 = nc_app.notifications.create("subject0123")
obj_id2 = nc_app.notifications.create("subject0123")
ntf1 = nc_app.notifications.by_object_id(obj_id1)
ntf2 = nc_app.notifications.by_object_id(obj_id2)
nc_app.notifications.delete(ntf1.notification_id)
assert nc_app.notifications.by_object_id(obj_id1) is None
assert nc_app.notifications.by_object_id(obj_id2)
assert nc_app.notifications.exists([ntf1.notification_id, ntf2.notification_id]) == [ntf2.notification_id]
nc_app.notifications.delete(ntf2.notification_id)
@pytest.mark.asyncio(scope="session")
async def test_delete_one_async(anc_app):
obj_id1 = await anc_app.notifications.create("subject0123")
obj_id2 = await anc_app.notifications.create("subject0123")
ntf1 = await anc_app.notifications.by_object_id(obj_id1)
ntf2 = await anc_app.notifications.by_object_id(obj_id2)
await anc_app.notifications.delete(ntf1.notification_id)
assert await anc_app.notifications.by_object_id(obj_id1) is None
assert await anc_app.notifications.by_object_id(obj_id2)
assert await anc_app.notifications.exists([ntf1.notification_id, ntf2.notification_id]) == [ntf2.notification_id]
await anc_app.notifications.delete(ntf2.notification_id)
def test_create_invalid_args(nc_app):
with pytest.raises(ValueError):
nc_app.notifications.create("")
@pytest.mark.asyncio(scope="session")
async def test_create_invalid_args_async(anc_app):
with pytest.raises(ValueError):
await anc_app.notifications.create("")
def test_get_one(nc_app):
nc_app.notifications.delete_all()
obj_id1 = nc_app.notifications.create("subject0123")
obj_id2 = nc_app.notifications.create("subject0123")
ntf1 = nc_app.notifications.by_object_id(obj_id1)
ntf2 = nc_app.notifications.by_object_id(obj_id2)
ntf1_2 = nc_app.notifications.get_one(ntf1.notification_id)
ntf2_2 = nc_app.notifications.get_one(ntf2.notification_id)
assert ntf1 == ntf1_2
assert ntf2 == ntf2_2
@pytest.mark.asyncio(scope="session")
async def test_get_one_async(anc_app):
await anc_app.notifications.delete_all()
obj_id1 = await anc_app.notifications.create("subject0123")
obj_id2 = await anc_app.notifications.create("subject0123")
ntf1 = await anc_app.notifications.by_object_id(obj_id1)
ntf2 = await anc_app.notifications.by_object_id(obj_id2)
ntf1_2 = await anc_app.notifications.get_one(ntf1.notification_id)
ntf2_2 = await anc_app.notifications.get_one(ntf2.notification_id)
assert ntf1 == ntf1_2
assert ntf2 == ntf2_2
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/loginflow_v2_test.py 0000664 0002322 0002322 00000001431 14766056032 026621 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudException
def test_init_poll(nc_client):
lf = nc_client.loginflow_v2.init()
assert isinstance(lf.endpoint, str)
assert isinstance(lf.login, str)
assert isinstance(lf.token, str)
with pytest.raises(NextcloudException) as exc_info:
nc_client.loginflow_v2.poll(lf.token, 1)
assert exc_info.value.status_code == 404
@pytest.mark.asyncio(scope="session")
async def test_init_poll_async(anc_client):
lf = await anc_client.loginflow_v2.init()
assert isinstance(lf.endpoint, str)
assert isinstance(lf.login, str)
assert isinstance(lf.token, str)
with pytest.raises(NextcloudException) as exc_info:
await anc_client.loginflow_v2.poll(lf.token, 1)
assert exc_info.value.status_code == 404
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/talk_test.py 0000664 0002322 0002322 00000070205 14766056032 025152 0 ustar debalance debalance from io import BytesIO
from os import environ
import pytest
from PIL import Image
from nc_py_api import AsyncNextcloud, Nextcloud, NextcloudException, files, talk
def test_conversation_create_delete(nc):
if nc.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = nc.talk.create_conversation(talk.ConversationType.GROUP, "admin")
nc.talk.delete_conversation(conversation)
assert isinstance(conversation.conversation_id, int)
assert isinstance(conversation.token, str) and conversation.token
assert isinstance(conversation.name, str)
assert isinstance(conversation.conversation_type, talk.ConversationType)
assert isinstance(conversation.display_name, str)
assert isinstance(conversation.description, str)
assert isinstance(conversation.participant_type, talk.ParticipantType)
assert isinstance(conversation.attendee_id, int)
assert isinstance(conversation.attendee_pin, str)
assert isinstance(conversation.actor_type, str)
assert isinstance(conversation.actor_id, str)
assert isinstance(conversation.permissions, talk.AttendeePermissions)
assert isinstance(conversation.attendee_permissions, talk.AttendeePermissions)
assert isinstance(conversation.call_permissions, talk.AttendeePermissions)
assert isinstance(conversation.default_permissions, talk.AttendeePermissions)
assert isinstance(conversation.participant_flags, talk.InCallFlags)
assert isinstance(conversation.read_only, bool)
assert isinstance(conversation.listable, talk.ListableScope)
assert isinstance(conversation.message_expiration, int)
assert isinstance(conversation.has_password, bool)
assert isinstance(conversation.has_call, bool)
assert isinstance(conversation.call_flag, talk.InCallFlags)
assert isinstance(conversation.can_start_call, bool)
assert isinstance(conversation.can_delete_conversation, bool)
assert isinstance(conversation.can_leave_conversation, bool)
assert isinstance(conversation.last_activity, int)
assert isinstance(conversation.is_favorite, bool)
assert isinstance(conversation.notification_level, talk.NotificationLevel)
assert isinstance(conversation.lobby_state, talk.WebinarLobbyStates)
assert isinstance(conversation.lobby_timer, int)
assert isinstance(conversation.sip_enabled, talk.SipEnabledStatus)
assert isinstance(conversation.can_enable_sip, bool)
assert isinstance(conversation.unread_messages_count, int)
assert isinstance(conversation.unread_mention, bool)
assert isinstance(conversation.unread_mention_direct, bool)
assert isinstance(conversation.last_read_message, int)
assert isinstance(conversation.last_message, talk.TalkMessage) or conversation.last_message is None
assert isinstance(conversation.last_common_read_message, int)
assert isinstance(conversation.breakout_room_mode, talk.BreakoutRoomMode)
assert isinstance(conversation.breakout_room_status, talk.BreakoutRoomStatus)
assert isinstance(conversation.avatar_version, str)
assert isinstance(conversation.is_custom_avatar, bool)
assert isinstance(conversation.call_start_time, int)
assert isinstance(conversation.recording_status, talk.CallRecordingStatus)
assert isinstance(conversation.status_type, str)
assert isinstance(conversation.status_message, str)
assert isinstance(conversation.status_icon, str)
assert isinstance(conversation.status_clear_at, int) or conversation.status_clear_at is None
if conversation.last_message is None:
return
talk_msg = conversation.last_message
assert isinstance(talk_msg.message_id, int)
assert isinstance(talk_msg.token, str)
assert talk_msg.actor_type in ("users", "guests", "bots", "bridged")
assert isinstance(talk_msg.actor_id, str)
assert isinstance(talk_msg.actor_display_name, str)
assert isinstance(talk_msg.timestamp, int)
assert isinstance(talk_msg.system_message, str)
assert talk_msg.message_type in ("comment", "comment_deleted", "system", "command")
assert talk_msg.is_replyable is False
assert isinstance(talk_msg.reference_id, str)
assert isinstance(talk_msg.message, str)
assert isinstance(talk_msg.message_parameters, dict)
assert isinstance(talk_msg.expiration_timestamp, int)
assert isinstance(talk_msg.parent, list)
assert isinstance(talk_msg.reactions, dict)
assert isinstance(talk_msg.reactions_self, list)
assert isinstance(talk_msg.markdown, bool)
def test_get_conversations_modified_since(nc):
if nc.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = nc.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
conversations = nc.talk.get_user_conversations()
assert conversations
nc.talk.modified_since += 2 # read notes for ``modified_since`` param in docs.
conversations = nc.talk.get_user_conversations(modified_since=True)
assert not conversations
conversations = nc.talk.get_user_conversations(modified_since=9992708529, no_status_update=False)
assert not conversations
finally:
nc.talk.delete_conversation(conversation.token)
@pytest.mark.asyncio(scope="session")
async def test_get_conversations_modified_since_async(anc):
if await anc.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = await anc.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
conversations = await anc.talk.get_user_conversations()
assert conversations
anc.talk.modified_since += 2 # read notes for ``modified_since`` param in docs.
conversations = await anc.talk.get_user_conversations(modified_since=True)
assert not conversations
conversations = await anc.talk.get_user_conversations(modified_since=9992708529, no_status_update=False)
assert not conversations
finally:
await anc.talk.delete_conversation(conversation.token)
def _test_get_conversations_include_status(participants: list[talk.Participant]):
assert len(participants) == 2
second_participant = next(i for i in participants if i.actor_id == environ["TEST_USER_ID"])
assert second_participant.actor_type == "users"
assert isinstance(second_participant.attendee_id, int)
assert isinstance(second_participant.display_name, str)
assert isinstance(second_participant.participant_type, talk.ParticipantType)
assert isinstance(second_participant.last_ping, int)
assert second_participant.participant_flags == talk.InCallFlags.DISCONNECTED
assert isinstance(second_participant.permissions, talk.AttendeePermissions)
assert isinstance(second_participant.attendee_permissions, talk.AttendeePermissions)
assert isinstance(second_participant.session_ids, list)
assert isinstance(second_participant.breakout_token, str)
assert second_participant.status_message == ""
assert str(second_participant).find("last_ping=") != -1
def test_get_conversations_include_status(nc, nc_client):
if nc.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
nc_second_user = Nextcloud(nc_auth_user=environ["TEST_USER_ID"], nc_auth_pass=environ["TEST_USER_PASS"])
nc_second_user.user_status.set_status_type("away")
nc_second_user.user_status.set_status("my status message", status_icon="😇")
conversation = nc.talk.create_conversation(talk.ConversationType.ONE_TO_ONE, environ["TEST_USER_ID"])
try:
conversations = nc.talk.get_user_conversations(include_status=False)
assert conversations
first_conv = next(i for i in conversations if i.conversation_id == conversation.conversation_id)
assert not first_conv.status_type
conversations = nc.talk.get_user_conversations(include_status=True)
assert conversations
first_conv = next(i for i in conversations if i.conversation_id == conversation.conversation_id)
assert first_conv.status_type == "away"
assert first_conv.status_message == "my status message"
assert first_conv.status_icon == "😇"
participants = nc.talk.list_participants(first_conv)
_test_get_conversations_include_status(participants)
participants = nc.talk.list_participants(first_conv, include_status=True)
assert len(participants) == 2
second_participant = next(i for i in participants if i.actor_id == environ["TEST_USER_ID"])
assert second_participant.status_message == "my status message"
assert str(conversation).find("type=") != -1
finally:
nc.talk.leave_conversation(conversation.token)
@pytest.mark.asyncio(scope="session")
async def test_get_conversations_include_status_async(anc, anc_client):
if await anc.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
nc_second_user = Nextcloud(nc_auth_user=environ["TEST_USER_ID"], nc_auth_pass=environ["TEST_USER_PASS"])
nc_second_user.user_status.set_status_type("away")
nc_second_user.user_status.set_status("my status message-async", status_icon="😇")
conversation = await anc.talk.create_conversation(talk.ConversationType.ONE_TO_ONE, environ["TEST_USER_ID"])
try:
conversations = await anc.talk.get_user_conversations(include_status=False)
assert conversations
first_conv = next(i for i in conversations if i.conversation_id == conversation.conversation_id)
assert not first_conv.status_type
conversations = await anc.talk.get_user_conversations(include_status=True)
assert conversations
first_conv = next(i for i in conversations if i.conversation_id == conversation.conversation_id)
assert first_conv.status_type == "away"
assert first_conv.status_message == "my status message-async"
assert first_conv.status_icon == "😇"
participants = await anc.talk.list_participants(first_conv)
_test_get_conversations_include_status(participants)
participants = await anc.talk.list_participants(first_conv, include_status=True)
assert len(participants) == 2
second_participant = next(i for i in participants if i.actor_id == environ["TEST_USER_ID"])
assert second_participant.status_message == "my status message-async"
assert str(conversation).find("type=") != -1
finally:
await anc.talk.leave_conversation(conversation.token)
def test_rename_description_favorite_get_conversation(nc_any):
if nc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = nc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
nc_any.talk.rename_conversation(conversation, "new era")
assert conversation.is_favorite is False
nc_any.talk.set_conversation_description(conversation, "the description")
nc_any.talk.set_conversation_fav(conversation, True)
nc_any.talk.set_conversation_readonly(conversation, True)
nc_any.talk.set_conversation_public(conversation, True)
nc_any.talk.set_conversation_notify_lvl(conversation, talk.NotificationLevel.NEVER_NOTIFY)
nc_any.talk.set_conversation_password(conversation, "zJf4aLafv8941nvs")
conversation = nc_any.talk.get_conversation_by_token(conversation)
assert conversation.display_name == "new era"
assert conversation.description == "the description"
assert conversation.is_favorite is True
assert conversation.read_only is True
assert conversation.notification_level == talk.NotificationLevel.NEVER_NOTIFY
assert conversation.has_password is True
nc_any.talk.set_conversation_fav(conversation, False)
nc_any.talk.set_conversation_readonly(conversation, False)
nc_any.talk.set_conversation_password(conversation, "")
nc_any.talk.set_conversation_public(conversation, False)
conversation = nc_any.talk.get_conversation_by_token(conversation)
assert conversation.is_favorite is False
assert conversation.read_only is False
assert conversation.has_password is False
finally:
nc_any.talk.delete_conversation(conversation)
@pytest.mark.asyncio(scope="session")
async def test_rename_description_favorite_get_conversation_async(anc_any):
if await anc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = await anc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
await anc_any.talk.rename_conversation(conversation, "new era")
assert conversation.is_favorite is False
await anc_any.talk.set_conversation_description(conversation, "the description")
await anc_any.talk.set_conversation_fav(conversation, True)
await anc_any.talk.set_conversation_readonly(conversation, True)
await anc_any.talk.set_conversation_public(conversation, True)
await anc_any.talk.set_conversation_notify_lvl(conversation, talk.NotificationLevel.NEVER_NOTIFY)
await anc_any.talk.set_conversation_password(conversation, "zJf4aLafv8941nvs")
conversation = await anc_any.talk.get_conversation_by_token(conversation)
assert conversation.display_name == "new era"
assert conversation.description == "the description"
assert conversation.is_favorite is True
assert conversation.read_only is True
assert conversation.notification_level == talk.NotificationLevel.NEVER_NOTIFY
assert conversation.has_password is True
await anc_any.talk.set_conversation_fav(conversation, False)
await anc_any.talk.set_conversation_readonly(conversation, False)
await anc_any.talk.set_conversation_password(conversation, "")
await anc_any.talk.set_conversation_public(conversation, False)
conversation = await anc_any.talk.get_conversation_by_token(conversation)
assert conversation.is_favorite is False
assert conversation.read_only is False
assert conversation.has_password is False
finally:
await anc_any.talk.delete_conversation(conversation)
def test_message_send_delete_reactions(nc_any):
if nc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = nc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
msg = nc_any.talk.send_message("yo yo yo!", conversation)
reactions = nc_any.talk.react_to_message(msg, "❤️")
assert "❤️" in reactions
assert len(reactions["❤️"]) == 1
reaction = reactions["❤️"][0]
assert reaction.actor_id == nc_any.user
assert reaction.actor_type == "users"
assert reaction.actor_display_name
assert isinstance(reaction.timestamp, int)
reactions2 = nc_any.talk.get_message_reactions(msg)
assert reactions == reactions2
nc_any.talk.react_to_message(msg, "☝️️")
assert nc_any.talk.delete_reaction(msg, "❤️")
assert not nc_any.talk.delete_reaction(msg, "☝️️")
assert not nc_any.talk.get_message_reactions(msg)
result = nc_any.talk.delete_message(msg)
assert result.system_message == "message_deleted"
messages = nc_any.talk.receive_messages(conversation)
deleted = [i for i in messages if i.system_message == "message_deleted"]
assert deleted
assert str(deleted[0]).find("time=") != -1
finally:
nc_any.talk.delete_conversation(conversation)
@pytest.mark.asyncio(scope="session")
async def test_message_send_delete_reactions_async(anc_any):
if await anc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = await anc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
msg = await anc_any.talk.send_message("yo yo yo!", conversation)
reactions = await anc_any.talk.react_to_message(msg, "❤️")
assert "❤️" in reactions
assert len(reactions["❤️"]) == 1
reaction = reactions["❤️"][0]
assert reaction.actor_id == await anc_any.user
assert reaction.actor_type == "users"
assert reaction.actor_display_name
assert isinstance(reaction.timestamp, int)
reactions2 = await anc_any.talk.get_message_reactions(msg)
assert reactions == reactions2
await anc_any.talk.react_to_message(msg, "☝️️")
assert await anc_any.talk.delete_reaction(msg, "❤️")
assert not await anc_any.talk.delete_reaction(msg, "☝️️")
assert not await anc_any.talk.get_message_reactions(msg)
result = await anc_any.talk.delete_message(msg)
assert result.system_message == "message_deleted"
messages = await anc_any.talk.receive_messages(conversation)
deleted = [i for i in messages if i.system_message == "message_deleted"]
assert deleted
assert str(deleted[0]).find("time=") != -1
finally:
await anc_any.talk.delete_conversation(conversation)
def _test_create_close_poll(poll: talk.Poll, closed: bool, user: str, conversation_token: str):
assert isinstance(poll.poll_id, int)
assert poll.question == "When was this test written?"
assert poll.options == ["2000", "2023", "2030"]
assert poll.max_votes == 1
assert poll.num_voters == 0
assert poll.hidden_results is True
assert poll.details == []
assert poll.closed is closed
assert poll.conversation_token == conversation_token
assert poll.actor_type == "users"
assert poll.actor_id == user
assert isinstance(poll.actor_display_name, str)
assert poll.voted_self == []
assert poll.votes == []
def test_create_close_poll(nc_any):
if nc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = nc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
poll = nc_any.talk.create_poll(conversation, "When was this test written?", ["2000", "2023", "2030"])
assert str(poll).find("author=") != -1
_test_create_close_poll(poll, False, nc_any.user, conversation.token)
poll = nc_any.talk.get_poll(poll)
_test_create_close_poll(poll, False, nc_any.user, conversation.token)
poll = nc_any.talk.get_poll(poll.poll_id, conversation.token)
_test_create_close_poll(poll, False, nc_any.user, conversation.token)
poll = nc_any.talk.close_poll(poll.poll_id, conversation.token)
_test_create_close_poll(poll, True, nc_any.user, conversation.token)
finally:
nc_any.talk.delete_conversation(conversation)
@pytest.mark.asyncio(scope="session")
async def test_create_close_poll_async(anc_any):
if await anc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = await anc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
poll = await anc_any.talk.create_poll(conversation, "When was this test written?", ["2000", "2023", "2030"])
assert str(poll).find("author=") != -1
_test_create_close_poll(poll, False, await anc_any.user, conversation.token)
poll = await anc_any.talk.get_poll(poll)
_test_create_close_poll(poll, False, await anc_any.user, conversation.token)
poll = await anc_any.talk.get_poll(poll.poll_id, conversation.token)
_test_create_close_poll(poll, False, await anc_any.user, conversation.token)
poll = await anc_any.talk.close_poll(poll.poll_id, conversation.token)
_test_create_close_poll(poll, True, await anc_any.user, conversation.token)
finally:
await anc_any.talk.delete_conversation(conversation)
def test_vote_poll(nc_any):
if nc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = nc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
poll = nc_any.talk.create_poll(
conversation, "what color is the grass", ["red", "green", "blue"], hidden_results=False, max_votes=3
)
assert poll.hidden_results is False
assert not poll.voted_self
poll = nc_any.talk.vote_poll([0, 2], poll)
assert poll.voted_self == [0, 2]
assert poll.votes == {
"option-0": 1,
"option-2": 1,
}
assert poll.num_voters == 1
poll = nc_any.talk.vote_poll([1], poll.poll_id, conversation)
assert poll.voted_self == [1]
assert poll.votes == {
"option-1": 1,
}
poll = nc_any.talk.close_poll(poll)
assert poll.closed is True
assert len(poll.details) == 1
assert poll.details[0].actor_id == nc_any.user
assert poll.details[0].actor_type == "users"
assert poll.details[0].option == 1
assert isinstance(poll.details[0].actor_display_name, str)
assert str(poll.details[0]).find("actor=") != -1
finally:
nc_any.talk.delete_conversation(conversation)
@pytest.mark.asyncio(scope="session")
async def test_vote_poll_async(anc_any):
if await anc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = await anc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
poll = await anc_any.talk.create_poll(
conversation, "what color is the grass", ["red", "green", "blue"], hidden_results=False, max_votes=3
)
assert poll.hidden_results is False
assert not poll.voted_self
poll = await anc_any.talk.vote_poll([0, 2], poll)
assert poll.voted_self == [0, 2]
assert poll.votes == {
"option-0": 1,
"option-2": 1,
}
assert poll.num_voters == 1
poll = await anc_any.talk.vote_poll([1], poll.poll_id, conversation)
assert poll.voted_self == [1]
assert poll.votes == {
"option-1": 1,
}
poll = await anc_any.talk.close_poll(poll)
assert poll.closed is True
assert len(poll.details) == 1
assert poll.details[0].actor_id == await anc_any.user
assert poll.details[0].actor_type == "users"
assert poll.details[0].option == 1
assert isinstance(poll.details[0].actor_display_name, str)
assert str(poll.details[0]).find("actor=") != -1
finally:
await anc_any.talk.delete_conversation(conversation)
def test_conversation_avatar(nc_any):
if nc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = nc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
assert conversation.is_custom_avatar is False
r = nc_any.talk.get_conversation_avatar(conversation)
assert isinstance(r, bytes)
im = Image.effect_mandelbrot((512, 512), (-3, -2.5, 2, 2.5), 100)
buffer = BytesIO()
im.save(buffer, format="PNG")
buffer.seek(0)
r = nc_any.talk.set_conversation_avatar(conversation, buffer.read())
assert r.is_custom_avatar is True
r = nc_any.talk.get_conversation_avatar(conversation)
assert isinstance(r, bytes)
r = nc_any.talk.delete_conversation_avatar(conversation)
assert r.is_custom_avatar is False
r = nc_any.talk.set_conversation_avatar(conversation, ("🫡", None))
assert r.is_custom_avatar is True
r = nc_any.talk.get_conversation_avatar(conversation, dark=True)
assert isinstance(r, bytes)
with pytest.raises(NextcloudException):
nc_any.talk.get_conversation_avatar("not_exist_conversation")
finally:
nc_any.talk.delete_conversation(conversation)
@pytest.mark.asyncio(scope="session")
async def test_conversation_avatar_async(anc_any):
if await anc_any.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
conversation = await anc_any.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
assert conversation.is_custom_avatar is False
r = await anc_any.talk.get_conversation_avatar(conversation)
assert isinstance(r, bytes)
im = Image.effect_mandelbrot((512, 512), (-3, -2.5, 2, 2.5), 100)
buffer = BytesIO()
im.save(buffer, format="PNG")
buffer.seek(0)
r = await anc_any.talk.set_conversation_avatar(conversation, buffer.read())
assert r.is_custom_avatar is True
r = await anc_any.talk.get_conversation_avatar(conversation)
assert isinstance(r, bytes)
r = await anc_any.talk.delete_conversation_avatar(conversation)
assert r.is_custom_avatar is False
r = await anc_any.talk.set_conversation_avatar(conversation, ("🫡", None))
assert r.is_custom_avatar is True
r = await anc_any.talk.get_conversation_avatar(conversation, dark=True)
assert isinstance(r, bytes)
with pytest.raises(NextcloudException):
await anc_any.talk.get_conversation_avatar("not_exist_conversation")
finally:
await anc_any.talk.delete_conversation(conversation)
def test_send_receive_file(nc_client):
if nc_client.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
nc_second_user = Nextcloud(nc_auth_user=environ["TEST_USER_ID"], nc_auth_pass=environ["TEST_USER_PASS"])
conversation = nc_client.talk.create_conversation(talk.ConversationType.ONE_TO_ONE, environ["TEST_USER_ID"])
try:
r, reference_id = nc_client.talk.send_file("/test_dir/subdir/test_12345_text.txt", conversation)
assert isinstance(reference_id, str)
assert isinstance(r, files.Share)
for _ in range(10):
m = nc_second_user.talk.receive_messages(conversation, limit=1)
if m and isinstance(m[0], talk.TalkFileMessage):
break
m_t: talk.TalkFileMessage = m[0] # noqa
fs_node = m_t.to_fs_node()
assert nc_second_user.files.download(fs_node) == b"12345"
assert m_t.reference_id == reference_id
assert fs_node.is_dir is False
# test with directory
directory = nc_client.files.by_path("/test_dir/subdir/")
r, reference_id = nc_client.talk.send_file(directory, conversation)
assert isinstance(reference_id, str)
assert isinstance(r, files.Share)
for _ in range(10):
m = nc_second_user.talk.receive_messages(conversation, limit=1)
if m and m[0].reference_id == reference_id:
break
m_t: talk.TalkFileMessage = m[0] # noqa
assert m_t.reference_id == reference_id
fs_node = m_t.to_fs_node()
assert fs_node.is_dir is True
finally:
nc_client.talk.leave_conversation(conversation.token)
@pytest.mark.asyncio(scope="session")
async def test_send_receive_file_async(anc_client):
if await anc_client.talk.available is False:
pytest.skip("Nextcloud Talk is not installed")
nc_second_user = AsyncNextcloud(nc_auth_user=environ["TEST_USER_ID"], nc_auth_pass=environ["TEST_USER_PASS"])
conversation = await anc_client.talk.create_conversation(talk.ConversationType.ONE_TO_ONE, environ["TEST_USER_ID"])
try:
r, reference_id = await anc_client.talk.send_file("/test_dir/test_12345_text.txt", conversation)
assert isinstance(reference_id, str)
assert isinstance(r, files.Share)
for _ in range(10):
m = await nc_second_user.talk.receive_messages(conversation, limit=1)
if m and isinstance(m[0], talk.TalkFileMessage):
break
m_t: talk.TalkFileMessage = m[0] # noqa
fs_node = m_t.to_fs_node()
assert await nc_second_user.files.download(fs_node) == b"12345"
assert m_t.reference_id == reference_id
assert fs_node.is_dir is False
# test with directory
directory = await anc_client.files.by_path("/test_dir/")
r, reference_id = await anc_client.talk.send_file(directory, conversation)
assert isinstance(reference_id, str)
assert isinstance(r, files.Share)
for _ in range(10):
m = await nc_second_user.talk.receive_messages(conversation, limit=1)
if m and m[0].reference_id == reference_id:
break
m_t: talk.TalkFileMessage = m[0] # noqa
assert m_t.reference_id == reference_id
fs_node = m_t.to_fs_node()
assert fs_node.is_dir is True
finally:
await anc_client.talk.leave_conversation(conversation.token)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/user_status_test.py 0000664 0002322 0002322 00000017270 14766056032 026603 0 ustar debalance debalance from time import time
import pytest
from nc_py_api.user_status import (
ClearAt,
CurrentUserStatus,
PredefinedStatus,
UserStatus,
)
def test_available(nc):
assert nc.user_status.available
@pytest.mark.asyncio(scope="session")
async def test_available_async(anc):
assert await anc.user_status.available
def compare_user_statuses(p1: UserStatus, p2: UserStatus):
assert p1.user_id == p2.user_id
assert p1.status_message == p2.status_message
assert p1.status_icon == p2.status_icon
assert p1.status_clear_at == p2.status_clear_at
assert p1.status_type == p2.status_type
def _test_get_status(r1: CurrentUserStatus, message):
assert r1.user_id == "admin"
assert r1.status_icon is None
assert r1.status_clear_at is None
if message == "":
message = None
assert r1.status_message == message
assert r1.status_id is None
assert not r1.message_predefined
assert str(r1).find("status_id=") != -1
@pytest.mark.parametrize("message", ("1 2 3", None, ""))
def test_get_status(nc, message):
nc.user_status.set_status(message)
r1 = nc.user_status.get_current()
r2 = nc.user_status.get(nc.user)
compare_user_statuses(r1, r2)
_test_get_status(r1, message)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("message", ("1 2 3", None, ""))
async def test_get_status_async(anc, message):
await anc.user_status.set_status(message)
r1 = await anc.user_status.get_current()
r2 = await anc.user_status.get(await anc.user)
compare_user_statuses(r1, r2)
_test_get_status(r1, message)
def test_get_status_non_existent_user(nc):
assert nc.user_status.get("no such user") is None
@pytest.mark.asyncio(scope="session")
async def test_get_status_non_existent_user_async(anc):
assert await anc.user_status.get("no such user") is None
def _test_get_predefined(r: list[PredefinedStatus]):
assert isinstance(r, list)
assert r
for i in r:
assert isinstance(i.status_id, str)
assert isinstance(i.message, str)
assert isinstance(i.icon, str)
assert isinstance(i.clear_at, ClearAt) or i.clear_at is None
def test_get_predefined(nc):
r = nc.user_status.get_predefined()
if nc.srv_version["major"] < 27:
assert r == []
else:
_test_get_predefined(r)
@pytest.mark.asyncio(scope="session")
async def test_get_predefined_async(anc):
r = await anc.user_status.get_predefined()
if (await anc.srv_version)["major"] < 27:
assert r == []
else:
_test_get_predefined(r)
def test_get_list(nc):
r_all = nc.user_status.get_list()
assert r_all
assert isinstance(r_all, list)
r_current = nc.user_status.get_current()
for i in r_all:
if i.user_id == nc.user:
compare_user_statuses(i, r_current)
assert str(i).find("status_type=") != -1
@pytest.mark.asyncio(scope="session")
async def test_get_list_async(anc):
r_all = await anc.user_status.get_list()
assert r_all
assert isinstance(r_all, list)
r_current = await anc.user_status.get_current()
for i in r_all:
if i.user_id == await anc.user:
compare_user_statuses(i, r_current)
assert str(i).find("status_type=") != -1
def test_set_status(nc):
time_clear = int(time()) + 60
nc.user_status.set_status("cool status", time_clear)
r = nc.user_status.get_current()
assert r.status_message == "cool status"
assert r.status_clear_at == time_clear
assert r.status_icon is None
nc.user_status.set_status("Sick!", status_icon="🤒")
r = nc.user_status.get_current()
assert r.status_message == "Sick!"
assert r.status_clear_at is None
assert r.status_icon == "🤒"
nc.user_status.set_status(None)
r = nc.user_status.get_current()
assert r.status_message is None
assert r.status_clear_at is None
assert r.status_icon is None
@pytest.mark.asyncio(scope="session")
async def test_set_status_async(anc):
time_clear = int(time()) + 60
await anc.user_status.set_status("cool status", time_clear)
r = await anc.user_status.get_current()
assert r.status_message == "cool status"
assert r.status_clear_at == time_clear
assert r.status_icon is None
await anc.user_status.set_status("Sick!", status_icon="🤒")
r = await anc.user_status.get_current()
assert r.status_message == "Sick!"
assert r.status_clear_at is None
assert r.status_icon == "🤒"
await anc.user_status.set_status(None)
r = await anc.user_status.get_current()
assert r.status_message is None
assert r.status_clear_at is None
assert r.status_icon is None
@pytest.mark.parametrize("value", ("online", "away", "dnd", "invisible", "offline"))
def test_set_status_type(nc, value):
nc.user_status.set_status_type(value)
r = nc.user_status.get_current()
assert r.status_type == value
assert r.status_type_defined
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("value", ("online", "away", "dnd", "invisible", "offline"))
async def test_set_status_type_async(anc, value):
await anc.user_status.set_status_type(value)
r = await anc.user_status.get_current()
assert r.status_type == value
assert r.status_type_defined
@pytest.mark.parametrize("clear_at", (None, int(time()) + 60 * 60 * 9))
def test_set_predefined(nc, clear_at):
if nc.srv_version["major"] < 27:
nc.user_status.set_predefined("meeting")
else:
predefined_statuses = nc.user_status.get_predefined()
for i in predefined_statuses:
nc.user_status.set_predefined(i.status_id, clear_at)
r = nc.user_status.get_current()
assert r.status_message == i.message
assert r.status_id == i.status_id
assert r.message_predefined
assert r.status_clear_at == clear_at
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("clear_at", (None, int(time()) + 60 * 60 * 9))
async def test_set_predefined_async(anc, clear_at):
if (await anc.srv_version)["major"] < 27:
await anc.user_status.set_predefined("meeting")
else:
predefined_statuses = await anc.user_status.get_predefined()
for i in predefined_statuses:
await anc.user_status.set_predefined(i.status_id, clear_at)
r = await anc.user_status.get_current()
assert r.status_message == i.message
assert r.status_id == i.status_id
assert r.message_predefined
assert r.status_clear_at == clear_at
def test_get_back_status_from_from_empty_user(nc_app):
orig_user = nc_app._session.user
nc_app._session.set_user("")
try:
with pytest.raises(ValueError):
nc_app.user_status.get_backup_status("")
finally:
nc_app._session.set_user(orig_user)
@pytest.mark.asyncio(scope="session")
async def test_get_back_status_from_from_empty_user_async(anc_app):
orig_user = await anc_app._session.user
anc_app._session.set_user("")
try:
with pytest.raises(ValueError):
await anc_app.user_status.get_backup_status("")
finally:
anc_app._session.set_user(orig_user)
def test_get_back_status_from_from_non_exist_user(nc):
assert nc.user_status.get_backup_status("mёm_m-m.l") is None
@pytest.mark.asyncio(scope="session")
async def test_get_back_status_from_from_non_exist_user_async(anc):
assert await anc.user_status.get_backup_status("mёm_m-m.l") is None
def test_restore_from_non_existing_back_status(nc):
assert nc.user_status.restore_backup_status("no such backup status") is None
@pytest.mark.asyncio(scope="session")
async def test_restore_from_non_existing_back_status_async(anc):
assert await anc.user_status.restore_backup_status("no such backup status") is None
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/calendar_test.py 0000664 0002322 0002322 00000001455 14766056032 025771 0 ustar debalance debalance import datetime
import pytest
def test_create_delete(nc):
if nc.cal.available is False:
pytest.skip("caldav package is not installed")
principal = nc.cal.principal()
calendar = principal.make_calendar("test_nc_py_api")
try:
calendars = principal.calendars()
assert calendars
all_events_before = calendar.events()
event = calendar.save_event(
dtstart=datetime.datetime.now(),
dtend=datetime.datetime.now() + datetime.timedelta(hours=1),
summary="NcPyApi + CalDAV test",
)
all_events_after = calendar.events()
assert len(all_events_after) == len(all_events_before) + 1
event.delete()
assert len(calendar.events()) == len(all_events_before)
finally:
calendar.delete()
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/misc_test.py 0000664 0002322 0002322 00000017562 14766056032 025161 0 ustar debalance debalance import datetime
import io
import os
import pytest
from httpx import Request, Response
from nc_py_api import (
AsyncNextcloud,
AsyncNextcloudApp,
Nextcloud,
NextcloudApp,
NextcloudException,
ex_app,
)
from nc_py_api._deffered_error import DeferredError # noqa
from nc_py_api._exceptions import check_error # noqa
from nc_py_api._misc import nc_iso_time_to_datetime, require_capabilities # noqa
from nc_py_api._session import BasicConfig # noqa
@pytest.mark.parametrize("code", (995, 996, 997, 998, 999, 1000))
def test_check_error(code):
if 996 <= code <= 999:
with pytest.raises(NextcloudException):
check_error(Response(code, request=Request(method="GET", url="https://example")))
else:
check_error(Response(code, request=Request(method="GET", url="https://example")))
def test_nc_exception_to_str():
reason = "this is a reason"
info = "some info"
try:
raise NextcloudException(status_code=666, reason=reason, info=info)
except NextcloudException as e:
assert str(e) == f"[666] {reason} <{info}>"
def test_require_capabilities(nc_app):
require_capabilities("app_api", nc_app.capabilities)
require_capabilities(["app_api", "theming"], nc_app.capabilities)
with pytest.raises(NextcloudException):
require_capabilities("non_exist_capability", nc_app.capabilities)
with pytest.raises(NextcloudException):
require_capabilities(["non_exist_capability", "app_api"], nc_app.capabilities)
with pytest.raises(NextcloudException):
require_capabilities(["non_exist_capability", "non_exist_capability2", "app_api"], nc_app.capabilities)
with pytest.raises(NextcloudException):
require_capabilities("app_api.non_exist_capability", nc_app.capabilities)
def test_config_get_value():
BasicConfig()._get_config_value("non_exist_value", raise_not_found=False)
with pytest.raises(ValueError):
BasicConfig()._get_config_value("non_exist_value")
assert BasicConfig()._get_config_value("non_exist_value", non_exist_value=123) == 123
def test_deffered_error():
try:
import unknown_non_exist_module
except ImportError as ex:
unknown_non_exist_module = DeferredError(ex)
with pytest.raises(ModuleNotFoundError):
unknown_non_exist_module.some_class_or_func()
def test_response_headers(nc):
old_headers = nc.response_headers
nc.users.get_user(nc.user) # do not remove "nc.user" arguments, it helps to trigger response header updates.
assert old_headers != nc.response_headers
@pytest.mark.asyncio(scope="session")
async def test_response_headers_async(anc):
old_headers = anc.response_headers
await anc.users.get_user(await anc.user)
assert old_headers != anc.response_headers
def test_nc_iso_time_to_datetime():
parsed_time = nc_iso_time_to_datetime("invalid")
assert parsed_time == datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
def test_persist_transformers_cache(nc_app):
assert "TRANSFORMERS_CACHE" not in os.environ
from nc_py_api.ex_app import persist_transformers_cache # noqa
assert os.environ["TRANSFORMERS_CACHE"]
os.environ.pop("TRANSFORMERS_CACHE")
def test_verify_version(nc_app):
version_file_path = os.path.join(ex_app.persistent_storage(), "_version.info")
if os.path.exists(version_file_path):
os.remove(version_file_path)
r = ex_app.verify_version(False)
assert not os.path.getsize(version_file_path)
assert isinstance(r, tuple)
assert r[0] == ""
assert r[1] == os.environ["APP_VERSION"]
r = ex_app.verify_version(True)
assert os.path.getsize(version_file_path)
assert r[0] == ""
assert r[1] == os.environ["APP_VERSION"]
assert ex_app.verify_version() is None
def test_init_adapter_dav(nc_any):
new_nc = Nextcloud() if isinstance(nc_any, Nextcloud) else NextcloudApp()
new_nc._session.init_adapter_dav()
old_adapter = getattr(new_nc._session, "adapter_dav", None)
assert old_adapter is not None
new_nc._session.init_adapter_dav()
assert old_adapter == getattr(new_nc._session, "adapter_dav", None)
new_nc._session.init_adapter_dav(restart=True)
assert old_adapter != getattr(new_nc._session, "adapter_dav", None)
@pytest.mark.asyncio(scope="session")
async def test_init_adapter_dav_async(anc_any):
new_nc = AsyncNextcloud() if isinstance(anc_any, AsyncNextcloud) else AsyncNextcloudApp()
new_nc._session.init_adapter_dav()
old_adapter = getattr(new_nc._session, "adapter_dav", None)
assert old_adapter is not None
new_nc._session.init_adapter_dav()
assert old_adapter == getattr(new_nc._session, "adapter_dav", None)
new_nc._session.init_adapter_dav(restart=True)
assert old_adapter != getattr(new_nc._session, "adapter_dav", None)
def test_no_initial_connection(nc_any):
new_nc = Nextcloud() if isinstance(nc_any, Nextcloud) else NextcloudApp()
assert not new_nc._session._capabilities
_ = new_nc.srv_version
assert new_nc._session._capabilities
@pytest.mark.asyncio(scope="session")
async def test_no_initial_connection_async(anc_any):
new_nc = AsyncNextcloud() if isinstance(anc_any, AsyncNextcloud) else AsyncNextcloudApp()
assert not new_nc._session._capabilities
_ = await new_nc.srv_version
assert new_nc._session._capabilities
def test_ocs_timeout(nc_any):
new_nc = Nextcloud(npa_timeout=0.01) if isinstance(nc_any, Nextcloud) else NextcloudApp(npa_timeout=0.01)
with pytest.raises(NextcloudException) as e:
if new_nc.weather_status.set_location(latitude=41.896655, longitude=12.488776):
new_nc.weather_status.get_forecast()
if e.value.status_code in (500, 996):
pytest.skip("Some network problem on the host")
assert e.value.status_code == 408
@pytest.mark.asyncio(scope="session")
async def test_ocs_timeout_async(anc_any):
new_nc = (
AsyncNextcloud(npa_timeout=0.01) if isinstance(anc_any, AsyncNextcloud) else AsyncNextcloudApp(npa_timeout=0.01)
)
with pytest.raises(NextcloudException) as e:
if await new_nc.weather_status.set_location(latitude=41.896655, longitude=12.488776):
await new_nc.weather_status.get_forecast()
if e.value.status_code in (500, 996):
pytest.skip("Some network problem on the host")
assert e.value.status_code == 408
def test_public_ocs(nc_any):
r = nc_any.ocs("GET", "/ocs/v1.php/cloud/capabilities")
assert r == nc_any.ocs("GET", "ocs/v1.php/cloud/capabilities")
assert r == nc_any._session.ocs("GET", "ocs/v1.php/cloud/capabilities") # noqa
@pytest.mark.asyncio(scope="session")
async def test_public_ocs_async(anc_any):
r = await anc_any.ocs("GET", "/ocs/v1.php/cloud/capabilities")
assert r == await anc_any.ocs("GET", "ocs/v1.php/cloud/capabilities")
assert r == await anc_any._session.ocs("GET", "ocs/v1.php/cloud/capabilities") # noqa
def test_all_scope(nc_any):
r = nc_any.ocs("GET", "/ocs/v2.php/core/whatsnew")
assert isinstance(r, list)
@pytest.mark.asyncio(scope="session")
async def test_all_scope_async(anc_any):
r = await anc_any.ocs("GET", "/ocs/v2.php/core/whatsnew")
assert isinstance(r, list)
def test_perform_login(nc_any):
new_nc = Nextcloud() if isinstance(nc_any, Nextcloud) else NextcloudApp()
assert not new_nc._session._capabilities
new_nc.perform_login()
assert new_nc._session._capabilities
@pytest.mark.asyncio(scope="session")
async def test_perform_login_async(anc_any):
new_nc = AsyncNextcloud() if isinstance(anc_any, Nextcloud) else AsyncNextcloudApp()
assert not new_nc._session._capabilities
await new_nc.perform_login()
assert new_nc._session._capabilities
def test_download_log(nc_any):
buf = io.BytesIO()
nc_any.download_log(buf)
assert buf.tell() > 0
@pytest.mark.asyncio(scope="session")
async def test_download_log_async(anc_any):
buf = io.BytesIO()
await anc_any.download_log(buf)
assert buf.tell() > 0
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/weather_status_test.py 0000664 0002322 0002322 00000014440 14766056032 027260 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudException, weather_status
def test_available(nc):
assert nc.weather_status.available
@pytest.mark.asyncio(scope="session")
async def test_available_async(anc):
assert await anc.weather_status.available
def test_get_set_location(nc_any):
try:
nc_any.weather_status.set_location(longitude=0.0, latitude=0.0)
except NextcloudException as e:
if e.status_code in (408, 500, 996):
pytest.skip("Some network problem on the host")
raise e from None
loc = nc_any.weather_status.get_location()
assert loc.latitude == 0.0
assert loc.longitude == 0.0
assert isinstance(loc.address, str)
assert isinstance(loc.mode, int)
try:
assert nc_any.weather_status.set_location(address="Paris, 75007, France")
except NextcloudException as e:
if e.status_code in (500, 996):
pytest.skip("Some network problem on the host")
raise e from None
loc = nc_any.weather_status.get_location()
assert loc.latitude
assert loc.longitude
if loc.address.find("Unknown") != -1:
pytest.skip("Some network problem on the host")
assert loc.address.find("Paris") != -1
assert nc_any.weather_status.set_location(latitude=41.896655, longitude=12.488776)
loc = nc_any.weather_status.get_location()
assert loc.latitude == 41.896655
assert loc.longitude == 12.488776
if loc.address.find("Unknown") != -1:
pytest.skip("Some network problem on the host")
assert loc.address.find("Rom") != -1
@pytest.mark.asyncio(scope="session")
async def test_get_set_location_async(anc_any):
try:
await anc_any.weather_status.set_location(longitude=0.0, latitude=0.0)
except NextcloudException as e:
if e.status_code in (408, 500, 996):
pytest.skip("Some network problem on the host")
raise e from None
loc = await anc_any.weather_status.get_location()
assert loc.latitude == 0.0
assert loc.longitude == 0.0
assert isinstance(loc.address, str)
assert isinstance(loc.mode, int)
try:
assert await anc_any.weather_status.set_location(address="Paris, 75007, France")
except NextcloudException as e:
if e.status_code in (500, 996):
pytest.skip("Some network problem on the host")
raise e from None
loc = await anc_any.weather_status.get_location()
assert loc.latitude
assert loc.longitude
if loc.address.find("Unknown") != -1:
pytest.skip("Some network problem on the host")
assert loc.address.find("Paris") != -1
assert await anc_any.weather_status.set_location(latitude=41.896655, longitude=12.488776)
loc = await anc_any.weather_status.get_location()
assert loc.latitude == 41.896655
assert loc.longitude == 12.488776
if loc.address.find("Unknown") != -1:
pytest.skip("Some network problem on the host")
assert loc.address.find("Rom") != -1
def test_get_set_location_no_lat_lon_address(nc):
with pytest.raises(ValueError):
nc.weather_status.set_location()
@pytest.mark.asyncio(scope="session")
async def test_get_set_location_no_lat_lon_address_async(anc):
with pytest.raises(ValueError):
await anc.weather_status.set_location()
def test_get_forecast(nc_any):
nc_any.weather_status.set_location(latitude=41.896655, longitude=12.488776)
if nc_any.weather_status.get_location().address.find("Unknown") != -1:
pytest.skip("Some network problem on the host")
forecast = nc_any.weather_status.get_forecast()
assert isinstance(forecast, list)
assert forecast
assert isinstance(forecast[0], dict)
@pytest.mark.asyncio(scope="session")
async def test_get_forecast_async(anc_any):
await anc_any.weather_status.set_location(latitude=41.896655, longitude=12.488776)
if (await anc_any.weather_status.get_location()).address.find("Unknown") != -1:
pytest.skip("Some network problem on the host")
forecast = await anc_any.weather_status.get_forecast()
assert isinstance(forecast, list)
assert forecast
assert isinstance(forecast[0], dict)
def test_get_set_favorites(nc):
nc.weather_status.set_favorites([])
r = nc.weather_status.get_favorites()
assert isinstance(r, list)
assert not r
nc.weather_status.set_favorites(["Paris, France", "Madrid, Spain"])
r = nc.weather_status.get_favorites()
assert any("Paris" in x for x in r)
assert any("Madrid" in x for x in r)
@pytest.mark.asyncio(scope="session")
async def test_get_set_favorites_async(anc):
await anc.weather_status.set_favorites([])
r = await anc.weather_status.get_favorites()
assert isinstance(r, list)
assert not r
await anc.weather_status.set_favorites(["Paris, France", "Madrid, Spain"])
r = await anc.weather_status.get_favorites()
assert any("Paris" in x for x in r)
assert any("Madrid" in x for x in r)
def test_set_mode(nc):
nc.weather_status.set_mode(weather_status.WeatherLocationMode.MODE_BROWSER_LOCATION)
assert nc.weather_status.get_location().mode == weather_status.WeatherLocationMode.MODE_BROWSER_LOCATION.value
nc.weather_status.set_mode(weather_status.WeatherLocationMode.MODE_MANUAL_LOCATION)
assert nc.weather_status.get_location().mode == weather_status.WeatherLocationMode.MODE_MANUAL_LOCATION.value
@pytest.mark.asyncio(scope="session")
async def test_set_mode_async(anc):
await anc.weather_status.set_mode(weather_status.WeatherLocationMode.MODE_BROWSER_LOCATION)
assert (
await anc.weather_status.get_location()
).mode == weather_status.WeatherLocationMode.MODE_BROWSER_LOCATION.value
await anc.weather_status.set_mode(weather_status.WeatherLocationMode.MODE_MANUAL_LOCATION)
assert (
await anc.weather_status.get_location()
).mode == weather_status.WeatherLocationMode.MODE_MANUAL_LOCATION.value
def test_set_mode_invalid(nc):
with pytest.raises(ValueError):
nc.weather_status.set_mode(weather_status.WeatherLocationMode.UNKNOWN)
with pytest.raises(ValueError):
nc.weather_status.set_mode(0)
@pytest.mark.asyncio(scope="session")
async def test_set_mode_invalid_async(anc):
with pytest.raises(ValueError):
await anc.weather_status.set_mode(weather_status.WeatherLocationMode.UNKNOWN)
with pytest.raises(ValueError):
await anc.weather_status.set_mode(0)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/files_test.py 0000664 0002322 0002322 00000150561 14766056032 025325 0 ustar debalance debalance import contextlib
import math
import os
import zipfile
from datetime import datetime
from io import BytesIO
from pathlib import Path
from random import choice, randbytes
from string import ascii_lowercase
from tempfile import NamedTemporaryFile
from zlib import adler32
import pytest
from nc_py_api import (
FsNode,
LockType,
NextcloudException,
NextcloudExceptionNotFound,
SystemTag,
)
class MyBytesIO(BytesIO):
def __init__(self):
self.n_read_calls = 0
self.n_write_calls = 0
super().__init__()
def read(self, *args, **kwargs):
self.n_read_calls += 1
return super().read(*args, **kwargs)
def write(self, *args, **kwargs):
self.n_write_calls += 1
return super().write(*args, **kwargs)
def _test_list_user_root(user_root: list[FsNode], user: str):
assert user_root
for obj in user_root:
assert obj.user == user
assert obj.has_extra
assert obj.name
assert obj.user_path
assert obj.file_id
assert obj.etag
def test_list_user_root(nc):
user_root = nc.files.listdir()
_test_list_user_root(user_root, nc.user)
root_node = FsNode(full_path=f"files/{nc.user}/")
user_root2 = nc.files.listdir(root_node)
assert user_root == user_root2
@pytest.mark.asyncio(scope="session")
async def test_list_user_root_async(anc):
user_root = await anc.files.listdir()
_test_list_user_root(user_root, await anc.user)
root_node = FsNode(full_path=f"files/{await anc.user}/")
user_root2 = await anc.files.listdir(root_node)
assert user_root == user_root2
def _test_list_user_root_self_exclude(user_root: list[FsNode], user_root_with_self: list[FsNode], user: str):
assert len(user_root_with_self) == 1 + len(user_root)
self_res = next(i for i in user_root_with_self if not i.user_path)
for i in user_root:
assert self_res != i
assert self_res.has_extra
assert self_res.file_id
assert self_res.user == user
assert self_res.name
assert self_res.etag
assert self_res.full_path == f"files/{user}/"
def test_list_user_root_self_exclude(nc):
user_root = nc.files.listdir()
user_root_with_self = nc.files.listdir(exclude_self=False)
_test_list_user_root_self_exclude(user_root, user_root_with_self, nc.user)
@pytest.mark.asyncio(scope="session")
async def test_list_user_root_self_exclude_async(anc):
user_root = await anc.files.listdir()
user_root_with_self = await anc.files.listdir(exclude_self=False)
_test_list_user_root_self_exclude(user_root, user_root_with_self, await anc.user)
def _test_list_empty_dir(result: list[FsNode], user: str):
assert len(result)
result = result[0]
assert result.file_id
assert result.user == user
assert result.name == "test_empty_dir"
assert result.etag
assert result.full_path == f"files/{user}/test_empty_dir/"
def test_list_empty_dir(nc_any):
assert not len(nc_any.files.listdir("test_empty_dir"))
result = nc_any.files.listdir("test_empty_dir", exclude_self=False)
_test_list_empty_dir(result, nc_any.user)
@pytest.mark.asyncio(scope="session")
async def test_list_empty_dir_async(anc_any):
assert not len(await anc_any.files.listdir("test_empty_dir"))
result = await anc_any.files.listdir("test_empty_dir", exclude_self=False)
_test_list_empty_dir(result, await anc_any.user)
def test_list_spec_dir(nc_any):
r = nc_any.files.listdir("test_###_dir", exclude_self=False)
assert r[0].full_path.find("test_###_dir") != -1
@pytest.mark.asyncio(scope="session")
async def test_list_spec_dir_async(anc_any):
r = await anc_any.files.listdir("test_###_dir", exclude_self=False)
assert r[0].full_path.find("test_###_dir") != -1
def test_list_dir_wrong_args(nc_any):
with pytest.raises(ValueError):
nc_any.files.listdir(depth=0, exclude_self=True)
@pytest.mark.asyncio(scope="session")
async def test_list_dir_wrong_args_async(anc_any):
with pytest.raises(ValueError):
await anc_any.files.listdir(depth=0, exclude_self=True)
def _test_by_path(result: FsNode, result2: FsNode, user: str):
assert isinstance(result, FsNode)
assert isinstance(result2, FsNode)
assert result == result2
assert result.is_dir == result2.is_dir
assert result.is_dir
assert result.user == result2.user
assert result.user == user
def test_by_path(nc_any):
result = nc_any.files.by_path("")
result2 = nc_any.files.by_path("/")
_test_by_path(result, result2, nc_any.user)
@pytest.mark.asyncio(scope="session")
async def test_by_path_async(anc_any):
result = await anc_any.files.by_path("")
result2 = await anc_any.files.by_path("/")
_test_by_path(result, result2, await anc_any.user)
def test_file_download(nc_any):
assert nc_any.files.download("test_empty_text.txt") == b""
assert nc_any.files.download("/test_12345_text.txt") == b"12345"
@pytest.mark.asyncio(scope="session")
async def test_file_download_async(anc_any):
assert await anc_any.files.download("test_empty_text.txt") == b""
assert await anc_any.files.download("/test_12345_text.txt") == b"12345"
@pytest.mark.parametrize("data_type", ("str", "bytes"))
@pytest.mark.parametrize("chunk_size", (15, 32, 64, None))
def test_file_download2stream(nc, data_type, chunk_size):
bytes_io_fp = MyBytesIO()
content = "".join(choice(ascii_lowercase) for _ in range(64)) if data_type == "str" else randbytes(64)
nc.files.upload("/test_dir_tmp/download2stream", content=content)
old_headers = nc.response_headers
if chunk_size is not None:
nc.files.download2stream("/test_dir_tmp/download2stream", bytes_io_fp, chunk_size=chunk_size)
else:
nc.files.download2stream("/test_dir_tmp/download2stream", bytes_io_fp)
assert nc.response_headers != old_headers
assert nc.files.download("/test_dir_tmp/download2stream") == bytes_io_fp.getbuffer()
if chunk_size is None:
assert bytes_io_fp.n_write_calls == 1
else:
assert bytes_io_fp.n_write_calls == math.ceil(64 / chunk_size)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("data_type", ("str", "bytes"))
@pytest.mark.parametrize("chunk_size", (15, 32, 64, None))
async def test_file_download2stream_async(anc, data_type, chunk_size):
bytes_io_fp = MyBytesIO()
content = "".join(choice(ascii_lowercase) for _ in range(64)) if data_type == "str" else randbytes(64)
await anc.files.upload("/test_dir_tmp/download2stream_async", content=content)
old_headers = anc.response_headers
if chunk_size is not None:
await anc.files.download2stream("/test_dir_tmp/download2stream_async", bytes_io_fp, chunk_size=chunk_size)
else:
await anc.files.download2stream("/test_dir_tmp/download2stream_async", bytes_io_fp)
assert anc.response_headers != old_headers
assert await anc.files.download("/test_dir_tmp/download2stream_async") == bytes_io_fp.getbuffer()
if chunk_size is None:
assert bytes_io_fp.n_write_calls == 1
else:
assert bytes_io_fp.n_write_calls == math.ceil(64 / chunk_size)
def test_file_download2file(nc_any, rand_bytes):
with NamedTemporaryFile() as tmp_file:
nc_any.files.download2stream("test_64_bytes.bin", tmp_file.name)
assert tmp_file.read() == rand_bytes
@pytest.mark.asyncio(scope="session")
async def test_file_download2file_async(anc_any, rand_bytes):
with NamedTemporaryFile() as tmp_file:
await anc_any.files.download2stream("test_64_bytes.bin", tmp_file.name)
assert tmp_file.read() == rand_bytes
def test_file_download2stream_invalid_type(nc_any):
for test_type in (
b"13",
int(55),
):
with pytest.raises(TypeError):
nc_any.files.download2stream("xxx", test_type)
@pytest.mark.asyncio(scope="session")
async def test_file_download2stream_invalid_type_async(anc_any):
for test_type in (
b"13",
int(55),
):
with pytest.raises(TypeError):
await anc_any.files.download2stream("xxx", test_type)
def test_file_upload_stream_invalid_type(nc_any):
for test_type in (
b"13",
int(55),
):
with pytest.raises(TypeError):
nc_any.files.upload_stream("xxx", test_type)
@pytest.mark.asyncio(scope="session")
async def test_file_upload_stream_invalid_type_async(anc_any):
for test_type in (
b"13",
int(55),
):
with pytest.raises(TypeError):
await anc_any.files.upload_stream("xxx", test_type)
def test_file_download_not_found(nc_any):
with pytest.raises(NextcloudException):
nc_any.files.download("file that does not exist on the server")
with pytest.raises(NextcloudException):
nc_any.files.listdir("non existing path")
@pytest.mark.asyncio(scope="session")
async def test_file_download_not_found_async(anc_any):
with pytest.raises(NextcloudException):
await anc_any.files.download("file that does not exist on the server")
with pytest.raises(NextcloudException):
await anc_any.files.listdir("non existing path")
def test_file_download2stream_not_found(nc_any):
buf = BytesIO()
with pytest.raises(NextcloudException):
nc_any.files.download2stream("file that does not exist on the server", buf)
with pytest.raises(NextcloudException):
nc_any.files.download2stream("non existing path", buf)
@pytest.mark.asyncio(scope="session")
async def test_file_download2stream_not_found_async(anc_any):
buf = BytesIO()
with pytest.raises(NextcloudException):
await anc_any.files.download2stream("file that does not exist on the server", buf)
with pytest.raises(NextcloudException):
await anc_any.files.download2stream("non existing path", buf)
def test_file_upload(nc_any):
file_name = "test_dir_tmp/12345.txt"
result = nc_any.files.upload(file_name, content=b"\x31\x32")
assert nc_any.files.by_id(result).info.size == 2
assert nc_any.files.download(file_name) == b"\x31\x32"
result = nc_any.files.upload(f"/{file_name}", content=b"\x31\x32\x33")
assert not result.has_extra
result = nc_any.files.by_path(result)
assert result.info.size == 3
assert result.is_updatable
assert not result.is_creatable
assert nc_any.files.download(file_name) == b"\x31\x32\x33"
nc_any.files.upload(file_name, content="life is good")
assert nc_any.files.download(file_name).decode("utf-8") == "life is good"
@pytest.mark.asyncio(scope="session")
async def test_file_upload_async(anc_any):
file_name = "test_dir_tmp/12345_async.txt"
result = await anc_any.files.upload(file_name, content=b"\x31\x32")
assert (await anc_any.files.by_id(result)).info.size == 2
assert await anc_any.files.download(file_name) == b"\x31\x32"
result = await anc_any.files.upload(f"/{file_name}", content=b"\x31\x32\x33")
assert not result.has_extra
result = await anc_any.files.by_path(result)
assert result.info.size == 3
assert result.is_updatable
assert not result.is_creatable
assert await anc_any.files.download(file_name) == b"\x31\x32\x33"
await anc_any.files.upload(file_name, content="life is good")
assert (await anc_any.files.download(file_name)).decode("utf-8") == "life is good"
@pytest.mark.parametrize("chunk_size", (63, 64, 65, None))
def test_file_upload_chunked(nc, chunk_size):
file_name = "/test_dir_tmp/chunked.bin"
buf_upload = MyBytesIO()
random_bytes = randbytes(64)
buf_upload.write(random_bytes)
buf_upload.seek(0)
if chunk_size is None:
result = nc.files.upload_stream(file_name, fp=buf_upload)
else:
result = nc.files.upload_stream(file_name, fp=buf_upload, chunk_size=chunk_size)
if chunk_size is None:
assert buf_upload.n_read_calls == 2
else:
assert buf_upload.n_read_calls == 1 + math.ceil(64 / chunk_size)
assert nc.files.by_id(result.file_id).info.size == 64
buf_download = BytesIO()
nc.files.download2stream(file_name, fp=buf_download)
buf_upload.seek(0)
buf_download.seek(0)
upload_crc = adler32(buf_upload.read())
download_crc = adler32(buf_download.read())
assert upload_crc == download_crc
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("chunk_size", (63, 64, 65, None))
async def test_file_upload_chunked_async(anc, chunk_size):
file_name = "/test_dir_tmp/chunked_async.bin"
buf_upload = MyBytesIO()
random_bytes = randbytes(64)
buf_upload.write(random_bytes)
buf_upload.seek(0)
if chunk_size is None:
result = await anc.files.upload_stream(file_name, fp=buf_upload)
else:
result = await anc.files.upload_stream(file_name, fp=buf_upload, chunk_size=chunk_size)
if chunk_size is None:
assert buf_upload.n_read_calls == 2
else:
assert buf_upload.n_read_calls == 1 + math.ceil(64 / chunk_size)
assert (await anc.files.by_id(result.file_id)).info.size == 64
buf_download = BytesIO()
await anc.files.download2stream(file_name, fp=buf_download)
buf_upload.seek(0)
buf_download.seek(0)
upload_crc = adler32(buf_upload.read())
download_crc = adler32(buf_download.read())
assert upload_crc == download_crc
@pytest.mark.parametrize("dest_path", ("test_dir_tmp/test_file_upload_file", "test_###_dir/test_file_upload_file"))
def test_file_upload_file(nc_any, dest_path):
content = randbytes(113)
with NamedTemporaryFile() as tmp_file:
tmp_file.write(content)
tmp_file.flush()
r = nc_any.files.upload_stream(dest_path, tmp_file.name)
assert nc_any.files.download(dest_path) == content
assert nc_any.files.by_id(r.file_id).full_path.find(dest_path) != -1
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("dest_path", ("test_dir_tmp/test_file_upload_file", "test_###_dir/test_file_upload_file"))
async def test_file_upload_file_async(anc_any, dest_path):
content = randbytes(113)
with NamedTemporaryFile() as tmp_file:
tmp_file.write(content)
tmp_file.flush()
r = await anc_any.files.upload_stream(dest_path, tmp_file.name)
assert await anc_any.files.download(dest_path) == content
assert (await anc_any.files.by_id(r.file_id)).full_path.find(dest_path) != -1
@pytest.mark.parametrize(
"dest_path", ("test_dir_tmp/upl_chunk_v2", "test_dir_tmp/upl_chunk_v2_ü", "test_dir_tmp/upl_chunk_v2_11###33")
)
def test_file_upload_chunked_v2(nc_any, dest_path):
with NamedTemporaryFile() as tmp_file:
tmp_file.seek(7 * 1024 * 1024)
tmp_file.write(b"\0")
tmp_file.flush()
r = nc_any.files.upload_stream(dest_path, tmp_file.name)
assert len(nc_any.files.download(dest_path)) == 7 * 1024 * 1024 + 1
assert nc_any.files.by_id(r.file_id).full_path.find(dest_path) != -1
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize(
"dest_path",
("test_dir_tmp/upl_chunk_v2_async", "test_dir_tmp/upl_chunk_v2_ü_async", "test_dir_tmp/upl_chunk_v2_11###33"),
)
async def test_file_upload_chunked_v2_async(anc_any, dest_path):
with NamedTemporaryFile() as tmp_file:
tmp_file.seek(7 * 1024 * 1024)
tmp_file.write(b"\0")
tmp_file.flush()
r = await anc_any.files.upload_stream(dest_path, tmp_file.name)
assert len(await anc_any.files.download(dest_path)) == 7 * 1024 * 1024 + 1
assert (await anc_any.files.by_id(r.file_id)).full_path.find(dest_path) != -1
@pytest.mark.parametrize("file_name", ("test_file_upload_del", "test_file_upload_del/", "test_file_upload_del//"))
def test_file_upload_zero_size(nc_any, file_name):
nc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
with pytest.raises(NextcloudException):
nc_any.files.delete(f"/test_dir_tmp/{file_name}")
result = nc_any.files.upload(f"/test_dir_tmp/{file_name}", content="")
assert nc_any.files.download(f"/test_dir_tmp/{file_name}") == b""
assert result.is_dir is False
assert result.name == "test_file_upload_del"
assert result.full_path.startswith("files/")
nc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("file_name", ("test_file_upload_del", "test_file_upload_del/", "test_file_upload_del//"))
async def test_file_upload_zero_size_async(anc_any, file_name):
await anc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
with pytest.raises(NextcloudException):
await anc_any.files.delete(f"/test_dir_tmp/{file_name}")
result = await anc_any.files.upload(f"/test_dir_tmp/{file_name}", content="")
assert await anc_any.files.download(f"/test_dir_tmp/{file_name}") == b""
assert result.is_dir is False
assert result.name == "test_file_upload_del"
assert result.full_path.startswith("files/")
await anc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
@pytest.mark.parametrize("file_name", ("chunked_zero", "chunked_zero/", "chunked_zero//"))
def test_file_upload_chunked_zero_size(nc_any, file_name):
nc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
buf_upload = MyBytesIO()
result = nc_any.files.upload_stream(f"test_dir_tmp/{file_name}", fp=buf_upload)
assert nc_any.files.download(f"test_dir_tmp/{file_name}") == b""
assert not nc_any.files.by_path(result.user_path).info.size
assert result.is_dir is False
assert result.full_path.startswith("files/")
assert result.name == "chunked_zero"
nc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("file_name", ("chunked_zero", "chunked_zero/", "chunked_zero//"))
async def test_file_upload_chunked_zero_size_async(anc_any, file_name):
await anc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
buf_upload = MyBytesIO()
result = await anc_any.files.upload_stream(f"test_dir_tmp/{file_name}", fp=buf_upload)
assert await anc_any.files.download(f"test_dir_tmp/{file_name}") == b""
assert not (await anc_any.files.by_path(result.user_path)).info.size
assert result.is_dir is False
assert result.full_path.startswith("files/")
assert result.name == "chunked_zero"
await anc_any.files.delete(f"/test_dir_tmp/{file_name}", not_fail=True)
@pytest.mark.parametrize("dir_name", ("1 2", "Яё", "відео та картинки", "复杂 目录 Í", "Björn", "João", "1##3"))
def test_mkdir(nc_any, dir_name):
nc_any.files.delete(dir_name, not_fail=True)
result = nc_any.files.mkdir(dir_name)
assert result.is_dir
assert not result.has_extra
result_by_id = nc_any.files.by_id(result.file_id)
with pytest.raises(NextcloudException):
nc_any.files.mkdir(dir_name)
nc_any.files.delete(dir_name)
with pytest.raises(NextcloudException):
nc_any.files.delete(dir_name)
assert result_by_id.full_path == result.full_path
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("dir_name", ("1 2", "Яё", "відео та картинки", "复杂 目录 Í", "Björn", "João", "1##3"))
async def test_mkdir_async(anc_any, dir_name):
await anc_any.files.delete(dir_name, not_fail=True)
result = await anc_any.files.mkdir(dir_name)
assert result.is_dir
assert not result.has_extra
result_by_id = await anc_any.files.by_id(result.file_id)
with pytest.raises(NextcloudException):
await anc_any.files.mkdir(dir_name)
await anc_any.files.delete(dir_name)
with pytest.raises(NextcloudException):
await anc_any.files.delete(dir_name)
assert result_by_id.full_path == result.full_path
def test_mkdir_invalid_args(nc_any):
with pytest.raises(NextcloudException) as exc_info:
nc_any.files.makedirs("test_dir_tmp/ /zzzzzzzz", exist_ok=True)
assert exc_info.value.status_code != 405
@pytest.mark.asyncio(scope="session")
async def test_mkdir_invalid_args_async(anc_any):
with pytest.raises(NextcloudException) as exc_info:
await anc_any.files.makedirs("test_dir_tmp/ /zzzzzzzz", exist_ok=True)
assert exc_info.value.status_code != 405
def test_mkdir_delete_with_end_slash(nc_any):
nc_any.files.delete("dir_with_slash", not_fail=True)
result = nc_any.files.mkdir("dir_with_slash/")
assert result.is_dir
assert result.name == "dir_with_slash"
assert result.full_path.startswith("files/")
nc_any.files.delete("dir_with_slash/")
with pytest.raises(NextcloudException):
nc_any.files.delete("dir_with_slash")
@pytest.mark.asyncio(scope="session")
async def test_mkdir_delete_with_end_slash_async(anc_any):
await anc_any.files.delete("dir_with_slash", not_fail=True)
result = await anc_any.files.mkdir("dir_with_slash/")
assert result.is_dir
assert result.name == "dir_with_slash"
assert result.full_path.startswith("files/")
await anc_any.files.delete("dir_with_slash/")
with pytest.raises(NextcloudException):
await anc_any.files.delete("dir_with_slash")
def test_favorites(nc_any):
favorites = nc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
for favorite in favorites:
nc_any.files.setfav(favorite.user_path, False)
favorites = nc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
assert not favorites
files = ("test_dir_tmp/fav1.txt", "test_dir_tmp/fav2.txt", "test_dir_tmp/fav##3.txt")
for n in files:
nc_any.files.upload(n, content=n)
nc_any.files.setfav(n, True)
favorites = nc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
assert len(favorites) == 3
for favorite in favorites:
assert isinstance(favorite, FsNode)
nc_any.files.setfav(favorite, False)
favorites = nc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
assert not favorites
@pytest.mark.asyncio(scope="session")
async def test_favorites_async(anc_any):
favorites = await anc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
for favorite in favorites:
await anc_any.files.setfav(favorite.user_path, False)
favorites = await anc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
assert not favorites
files = ("test_dir_tmp/fav1.txt", "test_dir_tmp/fav2.txt", "test_dir_tmp/fav##3.txt")
for n in files:
await anc_any.files.upload(n, content=n)
await anc_any.files.setfav(n, True)
favorites = await anc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
assert len(favorites) == 3
for favorite in favorites:
assert isinstance(favorite, FsNode)
await anc_any.files.setfav(favorite, False)
favorites = await anc_any.files.list_by_criteria(["favorite"])
favorites = [i for i in favorites if i.name != "test_generated_image.png"]
assert not favorites
@pytest.mark.parametrize(
"dest_path",
("test_dir_tmp/test_64_bytes.bin", "test_dir_tmp/test_64_bytes_ü.bin", "test_###_dir/test_64_bytes_ü.bin"),
)
def test_copy_file(nc_any, rand_bytes, dest_path):
copied_file = nc_any.files.copy("test_64_bytes.bin", dest_path)
assert copied_file.file_id
assert copied_file.is_dir is False
assert nc_any.files.download(dest_path) == rand_bytes
with pytest.raises(NextcloudException):
nc_any.files.copy("test_64_bytes.bin", dest_path)
copied_file = nc_any.files.copy("test_12345_text.txt", dest_path, overwrite=True)
assert copied_file.file_id
assert copied_file.is_dir is False
assert nc_any.files.download(dest_path) == b"12345"
nc_any.files.delete(copied_file)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize(
"dest_path",
("test_dir_tmp/test_64_bytes.bin", "test_dir_tmp/test_64_bytes_ü.bin", "test_###_dir/test_64_bytes_ü.bin"),
)
async def test_copy_file_async(anc_any, rand_bytes, dest_path):
copied_file = await anc_any.files.copy("test_64_bytes.bin", dest_path)
assert copied_file.file_id
assert copied_file.is_dir is False
assert await anc_any.files.download(dest_path) == rand_bytes
with pytest.raises(NextcloudException):
await anc_any.files.copy("test_64_bytes.bin", dest_path)
copied_file = await anc_any.files.copy("test_12345_text.txt", dest_path, overwrite=True)
assert copied_file.file_id
assert copied_file.is_dir is False
assert await anc_any.files.download(dest_path) == b"12345"
await anc_any.files.delete(copied_file)
@pytest.mark.parametrize(
"dest_path",
("test_dir_tmp/dest move test file", "test_dir_tmp/dest move test file-ä", "test_###_dir/dest move test file-ä"),
)
def test_move_file(nc_any, dest_path):
src = "test_dir_tmp/src move test file"
content = b"content of the file"
content2 = b"content of the file-second part"
nc_any.files.upload(src, content=content)
nc_any.files.delete(dest_path, not_fail=True)
result = nc_any.files.move(src, dest_path)
assert result.etag
assert result.file_id
assert result.is_dir is False
assert nc_any.files.download(dest_path) == content
with pytest.raises(NextcloudException):
nc_any.files.download(src)
nc_any.files.upload(src, content=content2)
with pytest.raises(NextcloudException):
nc_any.files.move(src, dest_path)
result = nc_any.files.move(src, dest_path, overwrite=True)
assert result.etag
assert result.file_id
assert result.is_dir is False
with pytest.raises(NextcloudException):
nc_any.files.download(src)
assert nc_any.files.download(dest_path) == content2
nc_any.files.delete(dest_path)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize(
"dest_path",
("test_dir_tmp/dest move test file", "test_dir_tmp/dest move test file-ä", "test_###_dir/dest move test file-ä"),
)
async def test_move_file_async(anc_any, dest_path):
src = "test_dir_tmp/src move test file"
content = b"content of the file"
content2 = b"content of the file-second part"
await anc_any.files.upload(src, content=content)
await anc_any.files.delete(dest_path, not_fail=True)
result = await anc_any.files.move(src, dest_path)
assert result.etag
assert result.file_id
assert result.is_dir is False
assert await anc_any.files.download(dest_path) == content
with pytest.raises(NextcloudException):
await anc_any.files.download(src)
await anc_any.files.upload(src, content=content2)
with pytest.raises(NextcloudException):
await anc_any.files.move(src, dest_path)
result = await anc_any.files.move(src, dest_path, overwrite=True)
assert result.etag
assert result.file_id
assert result.is_dir is False
with pytest.raises(NextcloudException):
await anc_any.files.download(src)
assert await anc_any.files.download(dest_path) == content2
await anc_any.files.delete(dest_path)
def test_move_copy_dir(nc_any):
result = nc_any.files.copy("/test_dir/subdir", "test_dir_tmp/test_copy_dir")
assert result.file_id
assert result.is_dir
assert nc_any.files.by_path(result).is_dir
assert len(nc_any.files.listdir("test_dir_tmp/test_copy_dir")) == len(nc_any.files.listdir("test_dir/subdir"))
result = nc_any.files.move("test_dir_tmp/test_copy_dir", "test_dir_tmp/test_move_dir")
with pytest.raises(NextcloudException):
nc_any.files.listdir("test_dir_tmp/test_copy_dir")
assert result.file_id
assert result.is_dir
assert nc_any.files.by_path(result).is_dir
assert len(nc_any.files.listdir("test_dir_tmp/test_move_dir")) == 4
nc_any.files.delete("test_dir_tmp/test_move_dir")
@pytest.mark.asyncio(scope="session")
async def test_move_copy_dir_async(anc_any):
result = await anc_any.files.copy("/test_dir/subdir", "test_dir_tmp/test_copy_dir")
assert result.file_id
assert result.is_dir
assert (await anc_any.files.by_path(result)).is_dir
assert len(await anc_any.files.listdir("test_dir_tmp/test_copy_dir")) == len(
await anc_any.files.listdir("test_dir/subdir")
)
result = await anc_any.files.move("test_dir_tmp/test_copy_dir", "test_dir_tmp/test_move_dir")
with pytest.raises(NextcloudException):
await anc_any.files.listdir("test_dir_tmp/test_copy_dir")
assert result.file_id
assert result.is_dir
assert (await anc_any.files.by_path(result)).is_dir
assert len(await anc_any.files.listdir("test_dir_tmp/test_move_dir")) == 4
await anc_any.files.delete("test_dir_tmp/test_move_dir")
def test_find_files_listdir_depth(nc_any):
result = nc_any.files.find(["and", "gt", "size", 0, "like", "mime", "image/%"], path="test_dir")
assert len(result) == 2
result2 = nc_any.files.find(["and", "gt", "size", 0, "like", "mime", "image/%"], path="/test_dir")
assert len(result2) == 2
assert result == result2
result = nc_any.files.find(["and", "gt", "size", 0, "like", "mime", "image/%"], path="test_dir/subdir/")
assert len(result) == 1
result = nc_any.files.find(["and", "gt", "size", 0, "like", "mime", "image/%"], path="test_dir/subdir")
assert len(result) == 1
result = nc_any.files.find(["and", "gt", "size", 1024 * 1024, "like", "mime", "image/%"], path="test_dir")
assert len(result) == 0
result = nc_any.files.find(
["or", "and", "gt", "size", 0, "like", "mime", "image/%", "like", "mime", "text/%"], path="test_dir"
)
assert len(result) == 6
result = nc_any.files.find(["eq", "name", "test_12345_text.txt"], path="test_dir")
assert len(result) == 2
result = nc_any.files.find(["like", "name", "test_%"], path="/test_dir")
assert len(result) == 9
assert not nc_any.files.find(["eq", "name", "no such file"], path="test_dir")
assert not nc_any.files.find(["like", "name", "no%such%file"], path="test_dir")
result = nc_any.files.find(["like", "mime", "text/%"], path="test_dir")
assert len(result) == 4
def test_listdir_depth(nc_any):
result = nc_any.files.listdir("test_dir/", depth=1)
result2 = nc_any.files.listdir("test_dir")
assert result == result2
assert len(result) == 6
result = nc_any.files.listdir("test_dir/", depth=2)
result2 = nc_any.files.listdir("test_dir", depth=-1)
assert result == result2
assert len(result) == 10
@pytest.mark.asyncio(scope="session")
async def test_listdir_depth_async(anc_any):
result = await anc_any.files.listdir("test_dir/", depth=1)
result2 = await anc_any.files.listdir("test_dir")
assert result == result2
assert len(result) == 6
result = await anc_any.files.listdir("test_dir/", depth=2)
result2 = await anc_any.files.listdir("test_dir", depth=-1)
assert result == result2
assert len(result) == 10
def test_fs_node_fields(nc_any):
results = nc_any.files.listdir("/test_dir")
assert len(results) == 6
for _, result in enumerate(results):
assert result.user == "admin"
if result.name == "subdir":
assert result.user_path == "test_dir/subdir/"
assert result.is_dir
assert result.full_path == "files/admin/test_dir/subdir/"
assert result.info.size == 2364
assert result.info.content_length == 0
assert result.info.permissions == "RGDNVCK"
assert result.info.favorite is False
assert not result.info.mimetype
elif result.name == "test_empty_child_dir":
assert result.user_path == "test_dir/test_empty_child_dir/"
assert result.is_dir
assert result.full_path == "files/admin/test_dir/test_empty_child_dir/"
assert result.info.size == 0
assert result.info.content_length == 0
assert result.info.permissions == "RGDNVCK"
assert result.info.favorite is False
assert not result.info.mimetype
elif result.name == "test_generated_image.png":
assert result.user_path == "test_dir/test_generated_image.png"
assert not result.is_dir
assert result.full_path == "files/admin/test_dir/test_generated_image.png"
assert result.info.size > 900
assert result.info.size == result.info.content_length
assert result.info.permissions == "RGDNVW"
assert result.info.favorite is True
assert result.info.mimetype == "image/png"
elif result.name == "test_empty_text.txt":
assert result.user_path == "test_dir/test_empty_text.txt"
assert not result.is_dir
assert result.full_path == "files/admin/test_dir/test_empty_text.txt"
assert not result.info.size
assert not result.info.content_length
assert result.info.permissions == "RGDNVW"
assert result.info.favorite is False
assert result.info.mimetype == "text/plain"
res_by_id = nc_any.files.by_id(result.file_id)
assert res_by_id
res_by_path = nc_any.files.by_path(result.user_path)
assert res_by_path
assert res_by_id.info == res_by_path.info == result.info
assert res_by_id.full_path == res_by_path.full_path == result.full_path
assert res_by_id.user == res_by_path.user == result.user
assert res_by_id.etag == res_by_path.etag == result.etag
assert res_by_id.info.last_modified == res_by_path.info.last_modified == result.info.last_modified
assert res_by_id.info.creation_date == res_by_path.info.creation_date == result.info.creation_date
def test_makedirs(nc_any):
nc_any.files.delete("/test_dir_tmp/abc", not_fail=True)
result = nc_any.files.makedirs("/test_dir_tmp/abc/def", exist_ok=True)
assert result.is_dir
with pytest.raises(NextcloudException) as exc_info:
nc_any.files.makedirs("/test_dir_tmp/abc/def")
assert exc_info.value.status_code == 405
result = nc_any.files.makedirs("/test_dir_tmp/abc/def", exist_ok=True)
assert result is None
@pytest.mark.asyncio(scope="session")
async def test_makedirs_async(anc_any):
await anc_any.files.delete("/test_dir_tmp/abc", not_fail=True)
result = await anc_any.files.makedirs("/test_dir_tmp/abc/def", exist_ok=True)
assert result.is_dir
with pytest.raises(NextcloudException) as exc_info:
await anc_any.files.makedirs("/test_dir_tmp/abc/def")
assert exc_info.value.status_code == 405
result = await anc_any.files.makedirs("/test_dir_tmp/abc/def", exist_ok=True)
assert result is None
def test_fs_node_str(nc_any):
fs_node1 = nc_any.files.by_path("test_empty_dir_in_dir")
str_fs_node1 = str(fs_node1)
assert str_fs_node1.find("Dir") != -1
assert str_fs_node1.find("test_empty_dir_in_dir") != -1
assert str_fs_node1.find(f"id={fs_node1.file_id}") != -1
fs_node2 = nc_any.files.by_path("test_12345_text.txt")
str_fs_node2 = str(fs_node2)
assert str_fs_node2.find("File") != -1
assert str_fs_node2.find("test_12345_text.txt") != -1
assert str_fs_node2.find(f"id={fs_node2.file_id}") != -1
def _test_download_as_zip(result: Path, n: int):
if n == 1:
with zipfile.ZipFile(result, "r") as zip_ref:
assert zip_ref.filelist[0].filename == "test_dir/"
assert not zip_ref.filelist[0].file_size
assert zip_ref.filelist[1].filename == "test_dir/subdir/"
assert not zip_ref.filelist[1].file_size
assert zip_ref.filelist[2].filename == "test_dir/subdir/test_12345_text.txt"
assert zip_ref.filelist[2].file_size == 5
assert zip_ref.filelist[3].filename == "test_dir/subdir/test_64_bytes.bin"
assert zip_ref.filelist[3].file_size == 64
assert len(zip_ref.filelist) == 11
elif n == 2:
with zipfile.ZipFile(result, "r") as zip_ref:
assert zip_ref.filelist[0].filename == "test_empty_dir_in_dir/"
assert not zip_ref.filelist[0].file_size
assert zip_ref.filelist[1].filename == "test_empty_dir_in_dir/test_empty_child_dir/"
assert not zip_ref.filelist[1].file_size
assert len(zip_ref.filelist) == 2
else:
with zipfile.ZipFile(result, "r") as zip_ref:
assert zip_ref.filelist[0].filename == "test_empty_dir/"
assert not zip_ref.filelist[0].file_size
assert len(zip_ref.filelist) == 1
def test_download_as_zip(nc):
old_headers = nc.response_headers
result = nc.files.download_directory_as_zip("test_dir")
assert nc.response_headers != old_headers
try:
_test_download_as_zip(result, 1)
finally:
os.remove(result)
old_headers = nc.response_headers
result = nc.files.download_directory_as_zip("test_empty_dir_in_dir", "2.zip")
assert nc.response_headers != old_headers
try:
assert str(result) == "2.zip"
_test_download_as_zip(result, 2)
finally:
os.remove("2.zip")
result = nc.files.download_directory_as_zip("/test_empty_dir", "empty_folder.zip")
try:
assert str(result) == "empty_folder.zip"
_test_download_as_zip(result, 3)
finally:
os.remove("empty_folder.zip")
@pytest.mark.asyncio(scope="session")
async def test_download_as_zip_async(anc):
old_headers = anc.response_headers
result = await anc.files.download_directory_as_zip("test_dir")
assert anc.response_headers != old_headers
try:
_test_download_as_zip(result, 1)
finally:
os.remove(result)
old_headers = anc.response_headers
result = await anc.files.download_directory_as_zip("test_empty_dir_in_dir", "2.zip")
assert anc.response_headers != old_headers
try:
assert str(result) == "2.zip"
_test_download_as_zip(result, 2)
finally:
os.remove("2.zip")
result = await anc.files.download_directory_as_zip("/test_empty_dir", "empty_folder.zip")
try:
assert str(result) == "empty_folder.zip"
_test_download_as_zip(result, 3)
finally:
os.remove("empty_folder.zip")
def test_fs_node_is_xx(nc_any):
folder = nc_any.files.listdir("test_empty_dir", exclude_self=False)[0]
assert folder.is_dir
assert folder.is_creatable
assert folder.is_readable
assert folder.is_deletable
assert folder.is_shareable
assert folder.is_updatable
assert not folder.is_mounted
assert not folder.is_shared
def test_fs_node_last_modified_time():
fs_node = FsNode("", last_modified="wrong time")
assert fs_node.info.last_modified == datetime(1970, 1, 1)
fs_node = FsNode("", last_modified="Sat, 29 Jul 2023 11:56:31")
assert fs_node.info.last_modified == datetime(2023, 7, 29, 11, 56, 31)
fs_node = FsNode("", last_modified=datetime(2022, 4, 5, 1, 2, 3))
assert fs_node.info.last_modified == datetime(2022, 4, 5, 1, 2, 3)
def test_fs_node_creation_date_time():
fs_node = FsNode("", creation_date="wrong time")
assert fs_node.info.creation_date == datetime(1970, 1, 1)
fs_node = FsNode("", creation_date="Sat, 29 Jul 2023 11:56:31")
assert fs_node.info.creation_date == datetime(2023, 7, 29, 11, 56, 31)
fs_node = FsNode("", creation_date=datetime(2022, 4, 5, 1, 2, 3))
assert fs_node.info.creation_date == datetime(2022, 4, 5, 1, 2, 3)
@pytest.mark.parametrize(
"file_path", ("test_dir_tmp/trashbin_test", "test_dir_tmp/trashbin_test-ä", "test_dir_tmp/trashbin_test-1##3")
)
def test_trashbin(nc_any, file_path):
r = nc_any.files.trashbin_list()
assert isinstance(r, list)
new_file = nc_any.files.upload(file_path, content=b"")
nc_any.files.delete(new_file)
# minimum one object now in a trashbin
r = nc_any.files.trashbin_list()
assert r
# clean up trashbin
nc_any.files.trashbin_cleanup()
# no objects should be in trashbin
r = nc_any.files.trashbin_list()
assert not r
new_file = nc_any.files.upload(file_path, content=b"")
nc_any.files.delete(new_file)
# one object now in a trashbin
r = nc_any.files.trashbin_list()
assert len(r) == 1
# check types of FsNode properties
i: FsNode = r[0]
assert i.info.in_trash is True
assert i.info.trashbin_filename.find("trashbin_test") != -1
assert i.info.trashbin_original_location == file_path
assert isinstance(i.info.trashbin_deletion_time, int)
# restore that object
nc_any.files.trashbin_restore(r[0])
# no files in trashbin
r = nc_any.files.trashbin_list()
assert not r
# move a restored object to trashbin again
nc_any.files.delete(new_file)
# one object now in a trashbin
r = nc_any.files.trashbin_list()
assert len(r) == 1
# remove one object from a trashbin
nc_any.files.trashbin_delete(r[0])
# NextcloudException with status_code 404
with pytest.raises(NextcloudException) as e:
nc_any.files.trashbin_delete(r[0])
assert e.value.status_code == 404
nc_any.files.trashbin_delete(r[0], not_fail=True)
# no files in trashbin
r = nc_any.files.trashbin_list()
assert not r
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize(
"file_path", ("test_dir_tmp/trashbin_test", "test_dir_tmp/trashbin_test-ä", "test_dir_tmp/trashbin_test-1##3")
)
async def test_trashbin_async(anc_any, file_path):
r = await anc_any.files.trashbin_list()
assert isinstance(r, list)
new_file = await anc_any.files.upload(file_path, content=b"")
await anc_any.files.delete(new_file)
# minimum one object now in a trashbin
r = await anc_any.files.trashbin_list()
assert r
# clean up trashbin
await anc_any.files.trashbin_cleanup()
# no objects should be in trashbin
r = await anc_any.files.trashbin_list()
assert not r
new_file = await anc_any.files.upload(file_path, content=b"")
await anc_any.files.delete(new_file)
# one object now in a trashbin
r = await anc_any.files.trashbin_list()
assert len(r) == 1
# check types of FsNode properties
i: FsNode = r[0]
assert i.info.in_trash is True
assert i.info.trashbin_filename.find("trashbin_test") != -1
assert i.info.trashbin_original_location == file_path
assert isinstance(i.info.trashbin_deletion_time, int)
# restore that object
await anc_any.files.trashbin_restore(r[0])
# no files in trashbin
r = await anc_any.files.trashbin_list()
assert not r
# move a restored object to trashbin again
await anc_any.files.delete(new_file)
# one object now in a trashbin
r = await anc_any.files.trashbin_list()
assert len(r) == 1
# remove one object from a trashbin
await anc_any.files.trashbin_delete(r[0])
# NextcloudException with status_code 404
with pytest.raises(NextcloudException) as e:
await anc_any.files.trashbin_delete(r[0])
assert e.value.status_code == 404
await anc_any.files.trashbin_delete(r[0], not_fail=True)
# no files in trashbin
r = await anc_any.files.trashbin_list()
assert not r
@pytest.mark.skipif(os.environ.get("DATABASE_PGSQL", "0") == "1", reason="Fails on the PGSQL")
@pytest.mark.parametrize(
"dest_path",
("/test_dir_tmp/file_versions.txt", "/test_dir_tmp/file_versions-ä.txt", "test_dir_tmp/file_versions-1##3"),
)
def test_file_versions(nc_any, dest_path):
if nc_any.check_capabilities("files.versioning"):
pytest.skip("Need 'Versions' App to be enabled.")
for i in (0, 1):
nc_any.files.delete(dest_path, not_fail=True)
nc_any.files.upload(dest_path, content=b"22")
new_file = nc_any.files.upload(dest_path, content=b"333")
if i:
new_file = nc_any.files.by_id(new_file)
versions = nc_any.files.get_versions(new_file)
assert versions
version_str = str(versions[0])
assert version_str.find("File version") != -1
assert version_str.find("bytes size") != -1
nc_any.files.restore_version(versions[0])
assert nc_any.files.download(new_file) == b"22"
@pytest.mark.skipif(os.environ.get("DATABASE_PGSQL", "0") == "1", reason="Fails on the PGSQL")
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize(
"dest_path",
("/test_dir_tmp/file_versions.txt", "/test_dir_tmp/file_versions-ä.txt", "test_dir_tmp/file_versions-1##3"),
)
async def test_file_versions_async(anc_any, dest_path):
if await anc_any.check_capabilities("files.versioning"):
pytest.skip("Need 'Versions' App to be enabled.")
for i in (0, 1):
await anc_any.files.delete(dest_path, not_fail=True)
await anc_any.files.upload(dest_path, content=b"22")
new_file = await anc_any.files.upload(dest_path, content=b"333")
if i:
new_file = await anc_any.files.by_id(new_file)
versions = await anc_any.files.get_versions(new_file)
assert versions
version_str = str(versions[0])
assert version_str.find("File version") != -1
assert version_str.find("bytes size") != -1
await anc_any.files.restore_version(versions[0])
assert await anc_any.files.download(new_file) == b"22"
def test_create_update_delete_tag(nc_any):
with contextlib.suppress(NextcloudExceptionNotFound):
nc_any.files.delete_tag(nc_any.files.tag_by_name("test_nc_py_api"))
with contextlib.suppress(NextcloudExceptionNotFound):
nc_any.files.delete_tag(nc_any.files.tag_by_name("test_nc_py_api2"))
nc_any.files.create_tag("test_nc_py_api", True, True)
tag = nc_any.files.tag_by_name("test_nc_py_api")
assert isinstance(tag.tag_id, int)
assert tag.display_name == "test_nc_py_api"
assert tag.user_visible is True
assert tag.user_assignable is True
nc_any.files.update_tag(tag, "test_nc_py_api2", False, False)
with pytest.raises(NextcloudExceptionNotFound):
nc_any.files.tag_by_name("test_nc_py_api")
tag = nc_any.files.tag_by_name("test_nc_py_api2")
assert tag.display_name == "test_nc_py_api2"
assert tag.user_visible is False
assert tag.user_assignable is False
for i in nc_any.files.list_tags():
assert str(i).find("name=") != -1
nc_any.files.delete_tag(tag)
with pytest.raises(ValueError):
nc_any.files.update_tag(tag)
@pytest.mark.asyncio(scope="session")
async def test_create_update_delete_tag_async(anc_any):
with contextlib.suppress(NextcloudExceptionNotFound):
await anc_any.files.delete_tag(await anc_any.files.tag_by_name("test_nc_py_api"))
with contextlib.suppress(NextcloudExceptionNotFound):
await anc_any.files.delete_tag(await anc_any.files.tag_by_name("test_nc_py_api2"))
await anc_any.files.create_tag("test_nc_py_api", True, True)
tag = await anc_any.files.tag_by_name("test_nc_py_api")
assert isinstance(tag.tag_id, int)
assert tag.display_name == "test_nc_py_api"
assert tag.user_visible is True
assert tag.user_assignable is True
await anc_any.files.update_tag(tag, "test_nc_py_api2", False, False)
with pytest.raises(NextcloudExceptionNotFound):
await anc_any.files.tag_by_name("test_nc_py_api")
tag = await anc_any.files.tag_by_name("test_nc_py_api2")
assert tag.display_name == "test_nc_py_api2"
assert tag.user_visible is False
assert tag.user_assignable is False
for i in await anc_any.files.list_tags():
assert str(i).find("name=") != -1
await anc_any.files.delete_tag(tag)
with pytest.raises(ValueError):
await anc_any.files.update_tag(tag)
def test_get_assign_unassign_tag(nc_any):
with contextlib.suppress(NextcloudExceptionNotFound):
nc_any.files.delete_tag(nc_any.files.tag_by_name("test_nc_py_api"))
with contextlib.suppress(NextcloudExceptionNotFound):
nc_any.files.delete_tag(nc_any.files.tag_by_name("test_nc_py_api2"))
nc_any.files.create_tag("test_nc_py_api", True, False)
nc_any.files.create_tag("test_nc_py_api2", False, False)
tag1 = nc_any.files.tag_by_name("test_nc_py_api")
assert tag1.user_visible is True
assert tag1.user_assignable is False
tag2 = nc_any.files.tag_by_name("test_nc_py_api2")
assert tag2.user_visible is False
assert tag2.user_assignable is False
new_file = nc_any.files.upload("/test_dir_tmp/tag_test.txt", content=b"")
new_file = nc_any.files.by_id(new_file)
assert nc_any.files.get_tags(new_file) == []
if nc_any.srv_version["major"] > 30:
pytest.skip("Skip temporary on master branch")
assert len(nc_any.files.list_by_criteria(tags=[tag1])) == 0
nc_any.files.assign_tag(new_file, tag1)
assert isinstance(nc_any.files.get_tags(new_file)[0], SystemTag)
assert len(nc_any.files.list_by_criteria(tags=[tag1])) == 1
assert len(nc_any.files.list_by_criteria(["favorite"], tags=[tag1])) == 0
assert len(nc_any.files.list_by_criteria(tags=[tag1, tag2.tag_id])) == 0
nc_any.files.assign_tag(new_file, tag2.tag_id)
assert len(nc_any.files.list_by_criteria(tags=[tag1, tag2.tag_id])) == 1
nc_any.files.unassign_tag(new_file, tag1)
assert len(nc_any.files.list_by_criteria(tags=[tag1])) == 0
nc_any.files.assign_tag(new_file, tag1)
with pytest.raises(ValueError):
nc_any.files.list_by_criteria()
@pytest.mark.asyncio(scope="session")
async def test_get_assign_unassign_tag_async(anc_any):
with contextlib.suppress(NextcloudExceptionNotFound):
await anc_any.files.delete_tag(await anc_any.files.tag_by_name("test_nc_py_api"))
with contextlib.suppress(NextcloudExceptionNotFound):
await anc_any.files.delete_tag(await anc_any.files.tag_by_name("test_nc_py_api2"))
await anc_any.files.create_tag("test_nc_py_api", True, False)
await anc_any.files.create_tag("test_nc_py_api2", False, False)
tag1 = await anc_any.files.tag_by_name("test_nc_py_api")
assert tag1.user_visible is True
assert tag1.user_assignable is False
tag2 = await anc_any.files.tag_by_name("test_nc_py_api2")
assert tag2.user_visible is False
assert tag2.user_assignable is False
new_file = await anc_any.files.upload("/test_dir_tmp/tag_test.txt", content=b"")
new_file = await anc_any.files.by_id(new_file)
assert await anc_any.files.get_tags(new_file) == []
if (await anc_any.srv_version)["major"] > 30:
pytest.skip("Skip temporary on master branch")
assert len(await anc_any.files.list_by_criteria(tags=[tag1])) == 0
await anc_any.files.assign_tag(new_file, tag1)
assert isinstance((await anc_any.files.get_tags(new_file))[0], SystemTag)
assert len(await anc_any.files.list_by_criteria(tags=[tag1])) == 1
assert len(await anc_any.files.list_by_criteria(["favorite"], tags=[tag1])) == 0
assert len(await anc_any.files.list_by_criteria(tags=[tag1, tag2.tag_id])) == 0
await anc_any.files.assign_tag(new_file, tag2.tag_id)
assert len(await anc_any.files.list_by_criteria(tags=[tag1, tag2.tag_id])) == 1
await anc_any.files.unassign_tag(new_file, tag1)
assert len(await anc_any.files.list_by_criteria(tags=[tag1])) == 0
await anc_any.files.assign_tag(new_file, tag1)
with pytest.raises(ValueError):
await anc_any.files.list_by_criteria()
def __check_lock_info(fs_node: FsNode):
assert isinstance(fs_node.lock_info.owner, str)
assert isinstance(fs_node.lock_info.owner_display_name, str)
assert isinstance(fs_node.lock_info.type, LockType)
assert isinstance(fs_node.lock_info.lock_creation_time, datetime)
assert isinstance(fs_node.lock_info.lock_ttl, int)
assert isinstance(fs_node.lock_info.owner_editor, str)
def test_file_locking(nc_any):
if nc_any.check_capabilities("files.locking"):
pytest.skip("Files Locking App is not installed")
test_file = nc_any.files.upload("/test_dir/test_lock", content="")
assert nc_any.files.by_id(test_file).lock_info.is_locked is False
with pytest.raises(NextcloudException) as e:
nc_any.files.unlock(test_file)
assert e.value.status_code == 412
nc_any.files.lock(test_file)
locked_file = nc_any.files.by_id(test_file)
assert locked_file.lock_info.is_locked is True
__check_lock_info(locked_file)
nc_any.files.unlock(test_file)
with pytest.raises(NextcloudException) as e:
nc_any.files.unlock(test_file)
assert e.value.status_code == 412
@pytest.mark.asyncio(scope="session")
async def test_file_locking_async(anc_any):
if await anc_any.check_capabilities("files.locking"):
pytest.skip("Files Locking App is not installed")
test_file = await anc_any.files.upload("/test_dir/test_lock_async", content="")
assert (await anc_any.files.by_id(test_file)).lock_info.is_locked is False
with pytest.raises(NextcloudException) as e:
await anc_any.files.unlock(test_file)
assert e.value.status_code == 412
await anc_any.files.lock(test_file)
locked_file = await anc_any.files.by_id(test_file)
assert locked_file.lock_info.is_locked is True
__check_lock_info(locked_file)
await anc_any.files.unlock(test_file)
with pytest.raises(NextcloudException) as e:
await anc_any.files.unlock(test_file)
assert e.value.status_code == 412
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/ui_resources_test.py 0000664 0002322 0002322 00000022372 14766056032 026730 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudExceptionNotFound
def test_initial_state(nc_app):
nc_app.ui.resources.delete_initial_state("top_menu", "some_page", "some_key")
assert nc_app.ui.resources.get_initial_state("top_menu", "some_page", "some_key") is None
nc_app.ui.resources.set_initial_state("top_menu", "some_page", "some_key", {"k1": 1, "k2": 2})
r = nc_app.ui.resources.get_initial_state("top_menu", "some_page", "some_key")
assert r.appid == nc_app.app_cfg.app_name
assert r.name == "some_page"
assert r.key == "some_key"
assert r.value == {"k1": 1, "k2": 2}
nc_app.ui.resources.set_initial_state("top_menu", "some_page", "some_key", {"k1": "v1"})
r = nc_app.ui.resources.get_initial_state("top_menu", "some_page", "some_key")
assert r.value == {"k1": "v1"}
assert str(r).find("key=some_key")
nc_app.ui.resources.delete_initial_state("top_menu", "some_page", "some_key", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.resources.delete_initial_state("top_menu", "some_page", "some_key", not_fail=False)
@pytest.mark.asyncio(scope="session")
async def test_initial_state_async(anc_app):
await anc_app.ui.resources.delete_initial_state("top_menu", "some_page", "some_key")
assert await anc_app.ui.resources.get_initial_state("top_menu", "some_page", "some_key") is None
await anc_app.ui.resources.set_initial_state("top_menu", "some_page", "some_key", {"k1": 1, "k2": 2})
r = await anc_app.ui.resources.get_initial_state("top_menu", "some_page", "some_key")
assert r.appid == anc_app.app_cfg.app_name
assert r.name == "some_page"
assert r.key == "some_key"
assert r.value == {"k1": 1, "k2": 2}
await anc_app.ui.resources.set_initial_state("top_menu", "some_page", "some_key", {"k1": "v1"})
r = await anc_app.ui.resources.get_initial_state("top_menu", "some_page", "some_key")
assert r.value == {"k1": "v1"}
assert str(r).find("key=some_key")
await anc_app.ui.resources.delete_initial_state("top_menu", "some_page", "some_key", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.ui.resources.delete_initial_state("top_menu", "some_page", "some_key", not_fail=False)
def test_initial_states(nc_app):
nc_app.ui.resources.set_initial_state("top_menu", "some_page", "key1", [])
nc_app.ui.resources.set_initial_state("top_menu", "some_page", "key2", {"k2": "v2"})
r1 = nc_app.ui.resources.get_initial_state("top_menu", "some_page", "key1")
r2 = nc_app.ui.resources.get_initial_state("top_menu", "some_page", "key2")
assert r1.key == "key1"
assert r1.value == []
assert r2.key == "key2"
assert r2.value == {"k2": "v2"}
nc_app.ui.resources.delete_initial_state("top_menu", "some_page", "key1", not_fail=False)
nc_app.ui.resources.delete_initial_state("top_menu", "some_page", "key2", not_fail=False)
def test_script(nc_app):
nc_app.ui.resources.delete_script("top_menu", "some_page", "js/some_script")
assert nc_app.ui.resources.get_script("top_menu", "some_page", "js/some_script") is None
nc_app.ui.resources.set_script("top_menu", "some_page", "js/some_script")
r = nc_app.ui.resources.get_script("top_menu", "some_page", "js/some_script")
assert r.appid == nc_app.app_cfg.app_name
assert r.name == "some_page"
assert r.path == "js/some_script"
assert r.after_app_id == ""
nc_app.ui.resources.set_script("top_menu", "some_page", "js/some_script", "core")
r = nc_app.ui.resources.get_script("top_menu", "some_page", "js/some_script")
assert r.path == "js/some_script"
assert r.after_app_id == "core"
assert str(r).find("path=js/some_script")
nc_app.ui.resources.delete_script("top_menu", "some_page", "js/some_script", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.resources.delete_script("top_menu", "some_page", "js/some_script", not_fail=False)
@pytest.mark.asyncio(scope="session")
async def test_script_async(anc_app):
await anc_app.ui.resources.delete_script("top_menu", "some_page", "js/some_script")
assert await anc_app.ui.resources.get_script("top_menu", "some_page", "js/some_script") is None
await anc_app.ui.resources.set_script("top_menu", "some_page", "js/some_script")
r = await anc_app.ui.resources.get_script("top_menu", "some_page", "js/some_script")
assert r.appid == anc_app.app_cfg.app_name
assert r.name == "some_page"
assert r.path == "js/some_script"
assert r.after_app_id == ""
await anc_app.ui.resources.set_script("top_menu", "some_page", "js/some_script", "core")
r = await anc_app.ui.resources.get_script("top_menu", "some_page", "js/some_script")
assert r.path == "js/some_script"
assert r.after_app_id == "core"
assert str(r).find("path=js/some_script")
await anc_app.ui.resources.delete_script("top_menu", "some_page", "js/some_script", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.ui.resources.delete_script("top_menu", "some_page", "js/some_script", not_fail=False)
def test_scripts(nc_app):
nc_app.ui.resources.set_script("top_menu", "some_page", "js/script1")
nc_app.ui.resources.set_script("top_menu", "some_page", "js/script2", "core")
r1 = nc_app.ui.resources.get_script("top_menu", "some_page", "js/script1")
r2 = nc_app.ui.resources.get_script("top_menu", "some_page", "js/script2")
assert r1.path == "js/script1"
assert r1.after_app_id == ""
assert r2.path == "js/script2"
assert r2.after_app_id == "core"
nc_app.ui.resources.delete_script("top_menu", "some_page", "js/script1", not_fail=False)
nc_app.ui.resources.delete_script("top_menu", "some_page", "js/script2", not_fail=False)
def test_scripts_slash(nc_app):
nc_app.ui.resources.set_script("top_menu", "test_slash", "/js/script1")
r = nc_app.ui.resources.get_script("top_menu", "test_slash", "/js/script1")
assert r == nc_app.ui.resources.get_script("top_menu", "test_slash", "js/script1")
assert r.path == "js/script1"
nc_app.ui.resources.delete_script("top_menu", "test_slash", "/js/script1", not_fail=False)
assert nc_app.ui.resources.get_script("top_menu", "test_slash", "js/script1") is None
assert nc_app.ui.resources.get_script("top_menu", "test_slash", "/js/script1") is None
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.resources.delete_script("top_menu", "test_slash", "/js/script1", not_fail=False)
def test_style(nc_app):
nc_app.ui.resources.delete_style("top_menu", "some_page", "css/some_path")
assert nc_app.ui.resources.get_style("top_menu", "some_page", "css/some_path") is None
nc_app.ui.resources.set_style("top_menu", "some_page", "css/some_path")
r = nc_app.ui.resources.get_style("top_menu", "some_page", "css/some_path")
assert r.appid == nc_app.app_cfg.app_name
assert r.name == "some_page"
assert r.path == "css/some_path"
assert str(r).find("path=css/some_path")
nc_app.ui.resources.delete_style("top_menu", "some_page", "css/some_path", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.resources.delete_style("top_menu", "some_page", "css/some_path", not_fail=False)
@pytest.mark.asyncio(scope="session")
async def test_style_async(anc_app):
await anc_app.ui.resources.delete_style("top_menu", "some_page", "css/some_path")
assert await anc_app.ui.resources.get_style("top_menu", "some_page", "css/some_path") is None
await anc_app.ui.resources.set_style("top_menu", "some_page", "css/some_path")
r = await anc_app.ui.resources.get_style("top_menu", "some_page", "css/some_path")
assert r.appid == anc_app.app_cfg.app_name
assert r.name == "some_page"
assert r.path == "css/some_path"
assert str(r).find("path=css/some_path")
await anc_app.ui.resources.delete_style("top_menu", "some_page", "css/some_path", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.ui.resources.delete_style("top_menu", "some_page", "css/some_path", not_fail=False)
def test_styles(nc_app):
nc_app.ui.resources.set_style("top_menu", "some_page", "css/style1")
nc_app.ui.resources.set_style("top_menu", "some_page", "css/style2")
r1 = nc_app.ui.resources.get_style("top_menu", "some_page", "css/style1")
r2 = nc_app.ui.resources.get_style("top_menu", "some_page", "css/style2")
assert r1.path == "css/style1"
assert r2.path == "css/style2"
nc_app.ui.resources.delete_style("top_menu", "some_page", "css/style1", not_fail=False)
nc_app.ui.resources.delete_style("top_menu", "some_page", "css/style2", not_fail=False)
def test_styles_slash(nc_app):
nc_app.ui.resources.set_style("top_menu", "test_slash", "/js/script1")
r = nc_app.ui.resources.get_style("top_menu", "test_slash", "/js/script1")
assert r == nc_app.ui.resources.get_style("top_menu", "test_slash", "js/script1")
assert r.path == "js/script1"
nc_app.ui.resources.delete_style("top_menu", "test_slash", "/js/script1", not_fail=False)
assert nc_app.ui.resources.get_style("top_menu", "test_slash", "js/script1") is None
assert nc_app.ui.resources.get_style("top_menu", "test_slash", "/js/script1") is None
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.resources.delete_style("top_menu", "test_slash", "/js/script1", not_fail=False)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/users_groups_test.py 0000664 0002322 0002322 00000016556 14766056032 026770 0 ustar debalance debalance import contextlib
from datetime import datetime, timezone
from os import environ
import pytest
from nc_py_api import NextcloudException
from nc_py_api.users_groups import GroupDetails
def test_group_get_list(nc, nc_client):
groups = nc.users_groups.get_list()
assert isinstance(groups, list)
assert len(groups) >= 3
assert environ["TEST_GROUP_BOTH"] in groups
assert environ["TEST_GROUP_USER"] in groups
groups = nc.users_groups.get_list(mask="test_nc_py_api_group")
assert len(groups) == 2
groups = nc.users_groups.get_list(limit=1)
assert len(groups) == 1
assert groups[0] != nc.users_groups.get_list(limit=1, offset=1)[0]
@pytest.mark.asyncio(scope="session")
async def test_group_get_list_async(anc, anc_client):
groups = await anc.users_groups.get_list()
assert isinstance(groups, list)
assert len(groups) >= 3
assert environ["TEST_GROUP_BOTH"] in groups
assert environ["TEST_GROUP_USER"] in groups
groups = await anc.users_groups.get_list(mask="test_nc_py_api_group")
assert len(groups) == 2
groups = await anc.users_groups.get_list(limit=1)
assert len(groups) == 1
assert groups[0] != (await anc.users_groups.get_list(limit=1, offset=1))[0]
def _test_group_get_details(groups: list[GroupDetails]):
assert len(groups) == 1
group = groups[0]
assert group.group_id == environ["TEST_GROUP_BOTH"]
assert group.display_name == environ["TEST_GROUP_BOTH"]
assert group.disabled is False
assert isinstance(group.user_count, int)
assert isinstance(group.can_add, bool)
assert isinstance(group.can_remove, bool)
assert str(group).find("user_count=") != -1
def test_group_get_details(nc, nc_client):
groups = nc.users_groups.get_details(mask=environ["TEST_GROUP_BOTH"])
_test_group_get_details(groups)
@pytest.mark.asyncio(scope="session")
async def test_group_get_details_async(anc, anc_client):
groups = await anc.users_groups.get_details(mask=environ["TEST_GROUP_BOTH"])
_test_group_get_details(groups)
def test_get_non_existing_group(nc_client):
groups = nc_client.users_groups.get_list(mask="Such group should not be present")
assert isinstance(groups, list)
assert not groups
@pytest.mark.asyncio(scope="session")
async def test_get_non_existing_group_async(anc_client):
groups = await anc_client.users_groups.get_list(mask="Such group should not be present")
assert isinstance(groups, list)
assert not groups
def test_group_edit(nc_client):
display_name = str(int(datetime.now(timezone.utc).timestamp()))
nc_client.users_groups.edit(environ["TEST_GROUP_USER"], display_name=display_name)
assert nc_client.users_groups.get_details(mask=environ["TEST_GROUP_USER"])[0].display_name == display_name
with pytest.raises(NextcloudException) as exc_info:
nc_client.users_groups.edit("non_existing_group", display_name="earth people")
# remove 996 in the future, PR was already accepted in Nextcloud Server
assert exc_info.value.status_code in (
404,
996,
)
@pytest.mark.asyncio(scope="session")
async def test_group_edit_async(anc_client):
display_name = str(int(datetime.now(timezone.utc).timestamp()))
await anc_client.users_groups.edit(environ["TEST_GROUP_USER"], display_name=display_name)
assert (await anc_client.users_groups.get_details(mask=environ["TEST_GROUP_USER"]))[0].display_name == display_name
with pytest.raises(NextcloudException) as exc_info:
await anc_client.users_groups.edit("non_existing_group", display_name="earth people")
# remove 996 in the future, PR was already accepted in Nextcloud Server
assert exc_info.value.status_code in (
404,
996,
)
def test_group_display_name_promote_demote(nc_client):
group_id = "test_group_display_name_promote_demote"
with contextlib.suppress(NextcloudException):
nc_client.users_groups.delete(group_id)
nc_client.users_groups.create(group_id, display_name="12345")
try:
group_details = nc_client.users_groups.get_details(mask=group_id)
assert len(group_details) == 1
assert group_details[0].display_name == "12345"
group_members = nc_client.users_groups.get_members(group_id)
assert isinstance(group_members, list)
assert not group_members
group_subadmins = nc_client.users_groups.get_subadmins(group_id)
assert isinstance(group_subadmins, list)
assert not group_subadmins
nc_client.users.add_to_group(environ["TEST_USER_ID"], group_id)
group_members = nc_client.users_groups.get_members(group_id)
assert group_members[0] == environ["TEST_USER_ID"]
group_subadmins = nc_client.users_groups.get_subadmins(group_id)
assert not group_subadmins
nc_client.users.promote_to_subadmin(environ["TEST_USER_ID"], group_id)
group_subadmins = nc_client.users_groups.get_subadmins(group_id)
assert group_subadmins[0] == environ["TEST_USER_ID"]
nc_client.users.demote_from_subadmin(environ["TEST_USER_ID"], group_id)
group_subadmins = nc_client.users_groups.get_subadmins(group_id)
assert not group_subadmins
nc_client.users.remove_from_group(environ["TEST_USER_ID"], group_id)
group_members = nc_client.users_groups.get_members(group_id)
assert not group_members
finally:
nc_client.users_groups.delete(group_id)
with pytest.raises(NextcloudException):
nc_client.users_groups.delete(group_id)
@pytest.mark.asyncio(scope="session")
async def test_group_display_name_promote_demote_async(anc_client):
group_id = "test_group_display_name_promote_demote"
with contextlib.suppress(NextcloudException):
await anc_client.users_groups.delete(group_id)
await anc_client.users_groups.create(group_id, display_name="12345")
try:
group_details = await anc_client.users_groups.get_details(mask=group_id)
assert len(group_details) == 1
assert group_details[0].display_name == "12345"
group_members = await anc_client.users_groups.get_members(group_id)
assert isinstance(group_members, list)
assert not group_members
group_subadmins = await anc_client.users_groups.get_subadmins(group_id)
assert isinstance(group_subadmins, list)
assert not group_subadmins
await anc_client.users.add_to_group(environ["TEST_USER_ID"], group_id)
group_members = await anc_client.users_groups.get_members(group_id)
assert group_members[0] == environ["TEST_USER_ID"]
group_subadmins = await anc_client.users_groups.get_subadmins(group_id)
assert not group_subadmins
await anc_client.users.promote_to_subadmin(environ["TEST_USER_ID"], group_id)
group_subadmins = await anc_client.users_groups.get_subadmins(group_id)
assert group_subadmins[0] == environ["TEST_USER_ID"]
await anc_client.users.demote_from_subadmin(environ["TEST_USER_ID"], group_id)
group_subadmins = await anc_client.users_groups.get_subadmins(group_id)
assert not group_subadmins
await anc_client.users.remove_from_group(environ["TEST_USER_ID"], group_id)
group_members = await anc_client.users_groups.get_members(group_id)
assert not group_members
finally:
await anc_client.users_groups.delete(group_id)
with pytest.raises(NextcloudException):
await anc_client.users_groups.delete(group_id)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/ui_settings_test.py 0000664 0002322 0002322 00000005621 14766056032 026554 0 ustar debalance debalance import copy
import pytest
from nc_py_api import NextcloudExceptionNotFound, ex_app
SETTINGS_EXAMPLE = ex_app.SettingsForm(
id="test_id",
section_type="admin",
section_id="test_section_id",
title="Some title",
description="Some description",
fields=[
ex_app.SettingsField(
id="field1",
title="Multi-selection",
description="Select some option setting",
type=ex_app.SettingsFieldType.MULTI_SELECT,
default=["foo", "bar"],
placeholder="Select some multiple options",
options=["foo", "bar", "baz"],
),
],
)
@pytest.mark.require_nc(major=29)
def test_register_ui_settings(nc_app):
nc_app.ui.settings.register_form(SETTINGS_EXAMPLE)
result = nc_app.ui.settings.get_entry(SETTINGS_EXAMPLE.id)
assert result == SETTINGS_EXAMPLE
nc_app.ui.settings.unregister_form(SETTINGS_EXAMPLE.id)
assert nc_app.ui.settings.get_entry(SETTINGS_EXAMPLE.id) is None
nc_app.ui.settings.unregister_form(SETTINGS_EXAMPLE.id)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.settings.unregister_form(SETTINGS_EXAMPLE.id, not_fail=False)
nc_app.ui.settings.register_form(SETTINGS_EXAMPLE)
result = nc_app.ui.settings.get_entry(SETTINGS_EXAMPLE.id)
assert result.description == SETTINGS_EXAMPLE.description
new_settings = copy.copy(SETTINGS_EXAMPLE)
new_settings.description = "new desc"
nc_app.ui.settings.register_form(new_settings)
result = nc_app.ui.settings.get_entry(new_settings.id)
assert result.description == "new desc"
nc_app.ui.settings.unregister_form(new_settings.id)
assert nc_app.ui.settings.get_entry(new_settings.id) is None
@pytest.mark.require_nc(major=29)
@pytest.mark.asyncio(scope="session")
async def test_register_ui_settings_async(anc_app):
await anc_app.ui.settings.register_form(SETTINGS_EXAMPLE)
result = await anc_app.ui.settings.get_entry(SETTINGS_EXAMPLE.id)
assert result == SETTINGS_EXAMPLE
await anc_app.ui.settings.unregister_form(SETTINGS_EXAMPLE.id)
assert await anc_app.ui.settings.get_entry(SETTINGS_EXAMPLE.id) is None
await anc_app.ui.settings.unregister_form(SETTINGS_EXAMPLE.id)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.ui.settings.unregister_form(SETTINGS_EXAMPLE.id, not_fail=False)
await anc_app.ui.settings.register_form(SETTINGS_EXAMPLE)
result = await anc_app.ui.settings.get_entry(SETTINGS_EXAMPLE.id)
assert result.description == SETTINGS_EXAMPLE.description
new_settings = copy.copy(SETTINGS_EXAMPLE)
new_settings.description = "new desc"
await anc_app.ui.settings.register_form(new_settings)
result = await anc_app.ui.settings.get_entry(new_settings.id)
assert result.description == "new desc"
await anc_app.ui.settings.unregister_form(new_settings.id)
assert await anc_app.ui.settings.get_entry(new_settings.id) is None
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/events_listener_test.py 0000664 0002322 0002322 00000004347 14766056032 027434 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudExceptionNotFound
def test_events_registration(nc_app):
nc_app.events_listener.register(
"node_event",
"/some_url",
)
result = nc_app.events_listener.get_entry("node_event")
assert result.event_type == "node_event"
assert result.action_handler == "some_url"
assert result.event_subtypes == []
nc_app.events_listener.register(
"node_event", callback_url="/new_url", event_subtypes=["NodeCreatedEvent", "NodeRenamedEvent"]
)
result = nc_app.events_listener.get_entry("node_event")
assert result.event_type == "node_event"
assert result.action_handler == "new_url"
assert result.event_subtypes == ["NodeCreatedEvent", "NodeRenamedEvent"]
nc_app.events_listener.unregister(result.event_type)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.events_listener.unregister(result.event_type, not_fail=False)
nc_app.events_listener.unregister(result.event_type)
assert nc_app.events_listener.get_entry(result.event_type) is None
assert str(result).find("event_type=") != -1
@pytest.mark.asyncio(scope="session")
async def test_events_registration_async(anc_app):
await anc_app.events_listener.register(
"node_event",
"/some_url",
)
result = await anc_app.events_listener.get_entry("node_event")
assert result.event_type == "node_event"
assert result.action_handler == "some_url"
assert result.event_subtypes == []
await anc_app.events_listener.register(
"node_event", callback_url="/new_url", event_subtypes=["NodeCreatedEvent", "NodeRenamedEvent"]
)
result = await anc_app.events_listener.get_entry("node_event")
assert result.event_type == "node_event"
assert result.action_handler == "new_url"
assert result.event_subtypes == ["NodeCreatedEvent", "NodeRenamedEvent"]
await anc_app.events_listener.unregister(result.event_type)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.events_listener.unregister(result.event_type, not_fail=False)
await anc_app.events_listener.unregister(result.event_type)
assert await anc_app.events_listener.get_entry(result.event_type) is None
assert str(result).find("event_type=") != -1
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/__init__.py 0000664 0002322 0002322 00000000000 14766056032 024701 0 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/talk_bot_test.py 0000664 0002322 0002322 00000020545 14766056032 026020 0 ustar debalance debalance from os import environ
import httpx
import pytest
from nc_py_api import talk, talk_bot
@pytest.mark.require_nc(major=27, minor=1)
def test_register_unregister_talk_bot(nc_app):
if nc_app.talk.bots_available is False:
pytest.skip("Need Talk bots support")
nc_app.unregister_talk_bot("/talk_bot_coverage")
list_of_bots = nc_app.talk.list_bots()
nc_app.register_talk_bot("/talk_bot_coverage", "Coverage bot", "Desc")
assert len(list_of_bots) + 1 == len(nc_app.talk.list_bots())
nc_app.register_talk_bot("/talk_bot_coverage", "Coverage bot", "Desc")
assert len(list_of_bots) + 1 == len(nc_app.talk.list_bots())
assert nc_app.unregister_talk_bot("/talk_bot_coverage") is True
assert len(list_of_bots) == len(nc_app.talk.list_bots())
assert nc_app.unregister_talk_bot("/talk_bot_coverage") is False
assert len(list_of_bots) == len(nc_app.talk.list_bots())
@pytest.mark.asyncio(scope="session")
@pytest.mark.require_nc(major=27, minor=1)
async def test_register_unregister_talk_bot_async(anc_app):
if await anc_app.talk.bots_available is False:
pytest.skip("Need Talk bots support")
await anc_app.unregister_talk_bot("/talk_bot_coverage")
list_of_bots = await anc_app.talk.list_bots()
await anc_app.register_talk_bot("/talk_bot_coverage", "Coverage bot", "Desc")
assert len(list_of_bots) + 1 == len(await anc_app.talk.list_bots())
await anc_app.register_talk_bot("/talk_bot_coverage", "Coverage bot", "Desc")
assert len(list_of_bots) + 1 == len(await anc_app.talk.list_bots())
assert await anc_app.unregister_talk_bot("/talk_bot_coverage") is True
assert len(list_of_bots) == len(await anc_app.talk.list_bots())
assert await anc_app.unregister_talk_bot("/talk_bot_coverage") is False
assert len(list_of_bots) == len(await anc_app.talk.list_bots())
def _test_list_bots(registered_bot: talk.BotInfo):
assert isinstance(registered_bot.bot_id, int)
assert registered_bot.url.find("/some_url") != -1
assert registered_bot.description == "some desc"
assert registered_bot.state == 1
assert not registered_bot.error_count
assert registered_bot.last_error_date == 0
assert registered_bot.last_error_message is None
assert isinstance(registered_bot.url_hash, str)
@pytest.mark.require_nc(major=27, minor=1)
def test_list_bots(nc, nc_app):
if nc_app.talk.bots_available is False:
pytest.skip("Need Talk bots support")
nc_app.register_talk_bot("/some_url", "some bot name", "some desc")
registered_bot = next(i for i in nc.talk.list_bots() if i.bot_name == "some bot name")
_test_list_bots(registered_bot)
conversation = nc.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
conversation_bots = nc.talk.conversation_list_bots(conversation)
assert conversation_bots
assert str(conversation_bots[0]).find("name=") != -1
finally:
nc.talk.delete_conversation(conversation.token)
@pytest.mark.asyncio(scope="session")
@pytest.mark.require_nc(major=27, minor=1)
async def test_list_bots_async(anc, anc_app):
if await anc_app.talk.bots_available is False:
pytest.skip("Need Talk bots support")
await anc_app.register_talk_bot("/some_url", "some bot name", "some desc")
registered_bot = next(i for i in await anc.talk.list_bots() if i.bot_name == "some bot name")
_test_list_bots(registered_bot)
conversation = await anc.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
conversation_bots = await anc.talk.conversation_list_bots(conversation)
assert conversation_bots
assert str(conversation_bots[0]).find("name=") != -1
finally:
await anc.talk.delete_conversation(conversation.token)
@pytest.mark.skipif(environ.get("CI", None) is None, reason="run only on GitHub")
@pytest.mark.require_nc(major=27, minor=1)
def test_chat_bot_receive_message(nc_app):
if nc_app.talk.bots_available is False:
pytest.skip("Need Talk bots support")
httpx.delete(f"{'http'}://{environ.get('APP_HOST', '127.0.0.1')}:{environ['APP_PORT']}/reset_bot_secret")
talk_bot_inst = talk_bot.TalkBot("/talk_bot_coverage", "Coverage bot", "Desc")
talk_bot_inst.enabled_handler(True, nc_app)
conversation = nc_app.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
coverage_bot = next(i for i in nc_app.talk.list_bots() if i.url.endswith("/talk_bot_coverage"))
c_bot_info = next(
i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 0
nc_app.talk.enable_bot(conversation, coverage_bot)
c_bot_info = next(
i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 1
with pytest.raises(ValueError):
nc_app.talk.send_message("Here are the msg!")
nc_app.talk.send_message("Here are the msg!", conversation)
msg_from_bot = None
for _ in range(40):
messages = nc_app.talk.receive_messages(conversation, look_in_future=True, timeout=1)
if messages[-1].message == "Hello from bot!":
msg_from_bot = messages[-1]
break
assert msg_from_bot
c_bot_info = next(
i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 1
nc_app.talk.disable_bot(conversation, coverage_bot)
c_bot_info = next(
i for i in nc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 0
finally:
nc_app.talk.delete_conversation(conversation.token)
talk_bot_inst.enabled_handler(False, nc_app)
talk_bot_inst.callback_url = "invalid_url"
with pytest.raises(RuntimeError):
talk_bot_inst.send_message("message", 999999, token="sometoken")
@pytest.mark.asyncio(scope="session")
@pytest.mark.skipif(environ.get("CI", None) is None, reason="run only on GitHub")
@pytest.mark.require_nc(major=27, minor=1)
async def test_chat_bot_receive_message_async(anc_app):
if await anc_app.talk.bots_available is False:
pytest.skip("Need Talk bots support")
httpx.delete(f"{'http'}://{environ.get('APP_HOST', '127.0.0.1')}:{environ['APP_PORT']}/reset_bot_secret")
talk_bot_inst = talk_bot.AsyncTalkBot("/talk_bot_coverage", "Coverage bot", "Desc")
await talk_bot_inst.enabled_handler(True, anc_app)
conversation = await anc_app.talk.create_conversation(talk.ConversationType.GROUP, "admin")
try:
coverage_bot = next(i for i in await anc_app.talk.list_bots() if i.url.endswith("/talk_bot_coverage"))
c_bot_info = next(
i for i in await anc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 0
await anc_app.talk.enable_bot(conversation, coverage_bot)
c_bot_info = next(
i for i in await anc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 1
with pytest.raises(ValueError):
await anc_app.talk.send_message("Here are the msg!")
await anc_app.talk.send_message("Here are the msg!", conversation)
msg_from_bot = None
for _ in range(40):
messages = await anc_app.talk.receive_messages(conversation, look_in_future=True, timeout=1)
if messages[-1].message == "Hello from bot!":
msg_from_bot = messages[-1]
break
assert msg_from_bot
c_bot_info = next(
i for i in await anc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 1
await anc_app.talk.disable_bot(conversation, coverage_bot)
c_bot_info = next(
i for i in await anc_app.talk.conversation_list_bots(conversation) if i.bot_id == coverage_bot.bot_id
)
assert c_bot_info.state == 0
finally:
await anc_app.talk.delete_conversation(conversation.token)
await talk_bot_inst.enabled_handler(False, anc_app)
talk_bot_inst.callback_url = "invalid_url"
with pytest.raises(RuntimeError):
await talk_bot_inst.send_message("message", 999999, token="sometoken")
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/ui_files_actions_test.py 0000664 0002322 0002322 00000016127 14766056032 027541 0 ustar debalance debalance import pytest
from nc_py_api import FilePermissions, FsNode, NextcloudExceptionNotFound, ex_app
def test_register_ui_file_actions(nc_app):
nc_app.ui.files_dropdown_menu.register_ex("test_ui_action_im", "UI TEST Image", "/ui_action_test", mime="image")
result = nc_app.ui.files_dropdown_menu.get_entry("test_ui_action_im")
assert result.name == "test_ui_action_im"
assert result.display_name == "UI TEST Image"
assert result.action_handler == "ui_action_test"
assert result.mime == "image"
assert result.permissions == 31
assert result.order == 0
assert result.icon == ""
assert result.appid == "nc_py_api"
assert result.version == "2.0"
nc_app.ui.files_dropdown_menu.unregister(result.name)
nc_app.ui.files_dropdown_menu.register("test_ui_action_any", "UI TEST", "ui_action", permissions=1, order=1)
result = nc_app.ui.files_dropdown_menu.get_entry("test_ui_action_any")
assert result.name == "test_ui_action_any"
assert result.display_name == "UI TEST"
assert result.action_handler == "ui_action"
assert result.mime == "file"
assert result.permissions == 1
assert result.order == 1
assert result.icon == ""
assert result.version == "1.0"
nc_app.ui.files_dropdown_menu.register_ex("test_ui_action_any", "UI", "/ui_action2", icon="/img/icon.svg")
result = nc_app.ui.files_dropdown_menu.get_entry("test_ui_action_any")
assert result.name == "test_ui_action_any"
assert result.display_name == "UI"
assert result.action_handler == "ui_action2"
assert result.mime == "file"
assert result.permissions == 31
assert result.order == 0
assert result.icon == "img/icon.svg"
assert result.version == "2.0"
nc_app.ui.files_dropdown_menu.unregister(result.name)
assert str(result).find("name=test_ui_action")
@pytest.mark.asyncio(scope="session")
async def test_register_ui_file_actions_async(anc_app):
await anc_app.ui.files_dropdown_menu.register_ex(
"test_ui_action_im", "UI TEST Image", "/ui_action_test", mime="image"
)
result = await anc_app.ui.files_dropdown_menu.get_entry("test_ui_action_im")
assert result.name == "test_ui_action_im"
assert result.display_name == "UI TEST Image"
assert result.action_handler == "ui_action_test"
assert result.mime == "image"
assert result.permissions == 31
assert result.order == 0
assert result.icon == ""
assert result.appid == "nc_py_api"
assert result.version == "2.0"
await anc_app.ui.files_dropdown_menu.unregister(result.name)
await anc_app.ui.files_dropdown_menu.register("test_ui_action_any", "UI TEST", "ui_action", permissions=1, order=1)
result = await anc_app.ui.files_dropdown_menu.get_entry("test_ui_action_any")
assert result.name == "test_ui_action_any"
assert result.display_name == "UI TEST"
assert result.action_handler == "ui_action"
assert result.mime == "file"
assert result.permissions == 1
assert result.order == 1
assert result.icon == ""
assert result.version == "1.0"
await anc_app.ui.files_dropdown_menu.register_ex("test_ui_action_any", "UI", "/ui_action2", icon="/img/icon.svg")
result = await anc_app.ui.files_dropdown_menu.get_entry("test_ui_action_any")
assert result.name == "test_ui_action_any"
assert result.display_name == "UI"
assert result.action_handler == "ui_action2"
assert result.mime == "file"
assert result.permissions == 31
assert result.order == 0
assert result.icon == "img/icon.svg"
assert result.version == "2.0"
await anc_app.ui.files_dropdown_menu.unregister(result.name)
assert str(result).find("name=test_ui_action")
def test_unregister_ui_file_actions(nc_app):
nc_app.ui.files_dropdown_menu.register_ex("test_ui_action", "NcPyApi UI TEST", "/any_rel_url")
nc_app.ui.files_dropdown_menu.unregister("test_ui_action")
assert nc_app.ui.files_dropdown_menu.get_entry("test_ui_action") is None
nc_app.ui.files_dropdown_menu.unregister("test_ui_action")
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.files_dropdown_menu.unregister("test_ui_action", not_fail=False)
@pytest.mark.asyncio(scope="session")
async def test_unregister_ui_file_actions_async(anc_app):
await anc_app.ui.files_dropdown_menu.register_ex("test_ui_action", "NcPyApi UI TEST", "/any_rel_url")
await anc_app.ui.files_dropdown_menu.unregister("test_ui_action")
assert await anc_app.ui.files_dropdown_menu.get_entry("test_ui_action") is None
await anc_app.ui.files_dropdown_menu.unregister("test_ui_action")
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.ui.files_dropdown_menu.unregister("test_ui_action", not_fail=False)
def test_ui_file_to_fs_node(nc_app):
def ui_action_check(directory: str, fs_object: FsNode):
permissions = 0
if fs_object.is_readable:
permissions += FilePermissions.PERMISSION_READ
if fs_object.is_updatable:
permissions += FilePermissions.PERMISSION_UPDATE
if fs_object.is_creatable:
permissions += FilePermissions.PERMISSION_CREATE
if fs_object.is_deletable:
permissions += FilePermissions.PERMISSION_DELETE
if fs_object.is_shareable:
permissions += FilePermissions.PERMISSION_SHARE
fileid_str = str(fs_object.info.fileid)
i = fs_object.file_id.find(fileid_str)
file_info = ex_app.UiActionFileInfo(
fileId=fs_object.info.fileid,
name=fs_object.name,
directory=directory,
etag=fs_object.etag,
mime=fs_object.info.mimetype,
fileType="dir" if fs_object.is_dir else "file",
mtime=int(fs_object.info.last_modified.timestamp()),
size=fs_object.info.size,
favorite=str(fs_object.info.favorite),
permissions=permissions,
userId=fs_object.user,
shareOwner="some_user" if fs_object.is_shared else None,
shareOwnerId="some_user_id" if fs_object.is_shared else None,
instanceId=fs_object.file_id[i + len(fileid_str) :],
)
fs_node = file_info.to_fs_node()
assert isinstance(fs_node, FsNode)
assert fs_node.etag == fs_object.etag
assert fs_node.name == fs_object.name
assert fs_node.user_path == fs_object.user_path
assert fs_node.full_path == fs_object.full_path
assert fs_node.file_id == fs_object.file_id
assert fs_node.is_dir == fs_object.is_dir
assert fs_node.info.mimetype == fs_object.info.mimetype
assert fs_node.info.permissions == fs_object.info.permissions
assert fs_node.info.last_modified == fs_object.info.last_modified
assert fs_node.info.favorite == fs_object.info.favorite
assert fs_node.info.content_length == fs_object.info.content_length
assert fs_node.info.size == fs_object.info.size
assert fs_node.info.fileid == fs_object.info.fileid
for each_file in nc_app.files.listdir():
ui_action_check(directory="/", fs_object=each_file)
for each_file in nc_app.files.listdir("test_dir"):
ui_action_check(directory="/test_dir", fs_object=each_file)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/ui_top_menu_test.py 0000664 0002322 0002322 00000005702 14766056032 026542 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudExceptionNotFound
def test_register_ui_top_menu(nc_app):
nc_app.ui.top_menu.register("test_name", "Disp name", "")
result = nc_app.ui.top_menu.get_entry("test_name")
assert result.name == "test_name"
assert result.display_name == "Disp name"
assert result.icon == ""
assert result.admin_required is False
assert result.appid == nc_app.app_cfg.app_name
nc_app.ui.top_menu.unregister(result.name)
assert nc_app.ui.top_menu.get_entry("test_name") is None
nc_app.ui.top_menu.unregister(result.name)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.ui.top_menu.unregister(result.name, not_fail=False)
nc_app.ui.top_menu.register("test_name", "display", "/img/test.svg", admin_required=True)
result = nc_app.ui.top_menu.get_entry("test_name")
assert result.name == "test_name"
assert result.display_name == "display"
assert result.icon == "img/test.svg"
assert result.admin_required is True
nc_app.ui.top_menu.register("test_name", "Display name", "", admin_required=False)
result = nc_app.ui.top_menu.get_entry("test_name")
assert result.name == "test_name"
assert result.display_name == "Display name"
assert result.icon == ""
assert result.admin_required is False
nc_app.ui.top_menu.unregister(result.name)
assert nc_app.ui.top_menu.get_entry("test_name") is None
assert str(result).find("name=test_name")
@pytest.mark.asyncio(scope="session")
async def test_register_ui_top_menu_async(anc_app):
await anc_app.ui.top_menu.register("test_name", "Disp name", "")
result = await anc_app.ui.top_menu.get_entry("test_name")
assert result.name == "test_name"
assert result.display_name == "Disp name"
assert result.icon == ""
assert result.admin_required is False
assert result.appid == anc_app.app_cfg.app_name
await anc_app.ui.top_menu.unregister(result.name)
assert await anc_app.ui.top_menu.get_entry("test_name") is None
await anc_app.ui.top_menu.unregister(result.name)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.ui.top_menu.unregister(result.name, not_fail=False)
await anc_app.ui.top_menu.register("test_name", "display", "/img/test.svg", admin_required=True)
result = await anc_app.ui.top_menu.get_entry("test_name")
assert result.name == "test_name"
assert result.display_name == "display"
assert result.icon == "img/test.svg"
assert result.admin_required is True
await anc_app.ui.top_menu.register("test_name", "Display name", "", admin_required=False)
result = await anc_app.ui.top_menu.get_entry("test_name")
assert result.name == "test_name"
assert result.display_name == "Display name"
assert result.icon == ""
assert result.admin_required is False
await anc_app.ui.top_menu.unregister(result.name)
assert await anc_app.ui.top_menu.get_entry("test_name") is None
assert str(result).find("name=test_name")
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/activity_test.py 0000664 0002322 0002322 00000006122 14766056032 026050 0 ustar debalance debalance import datetime
import pytest
from nc_py_api.activity import Activity
def test_get_filters(nc_any):
if nc_any.activity.available is False:
pytest.skip("Activity App is not installed")
r = nc_any.activity.get_filters()
assert r
for i in r:
assert i.filter_id
assert isinstance(i.icon, str)
assert i.name
assert isinstance(i.priority, int)
assert str(i).find("name=") != -1
@pytest.mark.asyncio(scope="session")
async def test_get_filters_async(anc_any):
if await anc_any.activity.available is False:
pytest.skip("Activity App is not installed")
r = await anc_any.activity.get_filters()
assert r
for i in r:
assert i.filter_id
assert isinstance(i.icon, str)
assert i.name
assert isinstance(i.priority, int)
assert str(i).find("name=") != -1
def _test_get_activities(r: list[Activity]):
assert r
for i in r:
assert i.activity_id
assert isinstance(i.app, str)
assert isinstance(i.activity_type, str)
assert isinstance(i.actor_id, str)
assert isinstance(i.subject, str)
assert isinstance(i.subject_rich, list)
assert isinstance(i.message, str)
assert isinstance(i.message_rich, list)
assert isinstance(i.object_type, str)
assert isinstance(i.object_id, int)
assert isinstance(i.object_name, str)
assert isinstance(i.objects, dict)
assert isinstance(i.link, str)
assert isinstance(i.icon, str)
assert i.time > datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
assert str(i).find("app=") != -1
def test_get_activities(nc_any):
if nc_any.activity.available is False:
pytest.skip("Activity App is not installed")
with pytest.raises(ValueError):
nc_any.activity.get_activities(object_id=4)
r = nc_any.activity.get_activities(since=True)
_test_get_activities(r)
r2 = nc_any.activity.get_activities(since=True)
if r2:
old_activities_id = [i.activity_id for i in r]
assert r2[0].activity_id not in old_activities_id
assert r2[-1].activity_id not in old_activities_id
assert len(nc_any.activity.get_activities(since=0, limit=1)) == 1
while True:
if not nc_any.activity.get_activities(since=True):
break
@pytest.mark.asyncio(scope="session")
async def test_get_activities_async(anc_any):
if await anc_any.activity.available is False:
pytest.skip("Activity App is not installed")
with pytest.raises(ValueError):
await anc_any.activity.get_activities(object_id=4)
r = await anc_any.activity.get_activities(since=True)
_test_get_activities(r)
r2 = await anc_any.activity.get_activities(since=True)
if r2:
old_activities_id = [i.activity_id for i in r]
assert r2[0].activity_id not in old_activities_id
assert r2[-1].activity_id not in old_activities_id
assert len(await anc_any.activity.get_activities(since=0, limit=1)) == 1
while True:
if not await anc_any.activity.get_activities(since=True):
break
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/nc_app_test.py 0000664 0002322 0002322 00000004654 14766056032 025464 0 ustar debalance debalance from os import environ
from unittest import mock
import pytest
from nc_py_api.ex_app import set_handlers
def test_get_users_list(nc_app):
users = nc_app.users_list()
assert users
assert nc_app.user in users
@pytest.mark.asyncio(scope="session")
async def test_get_users_list_async(anc_app):
users = await anc_app.users_list()
assert users
assert await anc_app.user in users
def test_app_cfg(nc_app):
app_cfg = nc_app.app_cfg
assert app_cfg.app_name == environ["APP_ID"]
assert app_cfg.app_version == environ["APP_VERSION"]
assert app_cfg.app_secret == environ["APP_SECRET"]
@pytest.mark.asyncio(scope="session")
async def test_app_cfg_async(anc_app):
app_cfg = anc_app.app_cfg
assert app_cfg.app_name == environ["APP_ID"]
assert app_cfg.app_version == environ["APP_VERSION"]
assert app_cfg.app_secret == environ["APP_SECRET"]
def test_change_user(nc_app):
orig_user = nc_app.user
try:
orig_capabilities = nc_app.capabilities
assert nc_app.user_status.available
nc_app.set_user("")
assert not nc_app.user_status.available
assert orig_capabilities != nc_app.capabilities
finally:
nc_app.set_user(orig_user)
assert orig_capabilities == nc_app.capabilities
@pytest.mark.asyncio(scope="session")
async def test_change_user_async(anc_app):
orig_user = await anc_app.user
try:
orig_capabilities = await anc_app.capabilities
assert await anc_app.user_status.available
await anc_app.set_user("")
assert not await anc_app.user_status.available
assert orig_capabilities != await anc_app.capabilities
finally:
await anc_app.set_user(orig_user)
assert orig_capabilities == await anc_app.capabilities
def test_set_user_same_value(nc_app):
with (mock.patch("tests.conftest.NC_APP._session.update_server_info") as update_server_info,):
nc_app.set_user(nc_app.user)
update_server_info.assert_not_called()
@pytest.mark.asyncio(scope="session")
async def test_set_user_same_value_async(anc_app):
with (mock.patch("tests.conftest.NC_APP_ASYNC._session.update_server_info") as update_server_info,):
await anc_app.set_user(await anc_app.user)
update_server_info.assert_not_called()
def test_set_handlers_invalid_param(nc_any):
with pytest.raises(ValueError):
set_handlers(None, None, default_init=False, models_to_fetch={"some": {}}) # noqa
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/files_sharing_test.py 0000664 0002322 0002322 00000045761 14766056032 027045 0 ustar debalance debalance import datetime
from os import environ
import pytest
from nc_py_api import (
FilePermissions,
FsNode,
Nextcloud,
NextcloudException,
NextcloudExceptionNotFound,
ShareType,
)
from nc_py_api.files.sharing import Share
def test_available(nc_any):
assert nc_any.files.sharing.available
@pytest.mark.asyncio(scope="session")
async def test_available_async(anc_any):
assert await anc_any.files.sharing.available
def test_create_delete(nc_any):
new_share = nc_any.files.sharing.create("test_12345_text.txt", ShareType.TYPE_LINK)
nc_any.files.sharing.delete(new_share)
with pytest.raises(NextcloudExceptionNotFound):
nc_any.files.sharing.delete(new_share)
@pytest.mark.asyncio(scope="session")
async def test_create_delete_async(anc_any):
new_share = await anc_any.files.sharing.create("test_12345_text.txt", ShareType.TYPE_LINK)
await anc_any.files.sharing.delete(new_share)
with pytest.raises(NextcloudExceptionNotFound):
await anc_any.files.sharing.delete(new_share)
def _test_share_fields(new_share: Share, get_by_id: Share, shared_file: FsNode):
assert new_share.share_type == ShareType.TYPE_LINK
assert not new_share.label
assert not new_share.note
assert new_share.mimetype.find("text") != -1
assert new_share.permissions & FilePermissions.PERMISSION_READ
assert new_share.url
assert new_share.path == shared_file.user_path
assert get_by_id.share_id == new_share.share_id
assert get_by_id.path == new_share.path
assert get_by_id.mimetype == new_share.mimetype
assert get_by_id.share_type == new_share.share_type
assert get_by_id.file_owner == new_share.file_owner
assert get_by_id.share_owner == new_share.share_owner
assert not get_by_id.share_with
assert str(get_by_id) == str(new_share)
assert get_by_id.file_source_id == shared_file.info.fileid
assert get_by_id.can_delete is True
assert get_by_id.can_edit is True
def test_share_fields(nc_any):
shared_file = nc_any.files.by_path("test_12345_text.txt")
new_share = nc_any.files.sharing.create(shared_file, ShareType.TYPE_LINK, FilePermissions.PERMISSION_READ)
try:
get_by_id = nc_any.files.sharing.get_by_id(new_share.share_id)
_test_share_fields(new_share, get_by_id, shared_file)
finally:
nc_any.files.sharing.delete(new_share)
@pytest.mark.asyncio(scope="session")
async def test_share_fields_async(anc_any):
shared_file = await anc_any.files.by_path("test_12345_text.txt")
new_share = await anc_any.files.sharing.create(shared_file, ShareType.TYPE_LINK, FilePermissions.PERMISSION_READ)
try:
get_by_id = await anc_any.files.sharing.get_by_id(new_share.share_id)
_test_share_fields(new_share, get_by_id, shared_file)
finally:
await anc_any.files.sharing.delete(new_share)
def test_create_permissions(nc_any):
new_share = nc_any.files.sharing.create("test_empty_dir", ShareType.TYPE_LINK, FilePermissions.PERMISSION_CREATE)
nc_any.files.sharing.delete(new_share)
assert (new_share.permissions & FilePermissions.PERMISSION_CREATE) == FilePermissions.PERMISSION_CREATE
new_share = nc_any.files.sharing.create(
"test_empty_dir",
ShareType.TYPE_LINK,
FilePermissions.PERMISSION_CREATE + FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_DELETE,
)
nc_any.files.sharing.delete(new_share)
assert (new_share.permissions & FilePermissions.PERMISSION_DELETE) == FilePermissions.PERMISSION_DELETE
new_share = nc_any.files.sharing.create(
"test_empty_dir",
ShareType.TYPE_LINK,
FilePermissions.PERMISSION_CREATE + FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_UPDATE,
)
nc_any.files.sharing.delete(new_share)
assert (new_share.permissions & FilePermissions.PERMISSION_UPDATE) == FilePermissions.PERMISSION_UPDATE
@pytest.mark.asyncio(scope="session")
async def test_create_permissions_async(anc_any):
new_share = await anc_any.files.sharing.create(
"test_empty_dir", ShareType.TYPE_LINK, FilePermissions.PERMISSION_CREATE
)
await anc_any.files.sharing.delete(new_share)
assert (new_share.permissions & FilePermissions.PERMISSION_CREATE) == FilePermissions.PERMISSION_CREATE
new_share = await anc_any.files.sharing.create(
"test_empty_dir",
ShareType.TYPE_LINK,
FilePermissions.PERMISSION_CREATE + FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_DELETE,
)
await anc_any.files.sharing.delete(new_share)
assert (new_share.permissions & FilePermissions.PERMISSION_DELETE) == FilePermissions.PERMISSION_DELETE
new_share = await anc_any.files.sharing.create(
"test_empty_dir",
ShareType.TYPE_LINK,
FilePermissions.PERMISSION_CREATE + FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_UPDATE,
)
await anc_any.files.sharing.delete(new_share)
assert (new_share.permissions & FilePermissions.PERMISSION_UPDATE) == FilePermissions.PERMISSION_UPDATE
def test_create_public_upload(nc_any):
new_share = nc_any.files.sharing.create("test_empty_dir", ShareType.TYPE_LINK, public_upload=True)
nc_any.files.sharing.delete(new_share)
assert (
new_share.permissions
== FilePermissions.PERMISSION_READ
| FilePermissions.PERMISSION_UPDATE
| FilePermissions.PERMISSION_SHARE
| FilePermissions.PERMISSION_DELETE
| FilePermissions.PERMISSION_CREATE
)
@pytest.mark.asyncio(scope="session")
async def test_create_public_upload_async(anc_any):
new_share = await anc_any.files.sharing.create("test_empty_dir", ShareType.TYPE_LINK, public_upload=True)
await anc_any.files.sharing.delete(new_share)
assert (
new_share.permissions
== FilePermissions.PERMISSION_READ
| FilePermissions.PERMISSION_UPDATE
| FilePermissions.PERMISSION_SHARE
| FilePermissions.PERMISSION_DELETE
| FilePermissions.PERMISSION_CREATE
)
def test_create_password(nc):
if nc.check_capabilities("spreed"):
pytest.skip(reason="Talk is not installed.")
new_share = nc.files.sharing.create("test_generated_image.png", ShareType.TYPE_LINK, password="s2dDS_z44ad1")
nc.files.sharing.delete(new_share)
assert new_share.password
assert new_share.send_password_by_talk is False
new_share = nc.files.sharing.create(
"test_generated_image.png", ShareType.TYPE_LINK, password="s2dDS_z44ad1", send_password_by_talk=True
)
nc.files.sharing.delete(new_share)
assert new_share.password
assert new_share.send_password_by_talk is True
@pytest.mark.asyncio(scope="session")
async def test_create_password_async(anc):
if await anc.check_capabilities("spreed"):
pytest.skip(reason="Talk is not installed.")
new_share = await anc.files.sharing.create("test_generated_image.png", ShareType.TYPE_LINK, password="s2dDS_z44ad1")
await anc.files.sharing.delete(new_share)
assert new_share.password
assert new_share.send_password_by_talk is False
new_share = await anc.files.sharing.create(
"test_generated_image.png", ShareType.TYPE_LINK, password="s2dDS_z44ad1", send_password_by_talk=True
)
await anc.files.sharing.delete(new_share)
assert new_share.password
assert new_share.send_password_by_talk is True
def test_create_note_label(nc_any):
new_share = nc_any.files.sharing.create(
"test_empty_text.txt", ShareType.TYPE_LINK, note="This is note", label="label"
)
nc_any.files.sharing.delete(new_share)
assert new_share.note == "This is note"
assert new_share.label == "label"
@pytest.mark.asyncio(scope="session")
async def test_create_note_label_async(anc_any):
new_share = await anc_any.files.sharing.create(
"test_empty_text.txt", ShareType.TYPE_LINK, note="This is note", label="label"
)
await anc_any.files.sharing.delete(new_share)
assert new_share.note == "This is note"
assert new_share.label == "label"
def test_create_expire_time(nc):
expire_time = datetime.datetime.now() + datetime.timedelta(days=1)
expire_time = expire_time.replace(hour=0, minute=0, second=0, microsecond=0)
new_share = nc.files.sharing.create("test_12345_text.txt", ShareType.TYPE_LINK, expire_date=expire_time)
nc.files.sharing.delete(new_share)
assert new_share.expire_date == expire_time
with pytest.raises(NextcloudException):
nc.files.sharing.create(
"test_12345_text.txt", ShareType.TYPE_LINK, expire_date=datetime.datetime.now() - datetime.timedelta(days=1)
)
new_share.raw_data["expiration"] = "invalid time"
new_share2 = Share(new_share.raw_data)
assert new_share2.expire_date == datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
@pytest.mark.asyncio(scope="session")
async def test_create_expire_time_async(anc):
expire_time = datetime.datetime.now() + datetime.timedelta(days=1)
expire_time = expire_time.replace(hour=0, minute=0, second=0, microsecond=0)
new_share = await anc.files.sharing.create("test_12345_text.txt", ShareType.TYPE_LINK, expire_date=expire_time)
await anc.files.sharing.delete(new_share)
assert new_share.expire_date == expire_time
with pytest.raises(NextcloudException):
await anc.files.sharing.create(
"test_12345_text.txt", ShareType.TYPE_LINK, expire_date=datetime.datetime.now() - datetime.timedelta(days=1)
)
new_share.raw_data["expiration"] = "invalid time"
new_share2 = Share(new_share.raw_data)
assert new_share2.expire_date == datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
def _test_get_list(share_by_id: Share, shares_list: list[Share]):
assert share_by_id.share_owner == shares_list[-1].share_owner
assert share_by_id.mimetype == shares_list[-1].mimetype
assert share_by_id.password == shares_list[-1].password
assert share_by_id.permissions == shares_list[-1].permissions
assert share_by_id.url == shares_list[-1].url
def test_get_list(nc):
shared_file = nc.files.by_path("test_12345_text.txt")
result = nc.files.sharing.get_list()
assert isinstance(result, list)
n_shares = len(result)
new_share = nc.files.sharing.create(shared_file, ShareType.TYPE_LINK)
assert isinstance(new_share, Share)
shares_list = nc.files.sharing.get_list()
assert n_shares + 1 == len(shares_list)
share_by_id = nc.files.sharing.get_by_id(shares_list[-1].share_id)
nc.files.sharing.delete(new_share)
assert n_shares == len(nc.files.sharing.get_list())
_test_get_list(share_by_id, shares_list)
@pytest.mark.asyncio(scope="session")
async def test_get_list_async(anc):
shared_file = await anc.files.by_path("test_12345_text.txt")
result = await anc.files.sharing.get_list()
assert isinstance(result, list)
n_shares = len(result)
new_share = await anc.files.sharing.create(shared_file, ShareType.TYPE_LINK)
assert isinstance(new_share, Share)
shares_list = await anc.files.sharing.get_list()
assert n_shares + 1 == len(shares_list)
share_by_id = await anc.files.sharing.get_by_id(shares_list[-1].share_id)
await anc.files.sharing.delete(new_share)
assert n_shares == len(await anc.files.sharing.get_list())
_test_get_list(share_by_id, shares_list)
def test_create_update(nc):
if nc.check_capabilities("spreed"):
pytest.skip(reason="Talk is not installed.")
new_share = nc.files.sharing.create(
"test_empty_dir",
ShareType.TYPE_LINK,
permissions=FilePermissions.PERMISSION_READ
+ FilePermissions.PERMISSION_SHARE
+ FilePermissions.PERMISSION_UPDATE,
)
update_share = nc.files.sharing.update(new_share, password="s2dDS_z44ad1")
assert update_share.password
assert update_share.permissions != FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_SHARE
update_share = nc.files.sharing.update(
new_share, permissions=FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_SHARE
)
assert update_share.password
assert update_share.permissions == FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_SHARE
assert update_share.send_password_by_talk is False
update_share = nc.files.sharing.update(new_share, send_password_by_talk=True, public_upload=True)
assert update_share.password
assert update_share.send_password_by_talk is True
expire_time = datetime.datetime.now() + datetime.timedelta(days=1)
expire_time = expire_time.replace(hour=0, minute=0, second=0, microsecond=0)
update_share = nc.files.sharing.update(new_share, expire_date=expire_time)
assert update_share.expire_date == expire_time
update_share = nc.files.sharing.update(new_share, note="note", label="label")
assert update_share.note == "note"
assert update_share.label == "label"
nc.files.sharing.delete(new_share)
@pytest.mark.asyncio(scope="session")
async def test_create_update_async(anc):
if await anc.check_capabilities("spreed"):
pytest.skip(reason="Talk is not installed.")
new_share = await anc.files.sharing.create(
"test_empty_dir",
ShareType.TYPE_LINK,
permissions=FilePermissions.PERMISSION_READ
+ FilePermissions.PERMISSION_SHARE
+ FilePermissions.PERMISSION_UPDATE,
)
update_share = await anc.files.sharing.update(new_share, password="s2dDS_z44ad1")
assert update_share.password
assert update_share.permissions != FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_SHARE
update_share = await anc.files.sharing.update(
new_share, permissions=FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_SHARE
)
assert update_share.password
assert update_share.permissions == FilePermissions.PERMISSION_READ + FilePermissions.PERMISSION_SHARE
assert update_share.send_password_by_talk is False
update_share = await anc.files.sharing.update(new_share, send_password_by_talk=True, public_upload=True)
assert update_share.password
assert update_share.send_password_by_talk is True
expire_time = datetime.datetime.now() + datetime.timedelta(days=1)
expire_time = expire_time.replace(hour=0, minute=0, second=0, microsecond=0)
update_share = await anc.files.sharing.update(new_share, expire_date=expire_time)
assert update_share.expire_date == expire_time
update_share = await anc.files.sharing.update(new_share, note="note", label="label")
assert update_share.note == "note"
assert update_share.label == "label"
await anc.files.sharing.delete(new_share)
def test_get_inherited(nc_any):
new_share = nc_any.files.sharing.create("test_dir/subdir", ShareType.TYPE_LINK)
assert not nc_any.files.sharing.get_inherited("test_dir")
assert not nc_any.files.sharing.get_inherited("test_dir/subdir")
new_share2 = nc_any.files.sharing.get_inherited("test_dir/subdir/test_12345_text.txt")[0]
assert new_share.share_id == new_share2.share_id
assert new_share.share_owner == new_share2.share_owner
assert new_share.file_owner == new_share2.file_owner
assert new_share.url == new_share2.url
nc_any.files.sharing.delete(new_share)
@pytest.mark.asyncio(scope="session")
async def test_get_inherited_async(anc_any):
new_share = await anc_any.files.sharing.create("test_dir/subdir", ShareType.TYPE_LINK)
assert not await anc_any.files.sharing.get_inherited("test_dir")
assert not await anc_any.files.sharing.get_inherited("test_dir/subdir")
new_share2 = (await anc_any.files.sharing.get_inherited("test_dir/subdir/test_12345_text.txt"))[0]
assert new_share.share_id == new_share2.share_id
assert new_share.share_owner == new_share2.share_owner
assert new_share.file_owner == new_share2.file_owner
assert new_share.url == new_share2.url
await anc_any.files.sharing.delete(new_share)
def test_share_with(nc, nc_client):
nc_second_user = Nextcloud(nc_auth_user=environ["TEST_USER_ID"], nc_auth_pass=environ["TEST_USER_PASS"])
assert not nc_second_user.files.sharing.get_list()
shared_file = nc.files.by_path("test_empty_text.txt")
folder_share = nc.files.sharing.create(
"test_empty_dir_in_dir", ShareType.TYPE_USER, share_with=environ["TEST_USER_ID"]
)
file_share = nc.files.sharing.create(shared_file, ShareType.TYPE_USER, share_with=environ["TEST_USER_ID"])
shares_list1 = nc.files.sharing.get_list(path="test_empty_dir_in_dir/")
shares_list2 = nc.files.sharing.get_list(path="test_empty_text.txt")
second_user_shares_list = nc_second_user.files.sharing.get_list()
second_user_shares_list_with_me = nc_second_user.files.sharing.get_list(shared_with_me=True)
nc.files.sharing.delete(folder_share)
nc.files.sharing.delete(file_share)
assert not second_user_shares_list
assert len(second_user_shares_list_with_me) == 2
assert len(shares_list1) == 1
assert len(shares_list2) == 1
assert not nc_second_user.files.sharing.get_list()
@pytest.mark.asyncio(scope="session")
async def test_share_with_async(anc, anc_client):
nc_second_user = Nextcloud(nc_auth_user=environ["TEST_USER_ID"], nc_auth_pass=environ["TEST_USER_PASS"])
assert not nc_second_user.files.sharing.get_list()
shared_file = await anc.files.by_path("test_empty_text.txt")
folder_share = await anc.files.sharing.create(
"test_empty_dir_in_dir", ShareType.TYPE_USER, share_with=environ["TEST_USER_ID"]
)
file_share = await anc.files.sharing.create(shared_file, ShareType.TYPE_USER, share_with=environ["TEST_USER_ID"])
shares_list1 = await anc.files.sharing.get_list(path="test_empty_dir_in_dir/")
shares_list2 = await anc.files.sharing.get_list(path="test_empty_text.txt")
second_user_shares_list = nc_second_user.files.sharing.get_list()
second_user_shares_list_with_me = nc_second_user.files.sharing.get_list(shared_with_me=True)
await anc.files.sharing.delete(folder_share)
await anc.files.sharing.delete(file_share)
assert not second_user_shares_list
assert len(second_user_shares_list_with_me) == 2
assert len(shares_list1) == 1
assert len(shares_list2) == 1
assert not nc_second_user.files.sharing.get_list()
def test_pending(nc_any):
assert isinstance(nc_any.files.sharing.get_pending(), list)
with pytest.raises(NextcloudExceptionNotFound):
nc_any.files.sharing.accept_share(99999999)
with pytest.raises(NextcloudExceptionNotFound):
nc_any.files.sharing.decline_share(99999999)
@pytest.mark.asyncio(scope="session")
async def test_pending_async(anc_any):
assert isinstance(await anc_any.files.sharing.get_pending(), list)
with pytest.raises(NextcloudExceptionNotFound):
await anc_any.files.sharing.accept_share(99999999)
with pytest.raises(NextcloudExceptionNotFound):
await anc_any.files.sharing.decline_share(99999999)
def test_deleted(nc_any):
assert isinstance(nc_any.files.sharing.get_deleted(), list)
with pytest.raises(NextcloudExceptionNotFound):
nc_any.files.sharing.undelete(99999999)
@pytest.mark.asyncio(scope="session")
async def test_deleted_async(anc_any):
assert isinstance(await anc_any.files.sharing.get_deleted(), list)
with pytest.raises(NextcloudExceptionNotFound):
await anc_any.files.sharing.undelete(99999999)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/options_test.py 0000664 0002322 0002322 00000006453 14766056032 025716 0 ustar debalance debalance import os
import sys
from subprocess import PIPE, run
from unittest import mock
import nc_py_api
def test_timeouts():
project_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
env_file = os.path.join(project_dir, ".env")
env_backup_file = os.path.join(project_dir, ".env.backup")
if os.path.exists(env_file):
os.rename(env_file, env_backup_file)
try:
check_command = [sys.executable, "-c", "import nc_py_api\nassert nc_py_api.options.NPA_TIMEOUT is None"]
with open(env_file, "w") as env_f:
env_f.write("NPA_TIMEOUT=None")
r = run(check_command, stderr=PIPE, env={}, cwd=project_dir, check=False)
assert not r.stderr
check_command = [sys.executable, "-c", "import nc_py_api\nassert nc_py_api.options.NPA_TIMEOUT == 11"]
with open(env_file, "w") as env_f:
env_f.write("NPA_TIMEOUT=11")
r = run(check_command, stderr=PIPE, env={}, cwd=project_dir, check=False)
assert not r.stderr
check_command = [sys.executable, "-c", "import nc_py_api\nassert nc_py_api.options.NPA_TIMEOUT_DAV is None"]
with open(env_file, "w") as env_f:
env_f.write("NPA_TIMEOUT_DAV=None")
r = run(check_command, stderr=PIPE, env={}, cwd=project_dir, check=False)
assert not r.stderr
check_command = [sys.executable, "-c", "import nc_py_api\nassert nc_py_api.options.NPA_TIMEOUT_DAV == 11"]
with open(env_file, "w") as env_f:
env_f.write("NPA_TIMEOUT_DAV=11")
r = run(check_command, stderr=PIPE, env={}, cwd=project_dir, check=False)
assert not r.stderr
check_command = [sys.executable, "-c", "import nc_py_api\nassert nc_py_api.options.NPA_NC_CERT is False"]
with open(env_file, "w") as env_f:
env_f.write("NPA_NC_CERT=False")
r = run(check_command, stderr=PIPE, env={}, cwd=project_dir, check=False)
assert not r.stderr
check_command = [sys.executable, "-c", "import nc_py_api\nassert nc_py_api.options.NPA_NC_CERT == ''"]
with open(env_file, "w") as env_f:
env_f.write('NPA_NC_CERT=""')
r = run(check_command, stderr=PIPE, env={}, cwd=project_dir, check=False)
assert not r.stderr
finally:
if os.path.exists(env_backup_file):
os.rename(env_backup_file, env_file)
def test_xdebug_session(nc_any):
nc_py_api.options.XDEBUG_SESSION = "12345"
new_nc = nc_py_api.Nextcloud() if isinstance(nc_any, nc_py_api.Nextcloud) else nc_py_api.NextcloudApp()
assert new_nc._session.adapter.cookies["XDEBUG_SESSION"] == "12345"
@mock.patch("nc_py_api.options.CHUNKED_UPLOAD_V2", False)
def test_chunked_upload(nc_any):
new_nc = nc_py_api.Nextcloud() if isinstance(nc_any, nc_py_api.Nextcloud) else nc_py_api.NextcloudApp()
assert new_nc._session.cfg.options.upload_chunk_v2 is False
def test_chunked_upload2(nc_any):
new_nc = (
nc_py_api.Nextcloud(chunked_upload_v2=False)
if isinstance(nc_any, nc_py_api.Nextcloud)
else nc_py_api.NextcloudApp(chunked_upload_v2=False)
)
assert new_nc._session.cfg.options.upload_chunk_v2 is False
new_nc = nc_py_api.Nextcloud() if isinstance(nc_any, nc_py_api.Nextcloud) else nc_py_api.NextcloudApp()
assert new_nc._session.cfg.options.upload_chunk_v2 is True
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/preferences_test.py 0000664 0002322 0002322 00000003210 14766056032 026510 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudException
def test_available(nc):
assert isinstance(nc.preferences.available, bool)
@pytest.mark.asyncio(scope="session")
async def test_available_async(anc):
assert isinstance(await anc.preferences.available, bool)
def test_preferences_set(nc):
if not nc.preferences.available:
pytest.skip("provisioning_api is not available")
nc.preferences.set_value("dav", key="user_status_automation", value="yes")
with pytest.raises(NextcloudException):
nc.preferences.set_value("non_existing_app", "some_cfg_name", "2")
@pytest.mark.asyncio(scope="session")
async def test_preferences_set_async(anc):
if not await anc.preferences.available:
pytest.skip("provisioning_api is not available")
await anc.preferences.set_value("dav", key="user_status_automation", value="yes")
with pytest.raises(NextcloudException):
await anc.preferences.set_value("non_existing_app", "some_cfg_name", "2")
def test_preferences_delete(nc):
if not nc.preferences.available:
pytest.skip("provisioning_api is not available")
nc.preferences.delete("dav", key="user_status_automation")
with pytest.raises(NextcloudException):
nc.preferences.delete("non_existing_app", "some_cfg_name")
@pytest.mark.asyncio(scope="session")
async def test_preferences_delete_async(anc):
if not await anc.preferences.available:
pytest.skip("provisioning_api is not available")
await anc.preferences.delete("dav", key="user_status_automation")
with pytest.raises(NextcloudException):
await anc.preferences.delete("non_existing_app", "some_cfg_name")
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/apps_test.py 0000664 0002322 0002322 00000011742 14766056032 025163 0 ustar debalance debalance import pytest
from nc_py_api.apps import ExAppInfo
APP_NAME = "files_trashbin"
def test_list_apps_types(nc):
assert isinstance(nc.apps.get_list(), list)
assert isinstance(nc.apps.get_list(enabled=True), list)
assert isinstance(nc.apps.get_list(enabled=False), list)
@pytest.mark.asyncio(scope="session")
async def test_list_apps_types_async(anc):
assert isinstance(await anc.apps.get_list(), list)
assert isinstance(await anc.apps.get_list(enabled=True), list)
assert isinstance(await anc.apps.get_list(enabled=False), list)
def test_list_apps(nc):
apps = nc.apps.get_list()
assert apps
assert APP_NAME in apps
@pytest.mark.asyncio(scope="session")
async def test_list_apps_async(anc):
apps = await anc.apps.get_list()
assert apps
assert APP_NAME in apps
def test_app_enable_disable(nc_client):
assert nc_client.apps.is_installed(APP_NAME) is True
if nc_client.apps.is_enabled(APP_NAME):
nc_client.apps.disable(APP_NAME)
assert nc_client.apps.is_disabled(APP_NAME) is True
assert nc_client.apps.is_enabled(APP_NAME) is False
assert nc_client.apps.is_installed(APP_NAME) is True
nc_client.apps.enable(APP_NAME)
assert nc_client.apps.is_enabled(APP_NAME) is True
assert nc_client.apps.is_installed(APP_NAME) is True
@pytest.mark.asyncio(scope="session")
async def test_app_enable_disable_async(anc_client):
assert await anc_client.apps.is_installed(APP_NAME) is True
if await anc_client.apps.is_enabled(APP_NAME):
await anc_client.apps.disable(APP_NAME)
assert await anc_client.apps.is_disabled(APP_NAME) is True
assert await anc_client.apps.is_enabled(APP_NAME) is False
assert await anc_client.apps.is_installed(APP_NAME) is True
await anc_client.apps.enable(APP_NAME)
assert await anc_client.apps.is_enabled(APP_NAME) is True
assert await anc_client.apps.is_installed(APP_NAME) is True
def test_is_installed_enabled(nc):
assert nc.apps.is_enabled(APP_NAME) != nc.apps.is_disabled(APP_NAME)
assert nc.apps.is_installed(APP_NAME)
@pytest.mark.asyncio(scope="session")
async def test_is_installed_enabled_async(anc):
assert await anc.apps.is_enabled(APP_NAME) != await anc.apps.is_disabled(APP_NAME)
assert await anc.apps.is_installed(APP_NAME)
def test_invalid_param(nc_any):
with pytest.raises(ValueError):
nc_any.apps.is_enabled("")
with pytest.raises(ValueError):
nc_any.apps.is_installed("")
with pytest.raises(ValueError):
nc_any.apps.is_disabled("")
with pytest.raises(ValueError):
nc_any.apps.enable("")
with pytest.raises(ValueError):
nc_any.apps.disable("")
with pytest.raises(ValueError):
nc_any.apps.ex_app_is_enabled("")
with pytest.raises(ValueError):
nc_any.apps.ex_app_is_disabled("")
with pytest.raises(ValueError):
nc_any.apps.ex_app_disable("")
with pytest.raises(ValueError):
nc_any.apps.ex_app_enable("")
@pytest.mark.asyncio(scope="session")
async def test_invalid_param_async(anc_any):
with pytest.raises(ValueError):
await anc_any.apps.is_enabled("")
with pytest.raises(ValueError):
await anc_any.apps.is_installed("")
with pytest.raises(ValueError):
await anc_any.apps.is_disabled("")
with pytest.raises(ValueError):
await anc_any.apps.enable("")
with pytest.raises(ValueError):
await anc_any.apps.disable("")
with pytest.raises(ValueError):
await anc_any.apps.ex_app_is_enabled("")
with pytest.raises(ValueError):
await anc_any.apps.ex_app_is_disabled("")
with pytest.raises(ValueError):
await anc_any.apps.ex_app_disable("")
with pytest.raises(ValueError):
await anc_any.apps.ex_app_enable("")
def _test_ex_app_get_list(ex_apps: list[ExAppInfo], enabled_ex_apps: list[ExAppInfo]):
assert isinstance(ex_apps, list)
assert "nc_py_api" in [i.app_id for i in ex_apps]
assert len(ex_apps) >= len(enabled_ex_apps)
for app in ex_apps:
assert isinstance(app.app_id, str)
assert isinstance(app.name, str)
assert isinstance(app.version, str)
assert isinstance(app.enabled, bool)
assert str(app).find("id=") != -1 and str(app).find("ver=") != -1
def test_ex_app_get_list(nc, nc_app):
enabled_ex_apps = nc.apps.ex_app_get_list(enabled=True)
assert isinstance(enabled_ex_apps, list)
for i in enabled_ex_apps:
assert i.enabled is True
assert "nc_py_api" in [i.app_id for i in enabled_ex_apps]
ex_apps = nc.apps.ex_app_get_list()
_test_ex_app_get_list(ex_apps, enabled_ex_apps)
@pytest.mark.asyncio(scope="session")
async def test_ex_app_get_list_async(anc, anc_app):
enabled_ex_apps = await anc.apps.ex_app_get_list(enabled=True)
assert isinstance(enabled_ex_apps, list)
for i in enabled_ex_apps:
assert i.enabled is True
assert "nc_py_api" in [i.app_id for i in enabled_ex_apps]
ex_apps = await anc.apps.ex_app_get_list()
_test_ex_app_get_list(ex_apps, enabled_ex_apps)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/notes_test.py 0000664 0002322 0002322 00000015116 14766056032 025347 0 ustar debalance debalance from datetime import datetime
import pytest
from nc_py_api import NextcloudException, notes
def test_settings(nc_any):
if nc_any.notes.available is False:
pytest.skip("Notes is not installed")
original_settings = nc_any.notes.get_settings()
assert isinstance(original_settings["file_suffix"], str)
assert isinstance(original_settings["notes_path"], str)
nc_any.notes.set_settings(file_suffix=".ncpa")
modified_settings = nc_any.notes.get_settings()
assert modified_settings["file_suffix"] == ".ncpa"
assert modified_settings["notes_path"] == original_settings["notes_path"]
nc_any.notes.set_settings(file_suffix=original_settings["file_suffix"])
modified_settings = nc_any.notes.get_settings()
assert modified_settings["file_suffix"] == original_settings["file_suffix"]
with pytest.raises(ValueError):
nc_any.notes.set_settings()
@pytest.mark.asyncio(scope="session")
async def test_settings_async(anc_any):
if await anc_any.notes.available is False:
pytest.skip("Notes is not installed")
original_settings = await anc_any.notes.get_settings()
assert isinstance(original_settings["file_suffix"], str)
assert isinstance(original_settings["notes_path"], str)
await anc_any.notes.set_settings(file_suffix=".ncpa")
modified_settings = await anc_any.notes.get_settings()
assert modified_settings["file_suffix"] == ".ncpa"
assert modified_settings["notes_path"] == original_settings["notes_path"]
await anc_any.notes.set_settings(file_suffix=original_settings["file_suffix"])
modified_settings = await anc_any.notes.get_settings()
assert modified_settings["file_suffix"] == original_settings["file_suffix"]
with pytest.raises(ValueError):
await anc_any.notes.set_settings()
def test_create_delete(nc_any):
if nc_any.notes.available is False:
pytest.skip("Notes is not installed")
unix_timestamp = (datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()
new_note = nc_any.notes.create(str(unix_timestamp))
nc_any.notes.delete(new_note)
_test_create_delete(new_note)
@pytest.mark.asyncio(scope="session")
async def test_create_delete_async(anc_any):
if await anc_any.notes.available is False:
pytest.skip("Notes is not installed")
unix_timestamp = (datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()
new_note = await anc_any.notes.create(str(unix_timestamp))
await anc_any.notes.delete(new_note)
_test_create_delete(new_note)
def _test_create_delete(new_note: notes.Note):
assert isinstance(new_note.note_id, int)
assert isinstance(new_note.etag, str)
assert isinstance(new_note.title, str)
assert isinstance(new_note.content, str)
assert isinstance(new_note.category, str)
assert new_note.readonly is False
assert new_note.favorite is False
assert isinstance(new_note.last_modified, datetime)
assert str(new_note).find("title=") != -1
def test_get_update_note(nc_any):
if nc_any.notes.available is False:
pytest.skip("Notes is not installed")
for i in nc_any.notes.get_list():
nc_any.notes.delete(i)
assert not nc_any.notes.get_list()
unix_timestamp = (datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()
new_note = nc_any.notes.create(str(unix_timestamp))
try:
all_notes = nc_any.notes.get_list()
assert all_notes[0] == new_note
assert not nc_any.notes.get_list(etag=True)
assert nc_any.notes.get_list()[0] == new_note
assert nc_any.notes.by_id(new_note) == new_note
updated_note = nc_any.notes.update(new_note, content="content")
assert updated_note.content == "content"
all_notes = nc_any.notes.get_list()
assert all_notes[0].content == "content"
all_notes_no_content = nc_any.notes.get_list(no_content=True)
assert all_notes_no_content[0].content == ""
assert nc_any.notes.by_id(new_note).content == "content"
with pytest.raises(NextcloudException):
assert nc_any.notes.update(new_note, content="should be rejected")
new_note = nc_any.notes.update(new_note, content="should not be rejected", overwrite=True)
nc_any.notes.update(new_note, category="test_category", favorite=True)
new_note = nc_any.notes.by_id(new_note)
assert new_note.favorite is True
assert new_note.category == "test_category"
finally:
nc_any.notes.delete(new_note)
@pytest.mark.asyncio(scope="session")
async def test_get_update_note_async(anc_any):
if await anc_any.notes.available is False:
pytest.skip("Notes is not installed")
for i in await anc_any.notes.get_list():
await anc_any.notes.delete(i)
assert not await anc_any.notes.get_list()
unix_timestamp = (datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()
new_note = await anc_any.notes.create(str(unix_timestamp))
try:
all_notes = await anc_any.notes.get_list()
assert all_notes[0] == new_note
assert not await anc_any.notes.get_list(etag=True)
assert (await anc_any.notes.get_list())[0] == new_note
assert await anc_any.notes.by_id(new_note) == new_note
updated_note = await anc_any.notes.update(new_note, content="content")
assert updated_note.content == "content"
all_notes = await anc_any.notes.get_list()
assert all_notes[0].content == "content"
all_notes_no_content = await anc_any.notes.get_list(no_content=True)
assert all_notes_no_content[0].content == ""
assert (await anc_any.notes.by_id(new_note)).content == "content"
with pytest.raises(NextcloudException):
assert await anc_any.notes.update(new_note, content="should be rejected")
new_note = await anc_any.notes.update(new_note, content="should not be rejected", overwrite=True)
await anc_any.notes.update(new_note, category="test_category", favorite=True)
new_note = await anc_any.notes.by_id(new_note)
assert new_note.favorite is True
assert new_note.category == "test_category"
finally:
await anc_any.notes.delete(new_note)
def test_update_note_invalid_param(nc_any):
if nc_any.notes.available is False:
pytest.skip("Notes is not installed")
with pytest.raises(ValueError):
nc_any.notes.update(notes.Note({"id": 0, "etag": "42242"}))
@pytest.mark.asyncio(scope="session")
async def test_update_note_invalid_param_async(anc_any):
if await anc_any.notes.available is False:
pytest.skip("Notes is not installed")
with pytest.raises(ValueError):
await anc_any.notes.update(notes.Note({"id": 0, "etag": "42242"}))
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/logs_test.py 0000664 0002322 0002322 00000010705 14766056032 025162 0 ustar debalance debalance import logging
from copy import deepcopy
from unittest import mock
import pytest
from nc_py_api.ex_app import LogLvl, setup_nextcloud_logging
def test_loglvl_values():
assert LogLvl.FATAL == 4
assert LogLvl.ERROR == 3
assert LogLvl.WARNING == 2
assert LogLvl.INFO == 1
assert LogLvl.DEBUG == 0
def test_log_success(nc_app):
nc_app.log(LogLvl.FATAL, "log success")
@pytest.mark.asyncio(scope="session")
async def test_log_success_async(anc_app):
await anc_app.log(LogLvl.FATAL, "log success")
def test_loglvl_str(nc_app):
nc_app.log("1", "lolglvl in str: should be written") # noqa
@pytest.mark.asyncio(scope="session")
async def test_loglvl_str_async(anc_app):
await anc_app.log("1", "lolglvl in str: should be written") # noqa
def test_invalid_log_level(nc_app):
with pytest.raises(ValueError):
nc_app.log(5, "wrong log level") # noqa
@pytest.mark.asyncio(scope="session")
async def test_invalid_log_level_async(anc_app):
with pytest.raises(ValueError):
await anc_app.log(5, "wrong log level") # noqa
def test_empty_log(nc_app):
nc_app.log(LogLvl.FATAL, "")
@pytest.mark.asyncio(scope="session")
async def test_empty_log_async(anc_app):
await anc_app.log(LogLvl.FATAL, "")
def test_loglvl_equal(nc_app):
current_log_lvl = nc_app.capabilities["app_api"].get("loglevel", LogLvl.FATAL)
nc_app.log(current_log_lvl, "log should be written")
@pytest.mark.asyncio(scope="session")
async def test_loglvl_equal_async(anc_app):
current_log_lvl = (await anc_app.capabilities)["app_api"].get("loglevel", LogLvl.FATAL)
await anc_app.log(current_log_lvl, "log should be written")
def test_loglvl_less(nc_app):
current_log_lvl = nc_app.capabilities["app_api"].get("loglevel", LogLvl.FATAL)
if current_log_lvl == LogLvl.DEBUG:
pytest.skip("Log lvl to low")
with mock.patch("tests.conftest.NC_APP._session.ocs") as ocs:
nc_app.log(int(current_log_lvl) - 1, "will not be sent") # noqa
ocs.assert_not_called()
nc_app.log(current_log_lvl, "will be sent")
assert ocs.call_count > 0
@pytest.mark.asyncio(scope="session")
async def test_loglvl_less_async(anc_app):
current_log_lvl = (await anc_app.capabilities)["app_api"].get("loglevel", LogLvl.FATAL)
if current_log_lvl == LogLvl.DEBUG:
pytest.skip("Log lvl to low")
with mock.patch("tests.conftest.NC_APP_ASYNC._session.ocs") as ocs:
await anc_app.log(int(current_log_lvl) - 1, "will not be sent") # noqa
ocs.assert_not_called()
await anc_app.log(current_log_lvl, "will be sent")
assert ocs.call_count > 0
def test_log_without_app_api(nc_app):
srv_capabilities = deepcopy(nc_app.capabilities)
srv_version = deepcopy(nc_app.srv_version)
log_lvl = srv_capabilities["app_api"].pop("loglevel")
srv_capabilities.pop("app_api")
patched_capabilities = {"capabilities": srv_capabilities, "version": srv_version}
with (
mock.patch.dict("tests.conftest.NC_APP._session._capabilities", patched_capabilities, clear=True),
mock.patch("tests.conftest.NC_APP._session.ocs") as ocs,
):
nc_app.log(log_lvl, "will not be sent")
ocs.assert_not_called()
@pytest.mark.asyncio(scope="session")
async def test_log_without_app_api_async(anc_app):
srv_capabilities = deepcopy(await anc_app.capabilities)
srv_version = deepcopy(await anc_app.srv_version)
log_lvl = srv_capabilities["app_api"].pop("loglevel")
srv_capabilities.pop("app_api")
patched_capabilities = {"capabilities": srv_capabilities, "version": srv_version}
with (
mock.patch.dict("tests.conftest.NC_APP_ASYNC._session._capabilities", patched_capabilities, clear=True),
mock.patch("tests.conftest.NC_APP_ASYNC._session.ocs") as ocs,
):
await anc_app.log(log_lvl, "will not be sent")
ocs.assert_not_called()
def test_logging(nc_app):
log_handler = setup_nextcloud_logging("my_logger")
logger = logging.getLogger("my_logger")
logger.fatal("testing logging.fatal")
try:
a = b # noqa
except Exception: # noqa
logger.exception("testing logger.exception")
logger.removeHandler(log_handler)
def test_recursive_logging(nc_app):
logging.getLogger("httpx").setLevel(logging.DEBUG)
log_handler = setup_nextcloud_logging()
logger = logging.getLogger()
logger.fatal("testing logging.fatal")
logger.removeHandler(log_handler)
logging.getLogger("httpx").setLevel(logging.ERROR)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/users_test.py 0000664 0002322 0002322 00000023460 14766056032 025361 0 ustar debalance debalance import contextlib
import datetime
from io import BytesIO
from os import environ
import pytest
from PIL import Image
from nc_py_api import (
AsyncNextcloudApp,
NextcloudApp,
NextcloudException,
NextcloudExceptionNotFound,
users,
)
def _test_get_user_info(admin: users.UserInfo, current_user: users.UserInfo):
for i in (
"user_id",
"email",
"display_name",
"storage_location",
"backend",
"manager",
"phone",
"address",
"website",
"twitter",
"fediverse",
"organisation",
"role",
"headline",
"biography",
"language",
"locale",
"notify_email",
):
assert getattr(current_user, i) == getattr(admin, i)
assert isinstance(getattr(current_user, i), str)
assert isinstance(getattr(admin, i), str)
assert admin.enabled is True
assert admin.enabled == current_user.enabled
assert admin.profile_enabled is True
assert admin.profile_enabled == current_user.profile_enabled
assert isinstance(admin.last_login, datetime.datetime)
assert isinstance(current_user.last_login, datetime.datetime)
assert isinstance(admin.subadmin, list)
assert isinstance(admin.quota, dict)
assert isinstance(admin.additional_mail, list)
assert isinstance(admin.groups, list)
assert isinstance(admin.backend_capabilities, dict)
assert admin.display_name == "admin"
assert str(admin).find("last_login=") != -1
def test_get_user_info(nc):
admin = nc.users.get_user("admin")
current_user = nc.users.get_user()
_test_get_user_info(admin, current_user)
@pytest.mark.asyncio(scope="session")
async def test_get_user_info_async(anc):
admin = await anc.users.get_user("admin")
current_user = await anc.users.get_user()
_test_get_user_info(admin, current_user)
def test_get_current_user_wo_user(nc):
orig_user = nc._session.user
try:
nc._session.set_user("")
if isinstance(nc, NextcloudApp):
with pytest.raises(NextcloudException):
nc.users.get_user()
else:
assert isinstance(nc.users.get_user(), users.UserInfo)
finally:
nc._session.set_user(orig_user)
@pytest.mark.asyncio(scope="session")
async def test_get_current_user_wo_user_async(anc):
orig_user = await anc._session.user
try:
anc._session.set_user("")
if isinstance(anc, AsyncNextcloudApp):
with pytest.raises(NextcloudException):
await anc.users.get_user()
else:
assert isinstance(await anc.users.get_user(), users.UserInfo)
finally:
anc._session.set_user(orig_user)
def test_get_user_404(nc):
with pytest.raises(NextcloudException):
nc.users.get_user("non existing user")
@pytest.mark.asyncio(scope="session")
async def test_get_user_404_async(anc):
with pytest.raises(NextcloudException):
await anc.users.get_user("non existing user")
def test_create_user_with_groups(nc_client):
admin_group = nc_client.users_groups.get_members("admin")
assert environ["TEST_ADMIN_ID"] in admin_group
assert environ["TEST_USER_ID"] not in admin_group
@pytest.mark.asyncio(scope="session")
async def test_create_user_with_groups_async(anc_client):
admin_group = await anc_client.users_groups.get_members("admin")
assert environ["TEST_ADMIN_ID"] in admin_group
assert environ["TEST_USER_ID"] not in admin_group
def test_create_user_no_name_mail(nc_client):
test_user_name = "test_create_user_no_name_mail"
with contextlib.suppress(NextcloudException):
nc_client.users.delete(test_user_name)
with pytest.raises(ValueError):
nc_client.users.create(test_user_name)
with pytest.raises(ValueError):
nc_client.users.create(test_user_name, password="")
with pytest.raises(ValueError):
nc_client.users.create(test_user_name, email="")
@pytest.mark.asyncio(scope="session")
async def test_create_user_no_name_mail_async(anc_client):
test_user_name = "test_create_user_no_name_mail"
with contextlib.suppress(NextcloudException):
await anc_client.users.delete(test_user_name)
with pytest.raises(ValueError):
await anc_client.users.create(test_user_name)
with pytest.raises(ValueError):
await anc_client.users.create(test_user_name, password="")
with pytest.raises(ValueError):
await anc_client.users.create(test_user_name, email="")
def test_delete_user(nc_client):
test_user_name = "test_delete_user"
with contextlib.suppress(NextcloudException):
nc_client.users.create(test_user_name, password="az1dcaNG4c42")
nc_client.users.delete(test_user_name)
with pytest.raises(NextcloudExceptionNotFound):
nc_client.users.delete(test_user_name)
@pytest.mark.asyncio(scope="session")
async def test_delete_user_async(anc_client):
test_user_name = "test_delete_user"
with contextlib.suppress(NextcloudException):
await anc_client.users.create(test_user_name, password="az1dcaNG4c42")
await anc_client.users.delete(test_user_name)
with pytest.raises(NextcloudExceptionNotFound):
await anc_client.users.delete(test_user_name)
def test_users_get_list(nc, nc_client):
_users = nc.users.get_list()
assert isinstance(_users, list)
assert nc.user in _users
assert environ["TEST_ADMIN_ID"] in _users
assert environ["TEST_USER_ID"] in _users
_users = nc.users.get_list(limit=1)
assert len(_users) == 1
assert _users[0] != nc.users.get_list(limit=1, offset=1)[0]
_users = nc.users.get_list(mask=environ["TEST_ADMIN_ID"])
assert len(_users) == 1
@pytest.mark.asyncio(scope="session")
async def test_users_get_list_async(anc, anc_client):
_users = await anc.users.get_list()
assert isinstance(_users, list)
assert await anc.user in _users
assert environ["TEST_ADMIN_ID"] in _users
assert environ["TEST_USER_ID"] in _users
_users = await anc.users.get_list(limit=1)
assert len(_users) == 1
assert _users[0] != (await anc.users.get_list(limit=1, offset=1))[0]
_users = await anc.users.get_list(mask=environ["TEST_ADMIN_ID"])
assert len(_users) == 1
def test_enable_disable_user(nc_client):
test_user_name = "test_enable_disable_user"
with contextlib.suppress(NextcloudException):
nc_client.users.create(test_user_name, password="az1dcaNG4c42")
nc_client.users.disable(test_user_name)
assert nc_client.users.get_user(test_user_name).enabled is False
nc_client.users.enable(test_user_name)
assert nc_client.users.get_user(test_user_name).enabled is True
nc_client.users.delete(test_user_name)
@pytest.mark.asyncio(scope="session")
async def test_enable_disable_user_async(anc_client):
test_user_name = "test_enable_disable_user"
with contextlib.suppress(NextcloudException):
await anc_client.users.create(test_user_name, password="az1dcaNG4c42")
await anc_client.users.disable(test_user_name)
assert (await anc_client.users.get_user(test_user_name)).enabled is False
await anc_client.users.enable(test_user_name)
assert (await anc_client.users.get_user(test_user_name)).enabled is True
await anc_client.users.delete(test_user_name)
def test_user_editable_fields(nc):
editable_fields = nc.users.editable_fields()
assert isinstance(editable_fields, list)
assert editable_fields
@pytest.mark.asyncio(scope="session")
async def test_user_editable_fields_async(anc):
editable_fields = await anc.users.editable_fields()
assert isinstance(editable_fields, list)
assert editable_fields
def test_edit_user(nc_client):
nc_client.users.edit(nc_client.user, address="Le Pame", email="admino@gmx.net")
current_user = nc_client.users.get_user()
assert current_user.address == "Le Pame"
assert current_user.email == "admino@gmx.net"
nc_client.users.edit(nc_client.user, address="", email="admin@gmx.net")
current_user = nc_client.users.get_user()
assert current_user.address == ""
assert current_user.email == "admin@gmx.net"
@pytest.mark.asyncio(scope="session")
async def test_edit_user_async(anc_client):
await anc_client.users.edit(await anc_client.user, address="Le Pame", email="admino@gmx.net")
current_user = await anc_client.users.get_user()
assert current_user.address == "Le Pame"
assert current_user.email == "admino@gmx.net"
await anc_client.users.edit(await anc_client.user, address="", email="admin@gmx.net")
current_user = await anc_client.users.get_user()
assert current_user.address == ""
assert current_user.email == "admin@gmx.net"
def test_resend_user_email(nc_client):
nc_client.users.resend_welcome_email(nc_client.user)
@pytest.mark.asyncio(scope="session")
async def test_resend_user_email_async(anc_client):
await anc_client.users.resend_welcome_email(await anc_client.user)
def test_avatars(nc):
im = nc.users.get_avatar()
im_64 = nc.users.get_avatar(size=64)
im_black = nc.users.get_avatar(dark=True)
im_64_black = nc.users.get_avatar(size=64, dark=True)
assert len(im_64) < len(im)
assert len(im_64_black) < len(im_black)
for i in (im, im_64, im_black, im_64_black):
img = Image.open(BytesIO(i))
img.load()
with pytest.raises(NextcloudException):
nc.users.get_avatar("not_existing_user")
@pytest.mark.asyncio(scope="session")
async def test_avatars_async(anc):
im = await anc.users.get_avatar()
im_64 = await anc.users.get_avatar(size=64)
im_black = await anc.users.get_avatar(dark=True)
im_64_black = await anc.users.get_avatar(size=64, dark=True)
assert len(im_64) < len(im)
assert len(im_64_black) < len(im_black)
for i in (im, im_64, im_black, im_64_black):
img = Image.open(BytesIO(i))
img.load()
with pytest.raises(NextcloudException):
await anc.users.get_avatar("not_existing_user")
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/theming_test.py 0000664 0002322 0002322 00000003200 14766056032 025641 0 ustar debalance debalance from copy import deepcopy
import pytest
from nc_py_api._theming import convert_str_color # noqa
def test_get_theme(nc):
theme = nc.theme
assert isinstance(theme["name"], str)
assert isinstance(theme["url"], str)
assert isinstance(theme["slogan"], str)
assert isinstance(theme["color"], tuple)
for i in range(3):
assert isinstance(theme["color"][i], int)
assert isinstance(theme["color_text"], tuple)
for i in range(3):
assert isinstance(theme["color_text"][i], int)
assert isinstance(theme["color_element"], tuple)
for i in range(3):
assert isinstance(theme["color_element"][i], int)
assert isinstance(theme["color_element_bright"], tuple)
for i in range(3):
assert isinstance(theme["color_element_bright"][i], int)
assert isinstance(theme["color_element_dark"], tuple)
for i in range(3):
assert isinstance(theme["color_element_dark"][i], int)
assert isinstance(theme["logo"], str)
assert isinstance(theme["background"], str)
assert isinstance(theme["background_plain"], bool)
assert isinstance(theme["background_default"], bool)
@pytest.mark.asyncio(scope="session")
async def test_get_theme_async(anc_any):
theme = await anc_any.theme
assert isinstance(theme["name"], str)
assert isinstance(theme["url"], str)
assert isinstance(theme["slogan"], str)
def test_convert_str_color_values_in(nc_any):
theme = deepcopy(nc_any.theme)
for i in ("#", ""):
theme["color"] = i
assert convert_str_color(theme, "color") == (0, 0, 0)
theme.pop("color")
assert convert_str_color(theme, "color") == (0, 0, 0)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/taskprocessing_provider_test.py 0000664 0002322 0002322 00000005320 14766056032 031164 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudException, NextcloudExceptionNotFound
from nc_py_api.ex_app.providers.task_processing import TaskProcessingProvider
@pytest.mark.require_nc(major=30)
def test_task_processing_provider(nc_app):
provider_info = TaskProcessingProvider(
id=f"test_id", name=f"Test Display Name", task_type="core:text2image" # noqa
)
nc_app.providers.task_processing.register(provider_info)
nc_app.providers.task_processing.unregister(provider_info.id)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.providers.task_processing.unregister(provider_info.id, not_fail=False)
nc_app.providers.task_processing.unregister(provider_info.id)
nc_app.providers.task_processing.register(provider_info)
assert not nc_app.providers.task_processing.next_task(["test_id"], ["core:text2image"])
assert not nc_app.providers.task_processing.set_progress(9999, 0.5)
assert not nc_app.providers.task_processing.report_result(9999, error_message="no such task")
with pytest.raises(NextcloudException):
nc_app.providers.task_processing.upload_result_file(9999, b"00")
nc_app.providers.task_processing.unregister(provider_info.id, not_fail=False)
@pytest.mark.asyncio(scope="session")
@pytest.mark.require_nc(major=30)
async def test_task_processing_async(anc_app):
provider_info = TaskProcessingProvider(
id=f"test_id", name=f"Test Display Name", task_type="core:text2image" # noqa
)
await anc_app.providers.task_processing.register(provider_info)
await anc_app.providers.task_processing.unregister(provider_info.id)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.providers.task_processing.unregister(provider_info.id, not_fail=False)
await anc_app.providers.task_processing.unregister(provider_info.id)
await anc_app.providers.task_processing.register(provider_info)
assert not await anc_app.providers.task_processing.next_task(["test_id"], ["core:text2image"])
assert not await anc_app.providers.task_processing.set_progress(9999, 0.5)
assert not await anc_app.providers.task_processing.report_result(9999, error_message="no such task")
with pytest.raises(NextcloudException):
await anc_app.providers.task_processing.upload_result_file(9999, b"00")
await anc_app.providers.task_processing.unregister(provider_info.id, not_fail=False)
@pytest.mark.require_nc(major=30)
def test_task_processing_provider_fail_report(nc_app):
nc_app.providers.task_processing.report_result(999999)
@pytest.mark.asyncio(scope="session")
@pytest.mark.require_nc(major=30)
async def test_task_processing_provider_fail_report_async(anc_app):
await anc_app.providers.task_processing.report_result(999999)
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/occ_commands_test.py 0000664 0002322 0002322 00000010324 14766056032 026640 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudExceptionNotFound
def test_occ_commands_registration(nc_app):
nc_app.occ_commands.register(
"test_occ_name",
"/some_url",
)
result = nc_app.occ_commands.get_entry("test_occ_name")
assert result.name == "test_occ_name"
assert result.description == ""
assert result.action_handler == "some_url"
assert result.hidden is False
assert result.usages == []
assert result.arguments == []
assert result.options == []
nc_app.occ_commands.register(
"test_occ_name2",
"some_url2",
description="desc",
arguments=[
{
"name": "argument_name",
"mode": "required",
"description": "Description of the argument",
"default": "default_value",
},
],
options=[],
)
result2 = nc_app.occ_commands.get_entry("test_occ_name2")
assert result2.name == "test_occ_name2"
assert result2.description == "desc"
assert result2.action_handler == "some_url2"
assert result2.hidden is False
assert result2.usages == []
assert result2.arguments == [
{
"name": "argument_name",
"mode": "required",
"description": "Description of the argument",
"default": "default_value",
}
]
assert result2.options == []
nc_app.occ_commands.register(
"test_occ_name",
description="new desc",
callback_url="/new_url",
)
result = nc_app.occ_commands.get_entry("test_occ_name")
assert result.name == "test_occ_name"
assert result.description == "new desc"
assert result.action_handler == "new_url"
nc_app.occ_commands.unregister(result.name)
with pytest.raises(NextcloudExceptionNotFound):
nc_app.occ_commands.unregister(result.name, not_fail=False)
nc_app.occ_commands.unregister(result.name)
nc_app.occ_commands.unregister(result2.name, not_fail=False)
assert nc_app.occ_commands.get_entry(result2.name) is None
assert str(result).find("name=") != -1
@pytest.mark.asyncio(scope="session")
async def test_occ_commands_registration_async(anc_app):
await anc_app.occ_commands.register(
"test_occ_name",
"/some_url",
)
result = await anc_app.occ_commands.get_entry("test_occ_name")
assert result.name == "test_occ_name"
assert result.description == ""
assert result.action_handler == "some_url"
assert result.hidden is False
assert result.usages == []
assert result.arguments == []
assert result.options == []
await anc_app.occ_commands.register(
"test_occ_name2",
"some_url2",
description="desc",
arguments=[
{
"name": "argument_name",
"mode": "required",
"description": "Description of the argument",
"default": "default_value",
},
],
options=[],
)
result2 = await anc_app.occ_commands.get_entry("test_occ_name2")
assert result2.name == "test_occ_name2"
assert result2.description == "desc"
assert result2.action_handler == "some_url2"
assert result2.hidden is False
assert result2.usages == []
assert result2.arguments == [
{
"name": "argument_name",
"mode": "required",
"description": "Description of the argument",
"default": "default_value",
}
]
assert result2.options == []
await anc_app.occ_commands.register(
"test_occ_name",
description="new desc",
callback_url="/new_url",
)
result = await anc_app.occ_commands.get_entry("test_occ_name")
assert result.name == "test_occ_name"
assert result.description == "new desc"
assert result.action_handler == "new_url"
await anc_app.occ_commands.unregister(result.name)
with pytest.raises(NextcloudExceptionNotFound):
await anc_app.occ_commands.unregister(result.name, not_fail=False)
await anc_app.occ_commands.unregister(result.name)
await anc_app.occ_commands.unregister(result2.name, not_fail=False)
assert await anc_app.occ_commands.get_entry(result2.name) is None
assert str(result).find("name=") != -1
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/appcfg_prefs_ex_test.py 0000664 0002322 0002322 00000032333 14766056032 027352 0 ustar debalance debalance import pytest
from nc_py_api import NextcloudExceptionNotFound
from ..conftest import NC_APP, NC_APP_ASYNC
if NC_APP is None:
pytest.skip("Need App mode", allow_module_level=True)
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_get_value_invalid(class_to_test):
with pytest.raises(ValueError):
class_to_test.get_value("")
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_get_value_invalid_async(class_to_test):
with pytest.raises(ValueError):
await class_to_test.get_value("")
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_get_values_invalid(class_to_test):
assert class_to_test.get_values([]) == []
with pytest.raises(ValueError):
class_to_test.get_values([""])
with pytest.raises(ValueError):
class_to_test.get_values(["", "k"])
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_get_values_invalid_async(class_to_test):
assert await class_to_test.get_values([]) == []
with pytest.raises(ValueError):
await class_to_test.get_values([""])
with pytest.raises(ValueError):
await class_to_test.get_values(["", "k"])
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_set_empty_key(class_to_test):
with pytest.raises(ValueError):
class_to_test.set_value("", "some value")
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_set_empty_key_async(class_to_test):
with pytest.raises(ValueError):
await class_to_test.set_value("", "some value")
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_delete_invalid(class_to_test):
class_to_test.delete([])
with pytest.raises(ValueError):
class_to_test.delete([""])
with pytest.raises(ValueError):
class_to_test.delete(["", "k"])
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_delete_invalid_async(class_to_test):
await class_to_test.delete([])
with pytest.raises(ValueError):
await class_to_test.delete([""])
with pytest.raises(ValueError):
await class_to_test.delete(["", "k"])
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_get_default(class_to_test):
assert class_to_test.get_value("non_existing_key", default="alice") == "alice"
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_get_default_async(class_to_test):
assert await class_to_test.get_value("non_existing_key", default="alice") == "alice"
@pytest.mark.parametrize("value", ("0", "1", "12 3", ""))
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_set_delete(value, class_to_test):
class_to_test.delete("test_key")
assert class_to_test.get_value("test_key") is None
class_to_test.set_value("test_key", value)
assert class_to_test.get_value("test_key") == value
class_to_test.set_value("test_key", "zzz")
assert class_to_test.get_value("test_key") == "zzz"
class_to_test.delete("test_key")
assert class_to_test.get_value("test_key") is None
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("value", ("0", "1", "12 3", ""))
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_set_delete_async(value, class_to_test):
await class_to_test.delete("test_key")
assert await class_to_test.get_value("test_key") is None
await class_to_test.set_value("test_key", value)
assert await class_to_test.get_value("test_key") == value
await class_to_test.set_value("test_key", "zzz")
assert await class_to_test.get_value("test_key") == "zzz"
await class_to_test.delete("test_key")
assert await class_to_test.get_value("test_key") is None
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_delete(class_to_test):
class_to_test.set_value("test_key", "123")
assert class_to_test.get_value("test_key")
class_to_test.delete("test_key")
assert class_to_test.get_value("test_key") is None
class_to_test.delete("test_key")
class_to_test.delete(["test_key"])
with pytest.raises(NextcloudExceptionNotFound):
class_to_test.delete("test_key", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
class_to_test.delete(["test_key"], not_fail=False)
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_delete_async(class_to_test):
await class_to_test.set_value("test_key", "123")
assert await class_to_test.get_value("test_key")
await class_to_test.delete("test_key")
assert await class_to_test.get_value("test_key") is None
await class_to_test.delete("test_key")
await class_to_test.delete(["test_key"])
with pytest.raises(NextcloudExceptionNotFound):
await class_to_test.delete("test_key", not_fail=False)
with pytest.raises(NextcloudExceptionNotFound):
await class_to_test.delete(["test_key"], not_fail=False)
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_get(class_to_test):
class_to_test.delete(["test key", "test key2"])
assert len(class_to_test.get_values(["test key", "test key2"])) == 0
class_to_test.set_value("test key", "123")
assert len(class_to_test.get_values(["test key", "test key2"])) == 1
class_to_test.set_value("test key2", "123")
assert len(class_to_test.get_values(["test key", "test key2"])) == 2
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_get_async(class_to_test):
await class_to_test.delete(["test key", "test key2"])
assert len(await class_to_test.get_values(["test key", "test key2"])) == 0
await class_to_test.set_value("test key", "123")
assert len(await class_to_test.get_values(["test key", "test key2"])) == 1
await class_to_test.set_value("test key2", "123")
assert len(await class_to_test.get_values(["test key", "test key2"])) == 2
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_multiply_delete(class_to_test):
class_to_test.set_value("test_key", "123")
class_to_test.set_value("test_key2", "123")
assert len(class_to_test.get_values(["test_key", "test_key2"])) == 2
class_to_test.delete(["test_key", "test_key2"])
assert len(class_to_test.get_values(["test_key", "test_key2"])) == 0
class_to_test.delete(["test_key", "test_key2"])
class_to_test.set_value("test_key", "123")
assert len(class_to_test.get_values(["test_key", "test_key2"])) == 1
class_to_test.delete(["test_key", "test_key2"])
assert len(class_to_test.get_values(["test_key", "test_key2"])) == 0
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_multiply_delete_async(class_to_test):
await class_to_test.set_value("test_key", "123")
await class_to_test.set_value("test_key2", "123")
assert len(await class_to_test.get_values(["test_key", "test_key2"])) == 2
await class_to_test.delete(["test_key", "test_key2"])
assert len(await class_to_test.get_values(["test_key", "test_key2"])) == 0
await class_to_test.delete(["test_key", "test_key2"])
await class_to_test.set_value("test_key", "123")
assert len(await class_to_test.get_values(["test_key", "test_key2"])) == 1
await class_to_test.delete(["test_key", "test_key2"])
assert len(await class_to_test.get_values(["test_key", "test_key2"])) == 0
@pytest.mark.parametrize("key", ("k", "k y", " "))
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_get_non_existing(key, class_to_test):
class_to_test.delete(key)
assert class_to_test.get_value(key) is None
assert class_to_test.get_values([key]) == []
assert len(class_to_test.get_values([key, "non_existing_key"])) == 0
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("key", ("k", "k y", " "))
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_get_non_existing_async(key, class_to_test):
await class_to_test.delete(key)
assert await class_to_test.get_value(key) is None
assert await class_to_test.get_values([key]) == []
assert len(await class_to_test.get_values([key, "non_existing_key"])) == 0
@pytest.mark.parametrize("class_to_test", (NC_APP.appconfig_ex, NC_APP.preferences_ex))
def test_cfg_ex_get_typing(class_to_test):
class_to_test.set_value("test key", "123")
class_to_test.set_value("test key2", "321")
r = class_to_test.get_values(["test key", "test key2"])
assert isinstance(r, list)
assert r[0].key == "test key"
assert r[1].key == "test key2"
assert r[0].value == "123"
assert r[1].value == "321"
@pytest.mark.asyncio(scope="session")
@pytest.mark.parametrize("class_to_test", (NC_APP_ASYNC.appconfig_ex, NC_APP_ASYNC.preferences_ex))
async def test_cfg_ex_get_typing_async(class_to_test):
await class_to_test.set_value("test key", "123")
await class_to_test.set_value("test key2", "321")
r = await class_to_test.get_values(["test key", "test key2"])
assert isinstance(r, list)
assert r[0].key == "test key"
assert r[1].key == "test key2"
assert r[0].value == "123"
assert r[1].value == "321"
def test_appcfg_sensitive(nc_app):
appcfg = nc_app.appconfig_ex
appcfg.delete("test_key")
appcfg.set_value("test_key", "123", sensitive=True)
assert appcfg.get_value("test_key") == "123"
assert appcfg.get_values(["test_key"])[0].value == "123"
appcfg.delete("test_key")
# next code tests `sensitive` value from the `AppAPI`
params = {"configKey": "test_key", "configValue": "123"}
result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert not result["sensitive"] # by default if sensitive value is unspecified it is False
appcfg.delete("test_key")
params = {"configKey": "test_key", "configValue": "123", "sensitive": True}
result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert result["configkey"] == "test_key"
assert result["configvalue"] == "123"
assert bool(result["sensitive"]) is True
params.pop("sensitive") # if we not specify value, AppEcosystem should not change it.
result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert result["configkey"] == "test_key"
assert result["configvalue"] == "123"
assert bool(result["sensitive"]) is True
params["sensitive"] = False
result = nc_app._session.ocs("POST", f"{nc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert result["configkey"] == "test_key"
assert result["configvalue"] == "123"
assert bool(result["sensitive"]) is False
@pytest.mark.asyncio(scope="session")
async def test_appcfg_sensitive_async(anc_app):
appcfg = anc_app.appconfig_ex
await appcfg.delete("test_key")
await appcfg.set_value("test_key", "123", sensitive=True)
assert await appcfg.get_value("test_key") == "123"
assert (await appcfg.get_values(["test_key"]))[0].value == "123"
await appcfg.delete("test_key")
# next code tests `sensitive` value from the `AppAPI`
params = {"configKey": "test_key", "configValue": "123"}
result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert not result["sensitive"] # by default if sensitive value is unspecified it is False
await appcfg.delete("test_key")
params = {"configKey": "test_key", "configValue": "123", "sensitive": True}
result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert result["configkey"] == "test_key"
assert result["configvalue"] == "123"
assert bool(result["sensitive"]) is True
params.pop("sensitive") # if we not specify value, AppEcosystem should not change it.
result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert result["configkey"] == "test_key"
assert result["configvalue"] == "123"
assert bool(result["sensitive"]) is True
params["sensitive"] = False
result = await anc_app._session.ocs("POST", f"{anc_app._session.ae_url}/{appcfg._url_suffix}", json=params)
assert result["configkey"] == "test_key"
assert result["configvalue"] == "123"
assert bool(result["sensitive"]) is False
cloud-py-api-nc_py_api-d4a32c6/tests/actual_tests/conftest.py 0000664 0002322 0002322 00000010756 14766056032 025012 0 ustar debalance debalance import contextlib
from io import BytesIO
from os import environ, path
from random import randbytes
import pytest
from PIL import Image
from nc_py_api import Nextcloud, NextcloudApp, NextcloudException, _session # noqa
from ..conftest import NC_CLIENT
_TEST_FAILED_INCREMENTAL: dict[str, dict[tuple[int, ...], str]] = {}
@pytest.fixture(scope="session")
def rand_bytes() -> bytes:
"""Returns 64 bytes from `test_64_bytes.bin` file."""
return randbytes(64)
def init_filesystem_for_user(nc_any, rand_bytes):
"""
/test_empty_dir
/test_empty_dir_in_dir/test_empty_child_dir
/test_dir
/test_dir/subdir/
/test_dir/subdir/test_empty_text.txt
/test_dir/subdir/test_64_bytes.bin
/test_dir/subdir/test_12345_text.txt
/test_dir/subdir/test_generated_image.png **Favorite**
/test_dir/test_empty_child_dir/
/test_dir/test_empty_text.txt
/test_dir/test_64_bytes.bin
/test_dir/test_12345_text.txt
/test_dir/test_generated_image.png **Favorite**
/test_empty_text.txt
/test_64_bytes.bin
/test_12345_text.txt
/test_generated_image.png **Favorite**
/test_dir_tmp
/test_###_dir
"""
clean_filesystem_for_user(nc_any)
im = BytesIO()
Image.linear_gradient("L").resize((768, 768)).save(im, format="PNG")
nc_any.files.mkdir("/test_empty_dir")
nc_any.files.makedirs("/test_empty_dir_in_dir/test_empty_child_dir")
nc_any.files.makedirs("/test_dir/subdir")
nc_any.files.mkdir("/test_dir/test_empty_child_dir/")
nc_any.files.mkdir("/test_dir_tmp")
nc_any.files.mkdir("/test_###_dir")
def init_folder(folder: str = ""):
nc_any.files.upload(path.join(folder, "test_empty_text.txt"), content=b"")
nc_any.files.upload(path.join(folder, "test_64_bytes.bin"), content=rand_bytes)
nc_any.files.upload(path.join(folder, "test_12345_text.txt"), content="12345")
im.seek(0)
nc_any.files.upload(path.join(folder, "test_generated_image.png"), content=im.read())
nc_any.files.setfav(path.join(folder, "test_generated_image.png"), True)
init_folder()
init_folder("test_dir")
init_folder("test_dir/subdir")
def clean_filesystem_for_user(nc_any):
clean_up_list = [
"test_empty_dir",
"test_empty_dir_in_dir",
"test_dir",
"test_dir_tmp",
"test_empty_text.txt",
"test_64_bytes.bin",
"test_12345_text.txt",
"test_generated_image.png",
"test_###_dir",
]
for i in clean_up_list:
nc_any.files.delete(i, not_fail=True)
@pytest.fixture(autouse=True, scope="session")
def tear_up_down(nc_any, rand_bytes):
if NC_CLIENT:
# create two additional groups
environ["TEST_GROUP_BOTH"] = "test_nc_py_api_group_both"
environ["TEST_GROUP_USER"] = "test_nc_py_api_group_user"
with contextlib.suppress(NextcloudException):
NC_CLIENT.users_groups.delete(environ["TEST_GROUP_BOTH"])
with contextlib.suppress(NextcloudException):
NC_CLIENT.users_groups.delete(environ["TEST_GROUP_USER"])
NC_CLIENT.users_groups.create(group_id=environ["TEST_GROUP_BOTH"])
NC_CLIENT.users_groups.create(group_id=environ["TEST_GROUP_USER"])
# create two additional users
environ["TEST_ADMIN_ID"] = "test_nc_py_api_admin"
environ["TEST_ADMIN_PASS"] = "az1dcaNG4c42"
environ["TEST_USER_ID"] = "test_nc_py_api_user"
environ["TEST_USER_PASS"] = "DC89GvaR42lk"
with contextlib.suppress(NextcloudException):
NC_CLIENT.users.delete(environ["TEST_ADMIN_ID"])
with contextlib.suppress(NextcloudException):
NC_CLIENT.users.delete(environ["TEST_USER_ID"])
NC_CLIENT.users.create(
environ["TEST_ADMIN_ID"], password=environ["TEST_ADMIN_PASS"], groups=["admin", environ["TEST_GROUP_BOTH"]]
)
NC_CLIENT.users.create(
environ["TEST_USER_ID"],
password=environ["TEST_USER_PASS"],
groups=[environ["TEST_GROUP_BOTH"], environ["TEST_GROUP_USER"]],
display_name=environ["TEST_USER_ID"],
)
init_filesystem_for_user(nc_any, rand_bytes) # currently we initialize filesystem only for admin
yield
clean_filesystem_for_user(nc_any)
if NC_CLIENT:
NC_CLIENT.users.delete(environ["TEST_ADMIN_ID"])
NC_CLIENT.users.delete(environ["TEST_USER_ID"])
NC_CLIENT.users_groups.delete(environ["TEST_GROUP_BOTH"])
NC_CLIENT.users_groups.delete(environ["TEST_GROUP_USER"])
cloud-py-api-nc_py_api-d4a32c6/tests/_app_security_checks.py 0000664 0002322 0002322 00000003022 14766056032 024644 0 ustar debalance debalance from base64 import b64encode
from os import environ
from sys import argv
import httpx
def sign_request(req_headers: dict, secret=None, user: str = ""):
app_secret = secret if secret is not None else environ["APP_SECRET"]
req_headers["AUTHORIZATION-APP-API"] = b64encode(f"{user}:{app_secret}".encode("UTF=8"))
# params: app base url
if __name__ == "__main__":
request_url = argv[1] + "/sec_check?value=1"
headers = {}
result = httpx.put(request_url, headers=headers)
assert result.status_code == 401 # Missing headers
headers.update(
{
"AA-VERSION": environ.get("AA_VERSION", "1.0.0"),
"EX-APP-ID": environ.get("APP_ID", "nc_py_api"),
"EX-APP-VERSION": environ.get("APP_VERSION", "1.0.0"),
}
)
sign_request(headers)
result = httpx.put(request_url, headers=headers)
assert result.status_code == 200
# Invalid AA-SIGNATURE
sign_request(headers, secret="xxx")
result = httpx.put(request_url, headers=headers)
assert result.status_code == 401
sign_request(headers)
result = httpx.put(request_url, headers=headers)
assert result.status_code == 200
# Invalid EX-APP-ID
old_app_name = headers.get("EX-APP-ID")
headers["EX-APP-ID"] = "unknown_app"
sign_request(headers)
result = httpx.put(request_url, headers=headers)
assert result.status_code == 401
headers["EX-APP-ID"] = old_app_name
sign_request(headers)
result = httpx.put(request_url, headers=headers)
assert result.status_code == 200
cloud-py-api-nc_py_api-d4a32c6/tests/_install_init_handler_models.py 0000664 0002322 0002322 00000002444 14766056032 026355 0 ustar debalance debalance from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from nc_py_api import NextcloudApp, ex_app
INVALID_URL = "https://invalid_url"
MODEL_NAME1 = "MBZUAI/LaMini-T5-61M"
MODEL_NAME2 = "https://huggingface.co/MBZUAI/LaMini-T5-61M/resolve/main/pytorch_model.bin"
MODEL_NAME2_http = "http://huggingface.co/MBZUAI/LaMini-T5-61M/resolve/main/pytorch_model.bin"
INVALID_PATH = "https://huggingface.co/invalid_path"
SOME_FILE = "https://raw.githubusercontent.com/cloud-py-api/nc_py_api/main/README.md"
@asynccontextmanager
async def lifespan(_app: FastAPI):
ex_app.set_handlers(
APP,
enabled_handler,
models_to_fetch={
INVALID_URL: {},
MODEL_NAME1: {},
MODEL_NAME2: {},
MODEL_NAME2_http: {},
INVALID_PATH: {},
SOME_FILE: {},
},
)
yield
APP = FastAPI(lifespan=lifespan)
def enabled_handler(enabled: bool, _nc: NextcloudApp) -> str:
if enabled:
try:
assert ex_app.get_model_path(MODEL_NAME1)
except Exception: # noqa
return "model1 not found"
assert Path("pytorch_model.bin").is_file()
return ""
if __name__ == "__main__":
ex_app.run_app("_install_init_handler_models:APP", log_level="warning")
cloud-py-api-nc_py_api-d4a32c6/tests/conftest.py 0000664 0002322 0002322 00000012413 14766056032 022307 0 ustar debalance debalance from os import environ
from typing import Optional, Union
import pytest
from nc_py_api import ( # noqa
AsyncNextcloud,
AsyncNextcloudApp,
Nextcloud,
NextcloudApp,
_session,
)
from . import gfixture_set_env # noqa
_TEST_FAILED_INCREMENTAL: dict[str, dict[tuple[int, ...], str]] = {}
NC_CLIENT = None if environ.get("SKIP_NC_CLIENT_TESTS", False) else Nextcloud()
NC_CLIENT_ASYNC = None if environ.get("SKIP_NC_CLIENT_TESTS", False) else AsyncNextcloud()
if environ.get("SKIP_AA_TESTS", False):
NC_APP = None
NC_APP_ASYNC = None
else:
NC_APP = NextcloudApp(user="admin")
NC_APP_ASYNC = AsyncNextcloudApp(user="admin")
if "app_api" not in NC_APP.capabilities:
NC_APP = None
NC_APP_ASYNC = None
if NC_CLIENT is None and NC_APP is None:
raise EnvironmentError("Tests require at least Nextcloud or NextcloudApp.")
@pytest.fixture(scope="session")
def nc_version() -> _session.ServerVersion:
return NC_APP.srv_version if NC_APP else NC_CLIENT.srv_version
@pytest.fixture(scope="session")
def nc_client() -> Optional[Nextcloud]:
if NC_CLIENT is None:
pytest.skip("Need Client mode")
return NC_CLIENT
@pytest.fixture(scope="session")
def anc_client() -> Optional[AsyncNextcloud]:
if NC_CLIENT_ASYNC is None:
pytest.skip("Need Async Client mode")
return NC_CLIENT_ASYNC
@pytest.fixture(scope="session")
def nc_app() -> Optional[NextcloudApp]:
if NC_APP is None:
pytest.skip("Need App mode")
return NC_APP
@pytest.fixture(scope="session")
def anc_app() -> Optional[AsyncNextcloudApp]:
if NC_APP_ASYNC is None:
pytest.skip("Need Async App mode")
return NC_APP_ASYNC
@pytest.fixture(scope="session")
def nc_any() -> Union[Nextcloud, NextcloudApp]:
"""Marks a test to run once for any of the modes."""
return NC_APP if NC_APP else NC_CLIENT
@pytest.fixture(scope="session")
def anc_any() -> Union[AsyncNextcloud, AsyncNextcloudApp]:
"""Marks a test to run once for any of the modes."""
return NC_APP_ASYNC if NC_APP_ASYNC else NC_CLIENT_ASYNC
@pytest.fixture(scope="session")
def nc(request) -> Union[Nextcloud, NextcloudApp]:
"""Marks a test to run for both modes if possible."""
return request.param
@pytest.fixture(scope="session")
def anc(request) -> Union[AsyncNextcloud, AsyncNextcloudApp]:
"""Marks a test to run for both modes if possible."""
return request.param
def pytest_generate_tests(metafunc):
if "nc" in metafunc.fixturenames:
values_ids = []
values = []
if NC_CLIENT is not None:
values.append(NC_CLIENT)
values_ids.append("client")
if NC_APP is not None:
values.append(NC_APP)
values_ids.append("app")
metafunc.parametrize("nc", values, ids=values_ids)
if "anc" in metafunc.fixturenames:
values_ids = []
values = []
if NC_CLIENT_ASYNC is not None:
values.append(NC_CLIENT_ASYNC)
values_ids.append("client_async")
if NC_APP_ASYNC is not None:
values.append(NC_APP_ASYNC)
values_ids.append("app_async")
metafunc.parametrize("anc", values, ids=values_ids)
def pytest_collection_modifyitems(items):
for item in items:
require_nc = [i for i in item.own_markers if i.name == "require_nc"]
if require_nc:
min_major = require_nc[0].kwargs["major"]
min_minor = require_nc[0].kwargs.get("minor", 0)
srv_ver = NC_APP.srv_version if NC_APP else NC_CLIENT.srv_version
if srv_ver["major"] < min_major:
item.add_marker(pytest.mark.skip(reason=f"Need NC>={min_major}"))
elif srv_ver["major"] == min_major and srv_ver["minor"] < min_minor:
item.add_marker(pytest.mark.skip(reason=f"Need NC>={min_major}.{min_minor}"))
def pytest_runtest_makereport(item, call):
if "incremental" in item.keywords and call.excinfo is not None:
# the test has failed
cls_name = str(item.cls) # retrieve the class name of the test
# Retrieve the index of the test (if parametrize is used in combination with incremental)
parametrize_index = tuple(item.callspec.indices.values()) if hasattr(item, "callspec") else ()
test_name = item.originalname or item.name # retrieve the name of the test function
# store in _test_failed_incremental the original name of the failed test
_TEST_FAILED_INCREMENTAL.setdefault(cls_name, {}).setdefault(parametrize_index, test_name)
def pytest_runtest_setup(item):
if "incremental" in item.keywords:
cls_name = str(item.cls)
if cls_name in _TEST_FAILED_INCREMENTAL: # check if a previous test has failed for this class
# retrieve the index of the test (if parametrize is used in combination with incremental)
parametrize_index = tuple(item.callspec.indices.values()) if hasattr(item, "callspec") else ()
# retrieve the name of the first test function to fail for this class name and index
test_name = _TEST_FAILED_INCREMENTAL[cls_name].get(parametrize_index, None)
# if name found, test has failed for the combination of class name & test name
if test_name is not None:
pytest.xfail("previous test failed ({})".format(test_name))
cloud-py-api-nc_py_api-d4a32c6/img/ 0000775 0002322 0002322 00000000000 14766056032 017521 5 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/img/icon.svg 0000664 0002322 0002322 00000246775 14766056032 021217 0 ustar debalance debalance
cloud-py-api-nc_py_api-d4a32c6/.pre-commit-config.yaml 0000664 0002322 0002322 00000002363 14766056032 023232 0 ustar debalance debalance ci:
skip: [pylint]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-yaml
- id: check-toml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: mixed-line-ending
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
- id: isort
files: >-
(?x)^(
nc_py_api/|
benchmarks/|
examples/|
tests/
)
- repo: https://github.com/psf/black
rev: 24.10.0
hooks:
- id: black
files: >-
(?x)^(
nc_py_api/|
benchmarks/|
examples/|
tests/
)
- repo: https://github.com/tox-dev/pyproject-fmt
rev: v2.5.0
hooks:
- id: pyproject-fmt
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.3
hooks:
- id: ruff
- repo: local
hooks:
- id: pylint
name: pylint
entry: pylint "nc_py_api/"
language: system
types: [ python ]
pass_filenames: false
args:
[
"-rn", # Only display messages
"-sn", # Don't display the score
]
cloud-py-api-nc_py_api-d4a32c6/benchmarks/ 0000775 0002322 0002322 00000000000 14766056032 021062 5 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/benchmarks/aa_overhead_dav_download_stream.py 0000664 0002322 0002322 00000002354 14766056032 027772 0 ustar debalance debalance from getpass import getuser
from io import BytesIO
from random import randbytes
from time import perf_counter
from typing import Any
import matplotlib.pyplot as plt
from aa_overhead_common import measure_overhead, os_id
from nc_py_api import Nextcloud, NextcloudApp
ITERS = 10
CACHE_SESS = False
def measure_download_100mb(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]:
__result = None
medium_file_name = "100Mb.bin"
medium_file = BytesIO()
medium_file.write(randbytes(100 * 1024 * 1024))
medium_file.seek(0)
nc_obj.files.upload_stream(medium_file_name, medium_file)
start_time = perf_counter()
for _ in range(ITERS):
medium_file.seek(0)
nc_obj.files.download2stream(medium_file_name, medium_file)
nc_obj._session.init_adapter_dav(restart=not CACHE_SESS) # noqa
end_time = perf_counter()
nc_obj.files.delete(medium_file_name, not_fail=True)
return __result, round((end_time - start_time) / ITERS, 3)
if __name__ == "__main__":
title = f"download stream 100mb, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}"
measure_overhead(measure_download_100mb, title)
plt.savefig(f"results/dav_download_stream_100mb__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200)
cloud-py-api-nc_py_api-d4a32c6/benchmarks/conf.py 0000664 0002322 0002322 00000003452 14766056032 022365 0 ustar debalance debalance from nc_py_api import Nextcloud, NextcloudApp
NC_CFGS = {
"http://stable26.local": {
# NC_APP
"secret": "12345",
"app_id": "nc_py_api",
"app_version": "1.0.0",
"user": "admin",
# NC
"nc_auth_user": "admin",
"nc_auth_pass": "admin",
"nc_auth_app_pass": "kFEfH-cqR8T-563tB-8CAjd-96LNj",
},
"http://stable27.local": {
# NC_APP
"secret": "12345",
"app_id": "nc_py_api",
"app_version": "1.0.0",
"user": "admin",
# NC
"nc_auth_user": "admin",
"nc_auth_pass": "admin",
"nc_auth_app_pass": "Npi8A-LAtWM-WaPm8-CPpEA-jq9od",
},
"http://nextcloud.local": {
# NC_APP
"secret": "12345",
"app_id": "nc_py_api",
"app_version": "1.0.0",
"user": "admin",
# NC
"nc_auth_user": "admin",
"nc_auth_pass": "admin",
"nc_auth_app_pass": "yEaoa-5Z96a-Z7SHs-44spP-EkC4o",
},
}
def init_nc(url, cfg) -> Nextcloud | None:
if cfg.get("nc_auth_user", "") and cfg.get("nc_auth_pass", ""):
return Nextcloud(nc_auth_user=cfg["nc_auth_user"], nc_auth_pass=cfg["nc_auth_pass"], nextcloud_url=url)
return None
def init_nc_by_app_pass(url, cfg) -> Nextcloud | None:
if cfg.get("nc_auth_user", "") and cfg.get("nc_auth_app_pass", ""):
return Nextcloud(nc_auth_user=cfg["nc_auth_user"], nc_auth_pass=cfg["nc_auth_app_pass"], nextcloud_url=url)
return None
def init_nc_app(url, cfg) -> NextcloudApp | None:
if cfg.get("secret", "") and cfg.get("app_id", ""):
return NextcloudApp(
app_id=cfg["app_id"],
app_version=cfg["app_version"],
app_secret=cfg["secret"],
nextcloud_url=url,
user=cfg["user"],
)
return None
cloud-py-api-nc_py_api-d4a32c6/benchmarks/aa_overhead_dav_upload_stream.py 0000664 0002322 0002322 00000002214 14766056032 027442 0 ustar debalance debalance from getpass import getuser
from io import BytesIO
from random import randbytes
from time import perf_counter
from typing import Any
import matplotlib.pyplot as plt
from aa_overhead_common import measure_overhead, os_id
from nc_py_api import Nextcloud, NextcloudApp
ITERS = 10
CACHE_SESS = False
def measure_upload_100mb(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]:
__result = None
medium_file_name = "100Mb.bin"
medium_file = BytesIO()
medium_file.write(randbytes(100 * 1024 * 1024))
start_time = perf_counter()
for _ in range(ITERS):
medium_file.seek(0)
nc_obj.files.upload_stream(medium_file_name, medium_file)
nc_obj._session.init_adapter_dav(restart=not CACHE_SESS) # noqa
end_time = perf_counter()
nc_obj.files.delete(medium_file_name, not_fail=True)
return __result, round((end_time - start_time) / ITERS, 3)
if __name__ == "__main__":
title = f"upload stream 100mb, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}"
measure_overhead(measure_upload_100mb, title)
plt.savefig(f"results/dav_upload_stream_100mb__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200)
cloud-py-api-nc_py_api-d4a32c6/benchmarks/aa_overhead_dav_download.py 0000664 0002322 0002322 00000002101 14766056032 026405 0 ustar debalance debalance from getpass import getuser
from random import randbytes
from time import perf_counter
from typing import Any
import matplotlib.pyplot as plt
from aa_overhead_common import measure_overhead, os_id
from nc_py_api import Nextcloud, NextcloudApp
ITERS = 30
CACHE_SESS = False
def measure_download_1mb(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]:
__result = None
small_file_name = "1Mb.bin"
small_file = randbytes(1024 * 1024)
nc_obj.files.upload(small_file_name, small_file)
start_time = perf_counter()
for _ in range(ITERS):
nc_obj.files.download(small_file_name)
nc_obj._session.init_adapter_dav(restart=not CACHE_SESS) # noqa
end_time = perf_counter()
nc_obj.files.delete(small_file_name, not_fail=True)
return __result, round((end_time - start_time) / ITERS, 3)
if __name__ == "__main__":
title = f"download 1mb, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}"
measure_overhead(measure_download_1mb, title)
plt.savefig(f"results/dav_download_1mb__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200)
cloud-py-api-nc_py_api-d4a32c6/benchmarks/aa_overhead_ocs.py 0000664 0002322 0002322 00000001556 14766056032 024545 0 ustar debalance debalance from getpass import getuser
from time import perf_counter
from typing import Any
import matplotlib.pyplot as plt
from aa_overhead_common import measure_overhead, os_id
from nc_py_api import Nextcloud, NextcloudApp
ITERS = 100
CACHE_SESS = False
def measure_get_details(nc_obj: Nextcloud | NextcloudApp) -> [Any, float]:
__result = None
start_time = perf_counter()
for _ in range(ITERS):
__result = nc_obj.users.get_details()
nc_obj._session.init_adapter(restart=not CACHE_SESS) # noqa
end_time = perf_counter()
return __result, round((end_time - start_time) / ITERS, 3)
if __name__ == "__main__":
title = f"OCS: get_user, {ITERS} iters, CACHE={CACHE_SESS} - {os_id()}"
measure_overhead(measure_get_details, title)
plt.savefig(f"results/ocs_user_get_details__cache{int(CACHE_SESS)}_iters{ITERS}__{getuser()}.png", dpi=200)
cloud-py-api-nc_py_api-d4a32c6/benchmarks/results/ 0000775 0002322 0002322 00000000000 14766056032 022563 5 ustar debalance debalance cloud-py-api-nc_py_api-d4a32c6/benchmarks/results/dav_download_1mb__cache0_iters30__shurik.png 0000664 0002322 0002322 00000222224 14766056032 033134 0 ustar debalance debalance PNG
IHDR jy 9tEXtSoftware Matplotlib version3.7.2, https://matplotlib.org/)] pHYs nu> IDATxwX6{Rc+b("`A^bר1FMPch%[b4bXQ*("6;]4\g<3