Initial project import
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -50,6 +51,80 @@ def configured_layers(monkeypatch, tmp_path: Path) -> None:
|
||||
monkeypatch.setenv("ONEC_REPOSITORY_STATE_FILE", str(tmp_path / "locks.json"))
|
||||
|
||||
|
||||
def test_repositoryless_base_inherits_test_write_profile_to_discovered_extension(monkeypatch, tmp_path: Path) -> None:
|
||||
config = {
|
||||
"upo_test": {
|
||||
"server": "sql", "database": "upo_test", "user": "reader",
|
||||
"development_layers": {
|
||||
"base": {
|
||||
"repository": {"mode": "none", "connection_state": "not_configured"},
|
||||
"support": {"mode": "unknown"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
extension_guid = "11111111-2222-3333-4444-555555555555"
|
||||
monkeypatch.setenv("ONEC_SQL_BASES_JSON", json.dumps(config))
|
||||
monkeypatch.setenv("ONEC_REPOSITORY_STATE_FILE", str(tmp_path / "locks.json"))
|
||||
|
||||
payload = {"base_id": "upo_test", "extension_guid": extension_guid}
|
||||
repository, repository_error = repository_control.repository_config("upo_test", f"extension:{extension_guid}")
|
||||
|
||||
assert repository_error is None
|
||||
assert repository["mode"] == "none"
|
||||
assert repository["inherited_from_layer"] == "base"
|
||||
assert repository_control.write_gate(payload)["allowed"] is True
|
||||
support = repository_control.support_gate(payload)
|
||||
assert support["allowed"] is True
|
||||
assert support["status"] == "not_on_support_inherited_no_repository"
|
||||
|
||||
|
||||
def test_repository_policy_is_not_inherited_from_real_base_to_extension(monkeypatch, tmp_path: Path) -> None:
|
||||
configured_layers(monkeypatch, tmp_path)
|
||||
|
||||
repository, error = repository_control.repository_config(
|
||||
"base", "extension:99999999-2222-3333-4444-555555555555"
|
||||
)
|
||||
|
||||
assert repository is None
|
||||
assert error["status"] == "layer_not_configured"
|
||||
|
||||
|
||||
def test_repository_state_migrates_once_to_local_sqlite(monkeypatch, tmp_path: Path) -> None:
|
||||
legacy_path = tmp_path / "locks.json"
|
||||
state_path = tmp_path / "adapter-state.sqlite"
|
||||
legacy = {
|
||||
"requests": {
|
||||
"rreq-legacy": {
|
||||
"base_id": "base",
|
||||
"layer_id": "base",
|
||||
"status": "cancelled",
|
||||
"created_at": 1.0,
|
||||
}
|
||||
},
|
||||
"sessions": {},
|
||||
"audit": [{"event": "legacy_event", "time": 1.0, "base_id": "base"}],
|
||||
}
|
||||
legacy_path.write_text(json.dumps(legacy), encoding="utf-8")
|
||||
original = legacy_path.read_bytes()
|
||||
monkeypatch.setenv("ONEC_REPOSITORY_STATE_FILE", str(legacy_path))
|
||||
monkeypatch.setenv("ONEC_ADAPTER_STATE_DB", str(state_path))
|
||||
|
||||
state = repository_control._read_state()
|
||||
|
||||
assert state["requests"]["rreq-legacy"]["status"] == "cancelled"
|
||||
assert legacy_path.read_bytes() == original
|
||||
with sqlite3.connect(state_path) as conn:
|
||||
assert conn.execute(
|
||||
"SELECT value FROM adapter_state_meta WHERE key='legacy_repository_state_imported'"
|
||||
).fetchone()[0] == "1"
|
||||
assert int(conn.execute(
|
||||
"SELECT value FROM adapter_state_meta WHERE key='adapter_state_schema_version'"
|
||||
).fetchone()[0]) >= 2
|
||||
assert conn.execute("SELECT COUNT(*) FROM repository_lock_requests").fetchone()[0] == 1
|
||||
assert conn.execute("SELECT COUNT(*) FROM repository_lock_events").fetchone()[0] == 1
|
||||
|
||||
|
||||
def test_repository_backend_and_endpoint_come_only_from_base_settings(monkeypatch, tmp_path: Path) -> None:
|
||||
configured_base(monkeypatch, tmp_path)
|
||||
config, error = repository_control.repository_config("base")
|
||||
@@ -70,6 +145,89 @@ def test_http_runner_profile_does_not_require_local_designer_or_credentials(monk
|
||||
assert "designer_path" not in configured
|
||||
|
||||
|
||||
def test_activation_debug_probe_calls_dedicated_http_endpoint_without_execution(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
captured = {}
|
||||
|
||||
class Response:
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc, tb):
|
||||
return False
|
||||
|
||||
def read(self):
|
||||
return json.dumps(
|
||||
{
|
||||
"status": "ready",
|
||||
"runner": {
|
||||
"kind": "local",
|
||||
"reachable": True,
|
||||
"designer_available": True,
|
||||
"infobase_selector_configured": True,
|
||||
},
|
||||
"operation": {
|
||||
"kind": "/UpdateDBCfg",
|
||||
"execution_supported": False,
|
||||
},
|
||||
"execution": {
|
||||
"mode": "debug",
|
||||
"performed": False,
|
||||
"designer_started": False,
|
||||
},
|
||||
"debug_acceptance": {
|
||||
"accepted": True,
|
||||
"request_id": "actreq-" + "1" * 32,
|
||||
"fingerprint": "a" * 64,
|
||||
"receipt": "b" * 64,
|
||||
},
|
||||
}
|
||||
).encode("utf-8")
|
||||
|
||||
def fake_urlopen(request, timeout):
|
||||
captured["url"] = request.full_url
|
||||
captured["body"] = json.loads(request.data.decode("utf-8"))
|
||||
captured["authorization"] = request.headers.get("Authorization")
|
||||
captured["timeout"] = timeout
|
||||
return Response()
|
||||
|
||||
monkeypatch.setenv("ACTIVATION_RUNNER_TOKEN", "runner-secret")
|
||||
monkeypatch.setattr(repository_control.urllib.request, "urlopen", fake_urlopen)
|
||||
|
||||
result = repository_control.activation_debug_probe(
|
||||
"upo_test",
|
||||
{
|
||||
"runner": {
|
||||
"kind": "http",
|
||||
"url": "http://runner:8121",
|
||||
"token_env": "ACTIVATION_RUNNER_TOKEN",
|
||||
}
|
||||
},
|
||||
layer="base_saved_state",
|
||||
timeout_seconds=7,
|
||||
request_id="actreq-" + "1" * 32,
|
||||
fingerprint="a" * 64,
|
||||
)
|
||||
|
||||
assert result["status"] == "ready"
|
||||
assert captured == {
|
||||
"url": "http://runner:8121/configuration/activation/debug",
|
||||
"body": {
|
||||
"base_id": "upo_test",
|
||||
"layer": "base_saved_state",
|
||||
"mode": "debug",
|
||||
"request_id": "actreq-" + "1" * 32,
|
||||
"fingerprint": "a" * 64,
|
||||
},
|
||||
"authorization": "Bearer runner-secret",
|
||||
"timeout": 7,
|
||||
}
|
||||
assert result["execution"]["performed"] is False
|
||||
assert result["execution"]["designer_started"] is False
|
||||
assert result["debug_acceptance"]["receipt"] == "b" * 64
|
||||
|
||||
|
||||
def test_lock_plan_maps_child_metadata_to_development_owner() -> None:
|
||||
result = repository_control.lock_plan({"object": "РегистрСведений.Настройки.Реквизит.Код"})
|
||||
assert result["status"] == "ready"
|
||||
@@ -121,6 +279,9 @@ def test_manual_lock_request_stays_pending_until_user_confirms_exact_saved_scope
|
||||
}
|
||||
pending = repository_control.lock_request_status({"request_id": requested["request_id"]})
|
||||
assert pending["status"] == "pending_user_lock"
|
||||
assert pending["base_id"] == "base"
|
||||
assert pending["objects"] == ["РегистрСведений.Настройки"]
|
||||
assert pending["native_lock_state"] == "unknown"
|
||||
assert pending["next_call"] == requested["next_call"]
|
||||
|
||||
incomplete = repository_control.confirm_manual_lock({"base_id": "base", "request_id": requested["request_id"]})
|
||||
@@ -494,6 +655,9 @@ def test_confirm_write_context_can_be_forwarded_under_repository_lock() -> None:
|
||||
assert context["lock_session_id"] == "rlock-test"
|
||||
assert context["repository_object"] == "Document.тл_Планировщик.Form.ФормаДокументаНовая"
|
||||
assert context["layer_id"] == "base"
|
||||
forwarded = adapter_1c_server.repository_write_context(context)
|
||||
assert forwarded["layer_id"] == "base"
|
||||
assert forwarded["lock_session_id"] == "rlock-test"
|
||||
|
||||
_context, conflict = adapter_1c_server.normalize_repository_write_context({
|
||||
"lock_session_id": "rlock-one",
|
||||
|
||||
Reference in New Issue
Block a user