Retire a shared node in use with ?detach=true

DELETE /flows/library/{name} still answers 409 while nodes use the source.
With detach=true each of them keeps a copy of the code, in the published
flow and the draft, the source is deleted and the running users rebuild.
This commit is contained in:
2026-10-15 09:48:47 +02:00
parent 96f4fa5a38
commit 0004e4b9bd
7 changed files with 114 additions and 2 deletions
+31
View File
@@ -550,3 +550,34 @@ async def test_the_live_socket_ends_when_its_client_goes(
await asyncio.sleep(0.05)
parked = [t for t in asyncio.all_tasks() if "Queue.get" in repr(t.get_coro())]
assert parked == []
def test_a_shared_node_in_use_is_retired_only_with_detach(
client: TestClient, superuser_token_headers: dict[str, str]
) -> None:
client.put(
f"{PREFIX}/shared", headers=superuser_token_headers, json=a_flow("shared")
)
client.put(
f"{PREFIX}/shared/nodes/sensor/source",
headers=superuser_token_headers,
json={"code": WORKING_NODE},
)
client.post(
f"{PREFIX}/shared/nodes/sensor/share",
headers=superuser_token_headers,
json={"lib_name": "reading"},
)
url = f"{PREFIX}/library/reading"
assert client.delete(url, headers=superuser_token_headers).status_code == 409
response = client.delete(f"{url}?detach=true", headers=superuser_token_headers)
assert response.status_code == 200
assert client.get(f"{PREFIX}/library", headers=superuser_token_headers).json() == []
source = client.get(
f"{PREFIX}/shared/nodes/sensor/source", headers=superuser_token_headers
)
assert source.json()["code"] == WORKING_NODE
client.delete(f"{PREFIX}/shared", headers=superuser_token_headers)
+24
View File
@@ -120,3 +120,27 @@ def test_unsharing_takes_a_private_copy_back(store: FlowStore):
def test_a_missing_shared_source_is_reported(store: FlowStore):
with pytest.raises(LibNotFound):
store.read_lib_source("nothing_here")
def test_retiring_a_shared_source_leaves_each_user_a_copy(store: FlowStore):
store.write_flow(a_flow())
store.write_node_source("heating", "sensor", SOURCE)
store.share_node("heating", "sensor", "read_temp")
store.publish_flow("heating", store.read_flow("heating", draft=True).version)
version = store.read_flow("heating").version
# Shared in a draft that was never published.
cooling = a_flow("cooling")
cooling.nodes[0].source_ref = "read_temp"
store.write_draft(cooling, 0)
store.detach_lib("read_temp")
assert store.list_lib() == []
heating = store.read_flow("heating")
assert heating.nodes[0].source_ref is None
assert heating.version == version + 1
assert store.read_node_source("heating", "sensor") == SOURCE
# What runs is the same code, so there is nothing to publish.
assert not store.has_draft("heating")
assert store.read_flow("cooling", draft=True).nodes[0].source_ref is None
assert store.read_node_source("cooling", "sensor", draft=True) == SOURCE