mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-09 12:40:59 +00:00
Merge remote-tracking branch 'origin/master' into dev/4.11.x
# Conflicts: # pyproject.toml # src/langbot/pkg/pipeline/controller.py # uv.lock # web/src/app/home/bots/components/bot-form/BotForm.tsx # web/src/app/home/components/home-sidebar/HomeSidebar.tsx # web/src/app/home/components/home-sidebar/SidebarDataContext.tsx # web/src/app/home/plugin-pages/page.tsx # web/src/app/infra/entities/api/index.ts
This commit is contained in:
@@ -9,6 +9,8 @@ Run: uv run pytest tests/integration/api/test_smoke.py -q
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, AsyncMock, Mock
|
||||
|
||||
@@ -304,12 +306,34 @@ class TestUserInitEndpoint:
|
||||
data = await response.get_json()
|
||||
assert data['data'] == {
|
||||
'initialized': True,
|
||||
'authenticated_invitation_acceptance_enabled': False,
|
||||
'password_login_enabled': True,
|
||||
'space_login_enabled': False,
|
||||
}
|
||||
fake_api_app.user_service.get_login_capabilities.assert_awaited_once_with()
|
||||
fake_api_app.user_service.get_first_user.assert_not_awaited()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_account_info_enables_authenticated_invitation_acceptance_in_cloud(
|
||||
self, quart_test_client, fake_api_app
|
||||
):
|
||||
fake_api_app.deployment = SimpleNamespace(mode='cloud')
|
||||
fake_api_app.user_service.is_initialized.return_value = True
|
||||
fake_api_app.user_service.get_login_capabilities = AsyncMock(
|
||||
return_value={'password_login_enabled': True, 'space_login_enabled': True}
|
||||
)
|
||||
|
||||
response = await quart_test_client.get('/api/v1/user/account-info')
|
||||
|
||||
assert response.status_code == 200
|
||||
data = await response.get_json()
|
||||
assert data['data'] == {
|
||||
'initialized': True,
|
||||
'authenticated_invitation_acceptance_enabled': True,
|
||||
'password_login_enabled': False,
|
||||
'space_login_enabled': True,
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_recovery_key_resets_any_existing_account(self, quart_test_client, fake_api_app, monkeypatch):
|
||||
fake_api_app.user_service.is_initialized.return_value = True
|
||||
|
||||
@@ -333,7 +333,6 @@ async def test_support_admin_request_context_has_actor_owner_and_no_membership(s
|
||||
assert Permission.RESOURCE_MANAGE.value in permissions
|
||||
assert not permissions.intersection(
|
||||
{
|
||||
Permission.OWNER_TRANSFER.value,
|
||||
Permission.MEMBER_VIEW.value,
|
||||
Permission.MEMBER_INVITE.value,
|
||||
Permission.MEMBER_UPDATE_ROLE.value,
|
||||
|
||||
@@ -272,9 +272,10 @@ async def test_space_credits_are_resolved_from_workspace_owner(space_oauth_api):
|
||||
'/api/v1/user/space-credits',
|
||||
headers={'Authorization': 'Bearer account-token', 'X-Workspace-Id': WORKSPACE_UUID},
|
||||
)
|
||||
payload = await response.get_json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert (await response.get_json())['data'] == {
|
||||
assert payload['data'] == {
|
||||
'credits': 25000,
|
||||
'owner_space_bound': True,
|
||||
'is_workspace_owner': True,
|
||||
@@ -282,6 +283,31 @@ async def test_space_credits_are_resolved_from_workspace_owner(space_oauth_api):
|
||||
application.space_service.get_credits.assert_awaited_once_with('owner@example.com')
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cloud_workspace_owner_is_always_space_bound_after_login(space_oauth_api):
|
||||
application, client = space_oauth_api
|
||||
application.deployment.mode = 'cloud'
|
||||
application.user_service.get_workspace_owner = AsyncMock(return_value=None)
|
||||
application.space_service.get_credits = AsyncMock()
|
||||
application.cloud_model_catalog_service = SimpleNamespace(
|
||||
get_workspace_credits=lambda workspace_uuid: 25000 if workspace_uuid == WORKSPACE_UUID else None
|
||||
)
|
||||
|
||||
response = await client.get(
|
||||
'/api/v1/user/space-credits',
|
||||
headers={'Authorization': 'Bearer account-token', 'X-Workspace-Id': WORKSPACE_UUID},
|
||||
)
|
||||
payload = await response.get_json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert payload['data'] == {
|
||||
'credits': 25000,
|
||||
'owner_space_bound': True,
|
||||
'is_workspace_owner': True,
|
||||
}
|
||||
application.space_service.get_credits.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bind_callback_uses_opaque_state_and_never_treats_it_as_jwt(space_oauth_api):
|
||||
application, client = space_oauth_api
|
||||
|
||||
@@ -188,6 +188,7 @@ async def test_owner_invites_second_account_and_secret_is_not_persisted(workspac
|
||||
workspace_uuid = current['workspace']['uuid']
|
||||
assert current['membership']['role'] == 'owner'
|
||||
assert 'member.invite' in current['permissions']
|
||||
assert 'owner.transfer' not in current['permissions']
|
||||
|
||||
invite_response = await client.post(
|
||||
f'/api/v1/workspaces/{workspace_uuid}/invitations',
|
||||
@@ -263,6 +264,14 @@ async def test_owner_invites_second_account_and_secret_is_not_persisted(workspac
|
||||
assert member_current['membership']['role'] == 'viewer'
|
||||
assert 'member.invite' not in member_current['permissions']
|
||||
|
||||
transfer_response = await client.patch(
|
||||
f'/api/v1/workspaces/{workspace_uuid}/members/{member_current["membership"]["account_uuid"]}',
|
||||
headers=_auth(owner_token, workspace_uuid),
|
||||
json={'role': 'owner'},
|
||||
)
|
||||
assert transfer_response.status_code == 403
|
||||
assert (await transfer_response.get_json())['code'] == 'permission_denied'
|
||||
|
||||
forbidden_invite = await client.post(
|
||||
f'/api/v1/workspaces/{workspace_uuid}/invitations',
|
||||
headers=_auth(member_token, workspace_uuid),
|
||||
|
||||
Reference in New Issue
Block a user