# Copyright 2026 Emcie Co Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from fastapi import status import httpx from lagom import Container from parlant.core.agents import AgentId, AgentStore from parlant.core.journeys import JourneyStore from parlant.core.relationships import ( RelationshipEntityKind, RelationshipKind, RelationshipEntity, RelationshipStore, ) from parlant.core.guidelines import Guideline, GuidelineContent, GuidelineStore from parlant.core.tags import Tag, TagId, TagStore from parlant.core.tools import LocalToolService, ToolOverlap async def create_guidelines_and_create_relationships_between_them( container: Container, agent_id: AgentId, guideline_contents: list[GuidelineContent], ) -> list[Guideline]: guidelines = [ await container[GuidelineStore].create_guideline( condition=gc.condition, action=gc.action, ) for gc in guideline_contents ] for guideline in guidelines: _ = await container[GuidelineStore].upsert_tag( guideline_id=guideline.id, tag_id=Tag.for_agent_id(agent_id).id, ) for source, target in zip(guidelines, guidelines[1:]): await container[RelationshipStore].create_relationship( source=RelationshipEntity( id=source.id, kind=RelationshipEntityKind.GUIDELINE, ), target=RelationshipEntity( id=target.id, kind=RelationshipEntityKind.GUIDELINE, ), kind=RelationshipKind.ENTAILMENT, ) return guidelines async def test_that_a_guideline_can_be_created( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={ "condition": "the customer asks about pricing", "action": "provide current pricing information", "enabled": True, "metadata": {"key1": "value1", "key2": "value2"}, }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["condition"] == "the customer asks about pricing" assert guideline["action"] == "provide current pricing information" assert guideline["enabled"] is True assert guideline["tags"] == [] assert guideline["metadata"] == {"key1": "value1", "key2": "value2"} async def test_that_a_guideline_can_be_created_with_a_title( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={ "condition": "the customer asks about pricing", "action": "provide current pricing information", "title": "Pricing inquiries", }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["title"] == "Pricing inquiries" async def test_that_a_guideline_title_can_be_updated( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", title="Old title", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={"title": "Weather inquiries"}, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert updated_guideline["title"] == "Weather inquiries" async def test_that_a_guideline_can_be_created_without_an_action( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={"condition": "the customer asks about pricing"}, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["condition"] == "the customer asks about pricing" assert guideline["action"] is None async def test_that_a_guideline_can_be_created_with_custom_id( async_client: httpx.AsyncClient, ) -> None: """Test that a guideline can be created with a custom ID.""" custom_id = "custom-guideline-id-456" response = await async_client.post( "/guidelines", json={ "id": custom_id, "condition": "the customer mentions a custom requirement", "action": "provide personalized assistance", }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() # Verify that the custom ID was used assert guideline["id"] == custom_id assert guideline["condition"] == "the customer mentions a custom requirement" assert guideline["action"] == "provide personalized assistance" assert guideline["enabled"] is True assert guideline["tags"] == [] assert guideline["metadata"] == {} async def test_that_creating_guideline_with_duplicate_id_fails( async_client: httpx.AsyncClient, ) -> None: """Test that creating a guideline with a duplicate ID fails appropriately.""" custom_id = "duplicate-guideline-id" # Create first guideline response1 = await async_client.post( "/guidelines", json={ "id": custom_id, "condition": "first condition", "action": "first action", }, ) assert response1.status_code == status.HTTP_201_CREATED # Try to create second guideline with same ID response2 = await async_client.post( "/guidelines", json={ "id": custom_id, "condition": "second condition", "action": "second action", }, ) # Should fail due to duplicate ID assert response2.status_code == status.HTTP_422_UNPROCESSABLE_CONTENT assert "already exists" in response2.text async def test_that_a_guideline_can_be_created_with_tags( async_client: httpx.AsyncClient, container: Container, ) -> None: tag_store = container[TagStore] agent_store = container[AgentStore] journey_store = container[JourneyStore] agent = await agent_store.create_agent("Test Agent") agent_tag = Tag.for_agent_id(agent.id).id journey = await journey_store.create_journey( title="Customer Support Journey", description="A journey for customer support interactions.", triggers=[], ) journey_tag = Tag.for_journey_id(journey.id).id tag_1 = await tag_store.create_tag(name="pricing") tag_2 = await tag_store.create_tag(name="sales") response = await async_client.post( "/guidelines", json={ "condition": "the customer asks about pricing", "action": "provide current pricing information", "tags": [ tag_1.id, tag_1.id, tag_2.id, agent_tag, journey_tag, ], }, ) assert response.status_code == status.HTTP_201_CREATED guideline_dto = ( (await async_client.get(f"/guidelines/{response.json()['id']}")).raise_for_status().json() ) assert guideline_dto["guideline"]["condition"] == "the customer asks about pricing" assert guideline_dto["guideline"]["action"] == "provide current pricing information" assert len(guideline_dto["guideline"]["tags"]) == 4 assert set(guideline_dto["guideline"]["tags"]) == {tag_1.id, tag_2.id, agent_tag, journey_tag} async def test_that_guidelines_can_be_listed( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] first_guideline = [ await guideline_store.create_guideline( condition=f"condition {i}", action=f"action {i}", ) for i in range(2) ] second_guideline = await guideline_store.create_guideline( condition="condition 2", action="action 2", ) response_guidelines = (await async_client.get("/guidelines")).raise_for_status().json() assert len(response_guidelines) >= 2 assert any(first_guideline[0].id == g["id"] for g in response_guidelines) assert any(first_guideline[1].id == g["id"] for g in response_guidelines) assert any(second_guideline.id == g["id"] for g in response_guidelines) async def test_that_guidelines_can_be_listed_by_tag( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] first_guideline = await guideline_store.create_guideline( condition="condition 1", action="action 1", ) second_guideline = await guideline_store.create_guideline( condition="condition 2", action="action 2", ) await guideline_store.upsert_tag( guideline_id=first_guideline.id, tag_id=TagId("tag_1"), ) await guideline_store.upsert_tag( guideline_id=second_guideline.id, tag_id=TagId("tag_2"), ) response_guidelines = ( (await async_client.get("/guidelines?tag_id=tag_1")).raise_for_status().json() ) assert len(response_guidelines) == 1 assert response_guidelines[0]["id"] == first_guideline.id response_guidelines = ( (await async_client.get("/guidelines?tag_id=tag_2")).raise_for_status().json() ) async def test_that_a_guideline_can_be_read( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", metadata={"key1": "value1", "key2": "value2"}, ) item = (await async_client.get(f"/guidelines/{guideline.id}")).raise_for_status().json() assert item["guideline"]["id"] == guideline.id assert item["guideline"]["condition"] == "the customer asks about the weather" assert item["guideline"]["action"] == "provide the current weather update" assert item["guideline"]["metadata"] == {"key1": "value1", "key2": "value2"} assert len(item["relationships"]) == 0 assert len(item["tool_associations"]) == 0 async def test_that_a_guideline_condition_can_be_updated( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "condition": "the customer inquires about weather", }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert updated_guideline["condition"] == "the customer inquires about weather" assert updated_guideline["action"] == guideline.content.action async def test_that_a_guideline_action_can_be_updated( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "action": "give current weather information", }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert updated_guideline["condition"] == guideline.content.condition assert updated_guideline["action"] == "give current weather information" async def test_that_a_guideline_can_be_disabled( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "enabled": False, }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert updated_guideline["enabled"] is False async def test_that_a_tag_can_be_added_to_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] tag_store = container[TagStore] tag = await tag_store.create_tag("test_tag") guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "tags": { "add": [tag.id], }, }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert tag.id in updated_guideline["tags"] async def test_that_a_tag_can_be_removed_from_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", ) # First add a tag await guideline_store.upsert_tag( guideline_id=guideline.id, tag_id=TagId("test_tag"), ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "tags": { "remove": ["test_tag"], }, }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert "test_tag" not in updated_guideline["tags"] async def test_that_an_agent_tag_can_be_added_to_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] agent_store = container[AgentStore] agent = await agent_store.create_agent("test_agent") agent_tag = Tag.for_agent_id(agent.id).id guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "tags": { "add": [agent_tag], }, }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert agent_tag in updated_guideline["tags"] async def test_that_a_journey_tag_can_be_added_to_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] journey_store = container[JourneyStore] journey = await journey_store.create_journey( title="test_journey", description="test_description", triggers=[], ) journey_tag = Tag.for_journey_id(journey.id).id guideline = await guideline_store.create_guideline( condition="the customer asks about the weather", action="provide the current weather update", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "tags": { "add": [journey_tag], }, }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert journey_tag in updated_guideline["tags"] async def test_that_a_guideline_can_be_deleted( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer wants to unsubscribe", action="ask for confirmation", ) (await async_client.delete(f"/guidelines/{guideline.id}")).raise_for_status() response = await async_client.get(f"/guidelines/{guideline.id}") assert response.status_code == status.HTTP_404_NOT_FOUND async def test_that_a_tool_association_can_be_added_to_a_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] local_tool_service = container[LocalToolService] await local_tool_service.create_tool( name="fetch_event_data", module_path="some.module", description="", parameters={}, required=[], overlap=ToolOverlap.NONE, ) guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", ) service_name = "local" tool_name = "fetch_event_data" request_data = { "tool_associations": { "add": [ { "service_name": service_name, "tool_name": tool_name, } ] } } response = await async_client.patch( f"/guidelines/{guideline.id}", json=request_data, ) assert response.status_code == status.HTTP_200_OK tool_associations = response.json()["tool_associations"] assert any( a["guideline_id"] == guideline.id and a["tool_id"]["service_name"] == service_name and a["tool_id"]["tool_name"] == tool_name for a in tool_associations ) async def test_that_a_tag_can_be_added_to_a_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] tag_store = container[TagStore] tag = await tag_store.create_tag("test_tag") guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={"tags": {"add": [tag.id]}}, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert tag.id in updated_guideline["tags"] async def test_that_a_tag_can_be_removed_from_a_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] tag_store = container[TagStore] tag = await tag_store.create_tag("test_tag") guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", ) await guideline_store.upsert_tag( guideline_id=guideline.id, tag_id=tag.id, ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={"tags": {"remove": [tag.id]}}, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert updated_guideline["tags"] == [] async def test_that_adding_nonexistent_agent_tag_to_guideline_returns_404( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={"tags": {"add": ["agent-id:nonexistent_agent"]}}, ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_that_adding_nonexistent_tag_to_guideline_returns_404( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={"tags": {"add": ["nonexistent_tag"]}}, ) assert response.status_code == status.HTTP_404_NOT_FOUND async def test_that_metadata_can_be_updated_for_a_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", metadata={"key3": "value2"}, ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "metadata": { "set": { "key1": "value1", "key2": "value2", }, "unset": ["key3"], } }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert updated_guideline["metadata"] == {"key1": "value1", "key2": "value2"} async def test_that_condition_association_is_deleted_when_a_guideline_is_deleted( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] journey_store = container[JourneyStore] guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", ) journey = await journey_store.create_journey( title="test_journey", description="test_description", triggers=[guideline.id], ) response = await async_client.delete(f"/guidelines/{guideline.id}") assert response.status_code == status.HTTP_204_NO_CONTENT updated_journey = await journey_store.read_journey(journey.id) assert updated_journey.triggers == [] async def test_that_guideline_relationships_can_be_read( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] relationship_store = container[RelationshipStore] guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", ) connected_guideline = await guideline_store.create_guideline( condition="reply with 'Hello'", action="finish with a smile", ) await relationship_store.create_relationship( source=RelationshipEntity( id=guideline.id, kind=RelationshipEntityKind.GUIDELINE, ), target=RelationshipEntity( id=connected_guideline.id, kind=RelationshipEntityKind.GUIDELINE, ), kind=RelationshipKind.ENTAILMENT, ) response = await async_client.get(f"/guidelines/{guideline.id}") assert response.status_code == status.HTTP_200_OK relationships = response.json()["relationships"] assert len(relationships) == 1 assert relationships[0]["source_guideline"]["id"] == guideline.id assert relationships[0]["target_guideline"]["id"] == connected_guideline.id assert relationships[0]["kind"] == "entailment" async def test_that_guideline_with_relationships_can_be_deleted( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] relationship_store = container[RelationshipStore] guideline = await guideline_store.create_guideline( condition="the customer wants to get meeting details", action="get meeting event information", ) connected_guideline = await guideline_store.create_guideline( condition="reply with 'Hello'", action="finish with a smile", ) await relationship_store.create_relationship( source=RelationshipEntity( id=guideline.id, kind=RelationshipEntityKind.GUIDELINE, ), target=RelationshipEntity( id=connected_guideline.id, kind=RelationshipEntityKind.GUIDELINE, ), kind=RelationshipKind.ENTAILMENT, ) (await async_client.delete(f"/guidelines/{guideline.id}")).raise_for_status() response = await async_client.get(f"/guidelines/{guideline.id}") assert response.status_code == status.HTTP_404_NOT_FOUND async def test_that_a_guideline_can_be_created_with_description( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={ "condition": "the customer asks about premium features", "action": "explain the premium features available", "description": "Premium features are only available to customers with active subscriptions", "enabled": True, }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["condition"] == "the customer asks about premium features" assert guideline["action"] == "explain the premium features available" assert ( guideline["description"] == "Premium features are only available to customers with active subscriptions" ) assert guideline["enabled"] is True guideline_id = guideline["id"] item = (await async_client.get(f"/guidelines/{guideline_id}")).raise_for_status().json() assert item["guideline"]["id"] == guideline_id assert ( item["guideline"]["description"] == "Premium features are only available to customers with active subscriptions" ) async def test_that_a_guideline_description_can_be_updated( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about refunds", action="explain the refund policy", metadata={}, ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "description": "Refunds are only available within 30 days of purchase", }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert ( updated_guideline["description"] == "Refunds are only available within 30 days of purchase" ) async def test_that_a_guideline_description_can_be_updated_to_none( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer asks about shipping", action="explain shipping options", metadata={}, ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "description": None, }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert updated_guideline["id"] == guideline.id assert updated_guideline["description"] is None async def test_that_guideline_can_be_created_with_criticality_via_api( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={ "condition": "Customer reports a critical security issue", "action": "Escalate to security team immediately", "criticality": "high", }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["condition"] == "Customer reports a critical security issue" assert guideline["action"] == "Escalate to security team immediately" assert guideline["criticality"] == "high" async def test_that_guideline_defaults_to_medium_criticality_via_api( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={ "condition": "Customer asks about product features", "action": "Provide detailed feature information", }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["condition"] == "Customer asks about product features" assert guideline["action"] == "Provide detailed feature information" assert guideline["criticality"] == "medium" async def test_that_guideline_criticality_can_be_updated_via_api( async_client: httpx.AsyncClient, container: Container, ) -> None: # Create a guideline with LOW criticality create_response = await async_client.post( "/guidelines", json={ "condition": "Customer has a minor question", "action": "Provide basic information", "criticality": "low", }, ) assert create_response.status_code == status.HTTP_201_CREATED guideline = create_response.json() guideline_id = guideline["id"] # Update criticality to HIGH update_response = await async_client.patch( f"/guidelines/{guideline_id}", json={ "criticality": "high", }, ) assert update_response.status_code == status.HTTP_200_OK updated_guideline = update_response.json()["guideline"] assert updated_guideline["id"] == guideline_id assert updated_guideline["criticality"] == "high" async def test_that_guideline_composition_mode_can_be_set_and_updated( async_client: httpx.AsyncClient, ) -> None: # Create guideline with CANNED_COMPOSITED mode response = await async_client.post( "/guidelines", json={ "condition": "User asks about pricing", "action": "Provide pricing information", "composition_mode": "composited_canned", }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() guideline_id = guideline["id"] # Check that the composition mode is set correctly after creation assert guideline["composition_mode"] == "composited_canned" # Retrieve guideline and verify composition mode response = await async_client.get(f"/guidelines/{guideline_id}") assert response.status_code == status.HTTP_200_OK guideline = response.json()["guideline"] assert guideline["composition_mode"] == "composited_canned" # Update guideline to CANNED_STRICT mode response = await async_client.patch( f"/guidelines/{guideline_id}", json={ "composition_mode": "strict_canned", }, ) assert response.status_code == status.HTTP_200_OK guideline = response.json()["guideline"] # Check that the composition mode is updated correctly assert guideline["composition_mode"] == "strict_canned" # Retrieve guideline again and verify composition mode response = await async_client.get(f"/guidelines/{guideline_id}") assert response.status_code == status.HTTP_200_OK guideline = response.json()["guideline"] assert guideline["composition_mode"] == "strict_canned" ############################################################################### ## Labels Tests ############################################################################### async def test_that_a_guideline_can_be_created_with_labels( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={ "condition": "the customer asks about pricing", "action": "provide current pricing information", "labels": ["premium", "sales"], }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["condition"] == "the customer asks about pricing" assert guideline["action"] == "provide current pricing information" assert set(guideline["labels"]) == {"premium", "sales"} async def test_that_a_guideline_is_created_with_empty_labels_by_default( async_client: httpx.AsyncClient, ) -> None: response = await async_client.post( "/guidelines", json={ "condition": "the customer asks about something", "action": "help them out", }, ) assert response.status_code == status.HTTP_201_CREATED guideline = response.json() assert guideline["labels"] == [] async def test_that_labels_can_be_added_to_a_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer wants help", action="help them", labels={"initial"}, ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={"labels": {"upsert": ["new_label", "another_label"]}}, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert set(updated_guideline["labels"]) == {"initial", "new_label", "another_label"} async def test_that_labels_can_be_removed_from_a_guideline( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="the customer wants help", action="help them", labels={"label1", "label2", "label3"}, ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={"labels": {"remove": ["label2"]}}, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert set(updated_guideline["labels"]) == {"label1", "label3"} async def test_that_labels_can_be_upserted_and_removed_in_same_operation( async_client: httpx.AsyncClient, container: Container, ) -> None: guideline_store = container[GuidelineStore] guideline = await guideline_store.create_guideline( condition="test condition", action="test action", labels={"keep", "remove_me"}, ) response = await async_client.patch( f"/guidelines/{guideline.id}", json={ "labels": { "upsert": ["new_label"], "remove": ["remove_me"], } }, ) assert response.status_code == status.HTTP_200_OK updated_guideline = response.json()["guideline"] assert set(updated_guideline["labels"]) == {"keep", "new_label"}