[test] Codex tests (#3911)
This commit is contained in:
@@ -0,0 +1,430 @@
|
||||
from types import SimpleNamespace
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from starlette.requests import Request
|
||||
|
||||
from lnbits.core.crud.db_versions import get_db_version, update_migration_version
|
||||
from lnbits.core.crud.extensions import (
|
||||
create_installed_extension,
|
||||
get_installed_extension,
|
||||
get_user_extension,
|
||||
)
|
||||
from lnbits.core.crud.users import get_account
|
||||
from lnbits.core.crud.wallets import create_wallet
|
||||
from lnbits.core.models import Account, CreateInvoice
|
||||
from lnbits.core.models.extensions import (
|
||||
CreateExtension,
|
||||
CreateExtensionReview,
|
||||
Extension,
|
||||
ExtensionConfig,
|
||||
ExtensionRelease,
|
||||
InstallableExtension,
|
||||
PayToEnableInfo,
|
||||
ReleasePaymentInfo,
|
||||
UserExtensionInfo,
|
||||
)
|
||||
from lnbits.core.models.users import AccountId
|
||||
from lnbits.core.services.payments import create_wallet_invoice
|
||||
from lnbits.core.services.users import create_user_account
|
||||
from lnbits.core.views.extension_api import (
|
||||
api_activate_extension,
|
||||
api_deactivate_extension,
|
||||
api_disable_extension,
|
||||
api_enable_extension,
|
||||
api_extension_details,
|
||||
api_get_user_extensions,
|
||||
api_install_extension,
|
||||
api_uninstall_extension,
|
||||
api_update_pay_to_enable,
|
||||
create_extension_review,
|
||||
delete_extension_db,
|
||||
extensions,
|
||||
get_extension_release,
|
||||
get_extension_releases,
|
||||
get_extension_reviews,
|
||||
get_extension_reviews_tags,
|
||||
get_pay_to_enable_invoice,
|
||||
get_pay_to_install_invoice,
|
||||
)
|
||||
from tests.helpers import make_extension_release, make_installable_extension
|
||||
|
||||
|
||||
class _MockHTTPResponse:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
json_data=None,
|
||||
text: str = "",
|
||||
status_code: int = 200,
|
||||
is_error: bool = False,
|
||||
):
|
||||
self._json_data = json_data
|
||||
self.text = text
|
||||
self.status_code = status_code
|
||||
self.is_error = is_error
|
||||
|
||||
def json(self):
|
||||
return self._json_data
|
||||
|
||||
def raise_for_status(self):
|
||||
if self.status_code >= 400:
|
||||
raise ValueError(self.text or "request failed")
|
||||
|
||||
|
||||
class _MockHTTPClient:
|
||||
def __init__(self, responses: dict[str, _MockHTTPResponse]):
|
||||
self.responses = responses
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *_args):
|
||||
return False
|
||||
|
||||
async def get(self, url: str):
|
||||
return self.responses[url]
|
||||
|
||||
async def post(self, url: str, json=None):
|
||||
return self.responses[url]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_extension_api_install_details_and_release_endpoints(mocker):
|
||||
ext_id = f"ext_{uuid4().hex[:8]}"
|
||||
release = make_extension_release(ext_id)
|
||||
create_data = CreateExtension(
|
||||
ext_id=ext_id,
|
||||
archive=release.archive,
|
||||
source_repo=release.source_repo,
|
||||
version=release.version,
|
||||
)
|
||||
|
||||
mocker.patch.object(
|
||||
InstallableExtension,
|
||||
"get_extension_release",
|
||||
mocker.AsyncMock(return_value=release),
|
||||
)
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.install_extension",
|
||||
mocker.AsyncMock(return_value=Extension(code=ext_id, is_valid=True)),
|
||||
)
|
||||
activate_mock = mocker.patch(
|
||||
"lnbits.core.views.extension_api.activate_extension", mocker.AsyncMock()
|
||||
)
|
||||
|
||||
installed = await api_install_extension(create_data)
|
||||
assert installed.code == ext_id
|
||||
activate_mock.assert_awaited_once()
|
||||
|
||||
mocker.patch.object(
|
||||
InstallableExtension,
|
||||
"get_extension_releases",
|
||||
mocker.AsyncMock(return_value=[release]),
|
||||
)
|
||||
mocker.patch.object(
|
||||
ExtensionRelease,
|
||||
"fetch_release_details",
|
||||
mocker.AsyncMock(return_value={"description": "Extension details"}),
|
||||
)
|
||||
details = await api_extension_details(ext_id, release.details_link or "")
|
||||
assert details["description"] == "Extension details"
|
||||
assert details["icon"] == release.icon
|
||||
assert details["repo"] == release.repo
|
||||
|
||||
installed_ext = make_installable_extension(
|
||||
ext_id,
|
||||
payments=[
|
||||
ReleasePaymentInfo(
|
||||
amount=55,
|
||||
pay_link=release.pay_link,
|
||||
payment_hash=f"payment_{uuid4().hex[:8]}",
|
||||
)
|
||||
],
|
||||
)
|
||||
await create_installed_extension(installed_ext)
|
||||
releases = await get_extension_releases(ext_id)
|
||||
assert releases[0].paid_sats == 55
|
||||
|
||||
config = ExtensionConfig(
|
||||
name=ext_id,
|
||||
short_description="Config",
|
||||
min_lnbits_version="0.1.0",
|
||||
max_lnbits_version=None,
|
||||
)
|
||||
mocker.patch.object(
|
||||
ExtensionConfig,
|
||||
"fetch_github_release_config",
|
||||
mocker.AsyncMock(return_value=config),
|
||||
)
|
||||
release_info = await get_extension_release("org", ext_id, "v1.0.0")
|
||||
assert release_info["is_version_compatible"] is True
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_extension_api_pay_to_enable_and_catalog_views(mocker, admin_user):
|
||||
regular_user = await create_user_account(
|
||||
Account(
|
||||
id=uuid4().hex,
|
||||
username=f"user_{uuid4().hex[:8]}",
|
||||
email=f"user_{uuid4().hex[:8]}@lnbits.com",
|
||||
)
|
||||
)
|
||||
admin_account = await get_account(admin_user.id)
|
||||
assert admin_account is not None
|
||||
admin_wallet = await create_wallet(
|
||||
user_id=admin_account.id, wallet_name="extension sales"
|
||||
)
|
||||
|
||||
ext_id = f"paid_{uuid4().hex[:8]}"
|
||||
await create_installed_extension(
|
||||
make_installable_extension(
|
||||
ext_id,
|
||||
pay_to_enable=PayToEnableInfo(
|
||||
required=True, amount=10, wallet=admin_wallet.id
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
updated = await api_update_pay_to_enable(
|
||||
ext_id,
|
||||
PayToEnableInfo(required=True, amount=21, wallet=admin_wallet.id),
|
||||
account=admin_account,
|
||||
)
|
||||
assert updated.success is True
|
||||
stored_extension = await get_installed_extension(ext_id)
|
||||
assert stored_extension is not None
|
||||
assert stored_extension.meta is not None
|
||||
assert stored_extension.meta.pay_to_enable is not None
|
||||
assert stored_extension.meta.pay_to_enable.amount == 21
|
||||
|
||||
enable_invoice = await create_wallet_invoice(
|
||||
admin_wallet.id, CreateInvoice(out=False, amount=21, memo="enable extension")
|
||||
)
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.create_invoice",
|
||||
mocker.AsyncMock(return_value=enable_invoice),
|
||||
)
|
||||
invoice_response = await get_pay_to_enable_invoice(
|
||||
ext_id,
|
||||
PayToEnableInfo(amount=21),
|
||||
account_id=AccountId(id=regular_user.id),
|
||||
)
|
||||
assert invoice_response["payment_hash"] == enable_invoice.payment_hash
|
||||
|
||||
user_ext = await get_user_extension(regular_user.id, ext_id)
|
||||
assert user_ext is not None
|
||||
assert user_ext.extra is not None
|
||||
assert user_ext.extra.payment_hash_to_enable == enable_invoice.payment_hash
|
||||
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.get_valid_extensions",
|
||||
mocker.AsyncMock(return_value=[Extension(code=ext_id, is_valid=True)]),
|
||||
)
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.check_transaction_status",
|
||||
mocker.AsyncMock(return_value=SimpleNamespace(paid=True)),
|
||||
)
|
||||
|
||||
enabled = await api_enable_extension(ext_id, AccountId(id=regular_user.id))
|
||||
assert enabled.success is True
|
||||
user_ext = await get_user_extension(regular_user.id, ext_id)
|
||||
assert user_ext is not None
|
||||
assert user_ext.active is True
|
||||
assert user_ext.extra == UserExtensionInfo(
|
||||
payment_hash_to_enable=enable_invoice.payment_hash,
|
||||
paid_to_enable=True,
|
||||
)
|
||||
|
||||
disabled = await api_disable_extension(ext_id, AccountId(id=regular_user.id))
|
||||
assert disabled.success is True
|
||||
disabled_again = await api_disable_extension(ext_id, AccountId(id=regular_user.id))
|
||||
assert disabled_again.success is True
|
||||
assert "already disabled" in disabled_again.message
|
||||
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.get_valid_extensions",
|
||||
mocker.AsyncMock(
|
||||
return_value=[
|
||||
Extension(code=ext_id, is_valid=True, name="Paid Extension"),
|
||||
Extension(code="other", is_valid=True),
|
||||
]
|
||||
),
|
||||
)
|
||||
visible_extensions = await api_get_user_extensions(AccountId(id=regular_user.id))
|
||||
assert [ext.code for ext in visible_extensions] == [ext_id]
|
||||
|
||||
catalog_entry = make_installable_extension(
|
||||
ext_id,
|
||||
pay_to_enable=PayToEnableInfo(required=True, amount=21, wallet=admin_wallet.id),
|
||||
)
|
||||
mocker.patch.object(
|
||||
InstallableExtension,
|
||||
"get_installable_extensions",
|
||||
mocker.AsyncMock(return_value=[catalog_entry]),
|
||||
)
|
||||
catalog = await extensions(AccountId(id=regular_user.id))
|
||||
catalog_item = next(item for item in catalog if item["id"] == ext_id)
|
||||
assert catalog_item["payToEnable"]["wallet"] is None
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_extension_api_activate_uninstall_install_invoice_and_cleanup(mocker):
|
||||
base_ext = f"base_{uuid4().hex[:8]}"
|
||||
dependent_ext = f"dependent_{uuid4().hex[:8]}"
|
||||
uninstall_ext = f"uninstall_{uuid4().hex[:8]}"
|
||||
db_ext = f"db_{uuid4().hex[:8]}"
|
||||
|
||||
await create_installed_extension(make_installable_extension(base_ext))
|
||||
await create_installed_extension(
|
||||
make_installable_extension(dependent_ext, dependencies=[base_ext])
|
||||
)
|
||||
await create_installed_extension(make_installable_extension(uninstall_ext))
|
||||
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.get_valid_extensions",
|
||||
mocker.AsyncMock(
|
||||
return_value=[
|
||||
Extension(code=base_ext, is_valid=True, name="Base"),
|
||||
Extension(code=dependent_ext, is_valid=True, name="Dependent"),
|
||||
Extension(code=uninstall_ext, is_valid=True, name="Remove"),
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
with pytest.raises(HTTPException, match="depends on this one"):
|
||||
await api_uninstall_extension(base_ext)
|
||||
|
||||
uninstall_mock = mocker.patch(
|
||||
"lnbits.core.views.extension_api.uninstall_extension", mocker.AsyncMock()
|
||||
)
|
||||
uninstalled = await api_uninstall_extension(uninstall_ext)
|
||||
assert uninstalled.success is True
|
||||
uninstall_mock.assert_awaited_once_with(uninstall_ext)
|
||||
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.get_valid_extension",
|
||||
mocker.AsyncMock(return_value=Extension(code=base_ext, is_valid=True)),
|
||||
)
|
||||
activate_mock = mocker.patch(
|
||||
"lnbits.core.views.extension_api.activate_extension", mocker.AsyncMock()
|
||||
)
|
||||
deactivate_mock = mocker.patch(
|
||||
"lnbits.core.views.extension_api.deactivate_extension", mocker.AsyncMock()
|
||||
)
|
||||
activated = await api_activate_extension(base_ext)
|
||||
assert activated.success is True
|
||||
deactivated = await api_deactivate_extension(base_ext)
|
||||
assert deactivated.success is True
|
||||
activate_mock.assert_awaited_once()
|
||||
deactivate_mock.assert_awaited_once()
|
||||
|
||||
owner = await create_user_account(
|
||||
Account(
|
||||
id=uuid4().hex,
|
||||
username=f"user_{uuid4().hex[:8]}",
|
||||
email=f"user_{uuid4().hex[:8]}@lnbits.com",
|
||||
)
|
||||
)
|
||||
wallet = owner.wallets[0]
|
||||
install_invoice = await create_wallet_invoice(
|
||||
wallet.id, CreateInvoice(out=False, amount=33, memo="install extension")
|
||||
)
|
||||
release = make_extension_release(base_ext, version="2.0.0")
|
||||
payment_info = ReleasePaymentInfo(
|
||||
amount=33,
|
||||
pay_link=release.pay_link,
|
||||
payment_hash=install_invoice.payment_hash,
|
||||
payment_request=install_invoice.bolt11,
|
||||
)
|
||||
mocker.patch.object(
|
||||
InstallableExtension,
|
||||
"get_extension_release",
|
||||
mocker.AsyncMock(return_value=release),
|
||||
)
|
||||
mocker.patch.object(
|
||||
ExtensionRelease,
|
||||
"fetch_release_payment_info",
|
||||
mocker.AsyncMock(return_value=payment_info),
|
||||
)
|
||||
invoice = await get_pay_to_install_invoice(
|
||||
base_ext,
|
||||
CreateExtension(
|
||||
ext_id=base_ext,
|
||||
archive=release.archive,
|
||||
source_repo=release.source_repo,
|
||||
version=release.version,
|
||||
cost_sats=33,
|
||||
),
|
||||
)
|
||||
assert invoice.payment_hash == install_invoice.payment_hash
|
||||
|
||||
await update_migration_version(None, db_ext, 1)
|
||||
drop_mock = mocker.patch(
|
||||
"lnbits.core.views.extension_api.drop_extension_db", mocker.AsyncMock()
|
||||
)
|
||||
deleted = await delete_extension_db(db_ext)
|
||||
assert deleted.success is True
|
||||
drop_mock.assert_awaited_once_with(ext_id=db_ext)
|
||||
assert await get_db_version(db_ext) is None
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_extension_api_review_endpoints(mocker):
|
||||
ext_id = f"review_{uuid4().hex[:8]}"
|
||||
reviews_base = "https://demo.lnbits.com/paidreviews/api/v1/AdFzLjzuKFLsdk4Bcnff6r"
|
||||
tags_url = f"{reviews_base}/tags"
|
||||
reviews_url = f"{reviews_base}/reviews/{ext_id}?offset=0&limit=5"
|
||||
create_review_url = f"{reviews_base}/reviews"
|
||||
request = Request(
|
||||
{
|
||||
"type": "http",
|
||||
"method": "GET",
|
||||
"path": f"/api/v1/extension/reviews/{ext_id}",
|
||||
"query_string": b"offset=0&limit=5",
|
||||
"headers": [],
|
||||
}
|
||||
)
|
||||
mock_client = _MockHTTPClient(
|
||||
{
|
||||
tags_url: _MockHTTPResponse(
|
||||
json_data=[{"tag": "good", "avg_rating": 900, "review_count": 3}]
|
||||
),
|
||||
reviews_url: _MockHTTPResponse(
|
||||
json_data={
|
||||
"data": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "Alice",
|
||||
"tag": "good",
|
||||
"rating": 950,
|
||||
"comment": "solid",
|
||||
}
|
||||
],
|
||||
"total": 1,
|
||||
}
|
||||
),
|
||||
create_review_url: _MockHTTPResponse(
|
||||
json_data={
|
||||
"payment_hash": f"hash_{uuid4().hex[:8]}",
|
||||
"payment_request": "lnbc1review",
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
mocker.patch(
|
||||
"lnbits.core.views.extension_api.httpx.AsyncClient", return_value=mock_client
|
||||
)
|
||||
|
||||
tags = await get_extension_reviews_tags()
|
||||
assert tags[0].tag == "good"
|
||||
|
||||
reviews = await get_extension_reviews(ext_id, request)
|
||||
assert reviews.total == 1
|
||||
assert reviews.data[0].comment == "solid"
|
||||
|
||||
payment_request = await create_extension_review(
|
||||
CreateExtensionReview(tag=ext_id, name="Alice", rating=900, comment="Great")
|
||||
)
|
||||
assert payment_request.payment_hash.startswith("hash_")
|
||||
Reference in New Issue
Block a user