feat(cloud): harden multi-tenant runtime resources

This commit is contained in:
Junyan Qin
2026-07-29 11:32:26 +08:00
parent 32abbb636f
commit ae85ac2b16
211 changed files with 14963 additions and 1968 deletions
@@ -12,6 +12,7 @@ import zipfile
from types import SimpleNamespace
from unittest.mock import MagicMock
import httpx
import pytest
@@ -123,6 +124,17 @@ class TestExtractDepsMetadata:
# Should find requirements.txt in subdirectory
assert task_context.metadata['deps_total'] == 2
def test_archive_preview_rejects_extreme_compression_ratio(self):
from langbot.pkg.plugin.connector import inspect_plugin_archive_metadata
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
zf.writestr('manifest.yaml', 'kind: Plugin\nmetadata: {}\n')
zf.writestr('bomb.py', b'A' * (1024 * 1024))
with pytest.raises(ValueError, match='compression-ratio limit'):
inspect_plugin_archive_metadata(zip_buffer.getvalue())
class TestParsePluginId:
"""Tests for _parse_plugin_id static method."""
@@ -141,3 +153,13 @@ class TestParsePluginId:
with pytest.raises(ValueError):
PluginRuntimeConnector._parse_plugin_id('')
@pytest.mark.asyncio
async def test_marketplace_response_reader_is_bounded():
from langbot.pkg.plugin.connector import _read_httpx_response_limited
response = httpx.Response(200, content=b'oversized')
with pytest.raises(ValueError, match='exceeds'):
await _read_httpx_response_limited(response, max_bytes=4)
@@ -136,6 +136,23 @@ async def test_shared_reconnect_replays_two_workspaces_and_removes_missing_proje
assert set(connector._known_desired_states) == {setting_a.installation_uuid}
@pytest.mark.asyncio
async def test_empty_projected_workspaces_do_not_retain_installation_sets():
binding_a = execution_binding('workspace-a')
binding_b = execution_binding('workspace-b')
connector = shared_connector(
[[binding_a, binding_b]],
{'workspace-a': [], 'workspace-b': []},
)
connector.handler = runtime_handler()
await connector._prepare_connected_runtime()
assert connector._workspace_installations == {}
assert connector._known_desired_states == {}
connector.handler.reconcile_plugin_installations.assert_awaited_once_with(())
@pytest.mark.asyncio
async def test_fresh_shared_runtime_cache_replays_persisted_local_package():
package = b'local-lbpkg-bytes'
@@ -257,7 +257,7 @@ class TestSetBinaryStorage:
)
assert response.code != 0
assert '2048 > 1024 bytes' in response.message
assert '1024-byte limit' in response.message
app.persistence_mgr.execute_async.assert_not_awaited()
@pytest.mark.asyncio
@@ -312,21 +312,25 @@ class TestSetBinaryStorage:
)
assert response.code != 0
assert '10485761 > 10485760 bytes' in response.message
assert '10485760' in response.message
app.persistence_mgr.execute_async.assert_not_awaited()
@pytest.mark.asyncio
async def test_negative_limit_disables_size_check(self, app):
"""Negative max_value_bytes allows values larger than the normal default."""
async def test_negative_limit_falls_back_to_bounded_default(self, app, monkeypatch):
"""Negative max_value_bytes cannot disable the process memory boundary."""
import langbot.pkg.plugin.handler as handler_module
runtime_handler = make_handler(app)
app.instance_config.data['plugin']['binary_storage']['max_value_bytes'] = -1
monkeypatch.setattr(handler_module, '_DEFAULT_BINARY_STORAGE_VALUE_BYTES', 1024)
response = await runtime_handler.actions[RuntimeToLangBotAction.SET_BINARY_STORAGE.value](
self.payload(b'x' * 2048)
)
assert response.code == 0
assert app.persistence_mgr.execute_async.await_count == 2
assert response.code != 0
assert '1024-byte limit' in response.message
app.persistence_mgr.execute_async.assert_not_awaited()
@pytest.mark.asyncio
async def test_zero_limit_rejects_non_empty_values(self, app):