"""
Phase 23.6 — Experience Extraction Engine Tests

Tests:
  - Experience + WorkflowTemplate data models
  - ExperienceExtractor (rule-based + LLM-fallback)
  - WorkflowGenerator (rule-based + LLM)
  - ExperienceRegistry (CRUD + recall + ranking)
  - SQLiteStore integration (experiences + workflow_templates tables)
  - SDK methods (extract_experiences, recall_experiences, etc.)
  - REST API endpoints
"""

import json
import sys
import time
import os
import pytest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from openmemo.experience.models import Experience, WorkflowTemplate
from openmemo.experience.extractor import ExperienceExtractor
from openmemo.experience.workflow_gen import WorkflowGenerator
from openmemo.experience.registry import ExperienceRegistry
from openmemo.storage.sqlite_store import SQLiteStore
from openmemo.core.memory_graph import MemoryGraph, MemoryNode
from openmemo.api.sdk import Memory


# ─── Fixtures ─────────────────────────────────────────────────────

@pytest.fixture
def store(tmp_path):
    db_path = str(tmp_path / "test_phase236.db")
    s = SQLiteStore(db_path=db_path)
    yield s
    s.conn.close()


@pytest.fixture
def memory_obj(store):
    m = Memory.__new__(Memory)
    m.store = store
    m._exp_registry = None
    m._llm_fn = None
    return m


@pytest.fixture
def registry(store):
    return ExperienceRegistry(store)


@pytest.fixture
def graph(store):
    return MemoryGraph(store)


def make_memory_node(store, node_type="summary", scene="deployment",
                     agent_id="agent-test", task_id="task-001",
                     content=None, importance=0.8):
    """Helper: write a MemoryNode to the store and return it."""
    if content is None:
        content = f"[Tool Execution] tool=docker | action=deploy | result=success"
    node = MemoryNode(
        type=node_type,
        content=content,
        scene=scene,
        agent_id=agent_id,
        task_id=task_id,
        timestamp=time.time(),
        importance=importance,
    )
    store.put_memory_node(node.to_dict())
    return node


# ─── 1. Experience model ──────────────────────────────────────────

class TestExperienceModel:
    def test_defaults(self):
        exp = Experience()
        assert exp.id.startswith("exp_")
        assert exp.type == "workflow"
        assert 0.0 <= exp.confidence <= 1.0

    def test_type_validation(self):
        exp = Experience(type="invalid_type")
        assert exp.type == "workflow"

    def test_confidence_clamped(self):
        exp = Experience(confidence=5.0)
        assert exp.confidence == 1.0
        exp2 = Experience(confidence=-1.0)
        assert exp2.confidence == 0.0

    def test_to_dict_from_dict_roundtrip(self):
        exp = Experience(
            title="Deploy Backend",
            type="workflow",
            steps=["build image", "push to registry", "run container"],
            outcome="success",
            key_facts=["docker deploy succeeded"],
            conditions={"scene": "deployment", "trigger": "deploy"},
            confidence=0.85,
            scene="deployment",
            agent_id="agent-1",
        )
        d = exp.to_dict()
        exp2 = Experience.from_dict(d)
        assert exp2.id == exp.id
        assert exp2.title == exp.title
        assert exp2.steps == exp.steps
        assert exp2.confidence == exp.confidence
        assert exp2.conditions == exp.conditions

    def test_to_prompt_text(self):
        exp = Experience(
            title="Deploy Workflow",
            steps=["step 1", "step 2"],
            outcome="success",
            key_facts=["docker works"],
        )
        text = exp.to_prompt_text()
        assert "Deploy Workflow" in text
        assert "step 1" in text
        assert "success" in text
        assert "docker works" in text

    def test_from_dict_json_strings(self):
        d = {
            "id": "exp_test123",
            "title": "Test",
            "type": "lesson",
            "source_node_ids": '["n1", "n2"]',
            "steps": '["do a", "do b"]',
            "outcome": "failure",
            "key_facts": '["fact1"]',
            "conditions": '{"scene": "debug"}',
            "confidence": "0.6",
            "scene": "debug",
            "agent_id": "",
            "created_at": str(time.time()),
            "usage_count": "3",
            "metadata": "{}",
        }
        exp = Experience.from_dict(d)
        assert exp.id == "exp_test123"
        assert exp.type == "lesson"
        assert exp.steps == ["do a", "do b"]
        assert exp.usage_count == 3


# ─── 2. WorkflowTemplate model ────────────────────────────────────

class TestWorkflowTemplateModel:
    def test_defaults(self):
        wf = WorkflowTemplate()
        assert wf.id.startswith("wf_")
        assert wf.success_rate == 0.0

    def test_to_dict_from_dict_roundtrip(self):
        wf = WorkflowTemplate(
            name="Docker Deploy Workflow",
            trigger="when deploying backend containers",
            steps=[
                {"step": "build image", "tool": "docker", "condition": ""},
                {"step": "push", "tool": "docker", "condition": ""},
                {"step": "run", "tool": "docker", "condition": "last"},
            ],
            experience_id="exp_abc",
            scene="deployment",
        )
        d = wf.to_dict()
        wf2 = WorkflowTemplate.from_dict(d)
        assert wf2.id == wf.id
        assert wf2.name == wf.name
        assert len(wf2.steps) == 3

    def test_to_prompt_text(self):
        wf = WorkflowTemplate(
            name="CI Pipeline",
            trigger="when CI runs",
            steps=[{"step": "run tests", "tool": "pytest"}],
        )
        text = wf.to_prompt_text()
        assert "CI Pipeline" in text
        assert "run tests" in text
        assert "pytest" in text


# ─── 3. ExperienceExtractor ───────────────────────────────────────

class TestExperienceExtractor:
    def test_extract_from_nodes_empty(self, graph, store):
        extractor = ExperienceExtractor(graph, store=store, min_nodes=2)
        result = extractor.extract_from_nodes([], scene="test")
        assert result is None

    def test_extract_from_nodes_too_few(self, graph, store):
        node = make_memory_node(store, node_type="raw")
        extractor = ExperienceExtractor(graph, store=store, min_nodes=2)
        result = extractor.extract_from_nodes([node], scene="deployment")
        assert result is None

    def test_extract_from_nodes_success(self, graph, store):
        nodes = [
            make_memory_node(store, content="[Tool Execution] tool=docker | action=build | result=success"),
            make_memory_node(store, content="[Tool Execution] tool=docker | action=deploy | result=success"),
            make_memory_node(store, content="Task completed successfully"),
        ]
        extractor = ExperienceExtractor(graph, store=store, min_nodes=2)
        exp = extractor.extract_from_nodes(nodes, agent_id="agent-1", scene="deployment")
        assert exp is not None
        assert isinstance(exp, Experience)
        assert exp.scene == "deployment"
        assert exp.agent_id == "agent-1"
        assert exp.confidence > 0

    def test_extract_detects_outcome_success(self, graph, store):
        nodes = [
            make_memory_node(store, content="deploy complete success"),
            make_memory_node(store, content="service running ok"),
        ]
        extractor = ExperienceExtractor(graph, store=store, min_nodes=2)
        exp = extractor.extract_from_nodes(nodes, scene="deployment")
        assert exp.outcome == "success"

    def test_extract_detects_outcome_failure(self, graph, store):
        nodes = [
            make_memory_node(store, content="deploy error timeout"),
            make_memory_node(store, content="connection refused"),
        ]
        extractor = ExperienceExtractor(graph, store=store, min_nodes=2)
        exp = extractor.extract_from_nodes(nodes, scene="deployment")
        assert exp.outcome == "failure"

    def test_extract_classifies_lesson_on_failure(self, graph, store):
        nodes = [
            make_memory_node(store, content="error: build failed"),
            make_memory_node(store, content="fail: cannot connect to docker"),
        ]
        extractor = ExperienceExtractor(graph, store=store, min_nodes=2)
        exp = extractor.extract_from_nodes(nodes, scene="deployment")
        assert exp.type in ("lesson", "workflow")

    def test_extract_full_pipeline(self, graph, store):
        for i in range(4):
            make_memory_node(
                store, node_type="summary",
                content=f"[Agent Step] step={i} tool=git action=commit result=success",
                scene="coding", agent_id="dev-agent",
            )
        extractor = ExperienceExtractor(graph, store=store, min_nodes=2)
        experiences = extractor.extract(agent_id="dev-agent", scene="coding")
        assert isinstance(experiences, list)

    def test_extract_with_llm_fallback(self, graph, store):
        """LLM that returns invalid JSON should fallback gracefully."""
        def bad_llm(prompt):
            return "not valid json at all"

        nodes = [
            make_memory_node(store, content="[Tool Execution] tool=npm | action=test"),
            make_memory_node(store, content="tests passed"),
        ]
        extractor = ExperienceExtractor(graph, store=store, llm_fn=bad_llm, min_nodes=2)
        exp = extractor.extract_from_nodes(nodes, scene="testing")
        assert exp is not None

    def test_extract_with_good_llm(self, graph, store):
        """Valid LLM JSON should produce LLM-based experience."""
        def good_llm(prompt):
            return json.dumps({
                "title": "Docker Deploy Workflow",
                "type": "workflow",
                "steps": ["build image", "push", "deploy"],
                "outcome": "success",
                "key_facts": ["docker works"],
                "conditions": {"scene": "deployment", "trigger": "deploy"},
            })

        nodes = [
            make_memory_node(store, content="[Tool Execution] tool=docker | action=build"),
            make_memory_node(store, content="[Tool Execution] tool=docker | action=push"),
        ]
        extractor = ExperienceExtractor(graph, store=store, llm_fn=good_llm, min_nodes=2)
        exp = extractor.extract_from_nodes(nodes, scene="deployment")
        assert exp is not None
        assert exp.title == "Docker Deploy Workflow"
        assert exp.steps == ["build image", "push", "deploy"]
        assert exp.metadata.get("llm_extracted") is True


# ─── 4. WorkflowGenerator ────────────────────────────────────────

class TestWorkflowGenerator:
    def make_experience(self, **kwargs):
        defaults = dict(
            title="Test Experience",
            type="workflow",
            steps=["[Tool Execution] tool=docker | action=build",
                   "[Tool Execution] tool=docker | action=deploy"],
            outcome="success",
            key_facts=["docker deploy succeeded"],
            conditions={"scene": "deployment", "trigger": "deploy"},
            scene="deployment",
            agent_id="agent-1",
        )
        defaults.update(kwargs)
        return Experience(**defaults)

    def test_generate_basic(self):
        gen = WorkflowGenerator()
        exp = self.make_experience()
        wf = gen.generate(exp)
        assert isinstance(wf, WorkflowTemplate)
        assert wf.experience_id == exp.id
        assert wf.scene == "deployment"
        assert len(wf.steps) > 0

    def test_generate_detects_docker_tool(self):
        gen = WorkflowGenerator()
        exp = self.make_experience()
        wf = gen.generate(exp)
        tools = [s.get("tool", "") for s in wf.steps if isinstance(s, dict)]
        assert "docker" in tools

    def test_generate_from_empty_steps(self):
        gen = WorkflowGenerator()
        exp = self.make_experience(steps=[], key_facts=["learned A", "learned B"])
        wf = gen.generate(exp)
        assert len(wf.steps) > 0

    def test_generate_lesson_not_in_batch(self):
        gen = WorkflowGenerator()
        experiences = [
            self.make_experience(type="workflow"),
            self.make_experience(type="lesson"),
            self.make_experience(type="pattern"),
        ]
        workflows = gen.generate_batch(experiences)
        assert len(workflows) == 2

    def test_generate_with_llm(self):
        def llm(prompt):
            return json.dumps({
                "name": "Deploy Workflow",
                "trigger": "when deploying backend",
                "steps": [
                    {"step": "build", "tool": "docker", "input_template": "docker build", "condition": ""},
                    {"step": "push", "tool": "docker", "input_template": "docker push", "condition": ""},
                ],
            })

        gen = WorkflowGenerator(llm_fn=llm)
        exp = self.make_experience()
        wf = gen.generate(exp)
        assert wf.name == "Deploy Workflow"
        assert len(wf.steps) == 2
        assert wf.metadata.get("llm_generated") is True

    def test_generate_with_bad_llm_fallback(self):
        def bad_llm(prompt):
            return "{{invalid}}"

        gen = WorkflowGenerator(llm_fn=bad_llm)
        exp = self.make_experience()
        wf = gen.generate(exp)
        assert isinstance(wf, WorkflowTemplate)


# ─── 5. ExperienceRegistry ────────────────────────────────────────

class TestExperienceRegistry:
    def test_save_and_get(self, registry):
        exp = Experience(title="Test", type="workflow", scene="test")
        exp_id = registry.save(exp)
        fetched = registry.get(exp_id)
        assert fetched is not None
        assert fetched.title == "Test"
        assert fetched.id == exp_id

    def test_get_nonexistent(self, registry):
        assert registry.get("exp_nonexistent") is None

    def test_list_experiences(self, registry):
        for i in range(3):
            registry.save(Experience(title=f"Exp {i}", scene="coding", agent_id="agent-A"))
        results = registry.list_experiences(agent_id="agent-A", scene="coding")
        assert len(results) >= 3

    def test_list_by_type(self, registry):
        registry.save(Experience(title="W1", type="workflow"))
        registry.save(Experience(title="L1", type="lesson"))
        workflows = registry.list_experiences(exp_type="workflow")
        lessons = registry.list_experiences(exp_type="lesson")
        assert all(e.type == "workflow" for e in workflows)
        assert all(e.type == "lesson" for e in lessons)

    def test_delete(self, registry):
        exp = Experience(title="To Delete")
        registry.save(exp)
        deleted = registry.delete(exp.id)
        assert deleted is True
        assert registry.get(exp.id) is None

    def test_save_workflow(self, registry):
        wf = WorkflowTemplate(name="Deploy WF", scene="deployment")
        wf_id = registry.save_workflow(wf)
        fetched = registry.get_workflow(wf_id)
        assert fetched is not None
        assert fetched.name == "Deploy WF"

    def test_list_workflows(self, registry):
        for i in range(2):
            registry.save_workflow(WorkflowTemplate(name=f"WF {i}", scene="deployment"))
        results = registry.list_workflows(scene="deployment")
        assert len(results) >= 2

    def test_record_workflow_result(self, registry):
        wf = WorkflowTemplate(name="Test WF", success_rate=0.0, usage_count=0)
        registry.save_workflow(wf)

        registry.record_workflow_result(wf.id, success=True)
        updated = registry.get_workflow(wf.id)
        assert updated.usage_count == 1
        assert updated.success_rate == 1.0

        registry.record_workflow_result(wf.id, success=False)
        updated2 = registry.get_workflow(wf.id)
        assert updated2.usage_count == 2
        assert abs(updated2.success_rate - 0.5) < 0.01

    def test_record_workflow_result_nonexistent(self, registry):
        result = registry.record_workflow_result("wf_nonexistent", True)
        assert result is False

    def test_recall_experience(self, registry):
        registry.save(Experience(
            title="Docker deploy success", type="workflow",
            steps=["build docker image", "deploy container"],
            conditions={"trigger": "deploy"},
            scene="deployment", confidence=0.9,
        ))
        registry.save(Experience(
            title="Python unit testing", type="pattern",
            steps=["run pytest", "check coverage"],
            scene="testing", confidence=0.85,
        ))
        results = registry.recall("deploy docker container", scene="deployment")
        assert len(results) > 0
        assert results[0].scene == "deployment"

    def test_recall_increments_usage_count(self, registry):
        exp = Experience(title="Docker workflow", scene="deployment")
        registry.save(exp)
        registry.recall("docker deploy")
        updated = registry.get(exp.id)
        assert updated.usage_count >= 1

    def test_recall_workflows(self, registry):
        registry.save_workflow(WorkflowTemplate(
            name="Deploy Backend", trigger="when deploying backend containers",
            scene="deployment",
        ))
        results = registry.recall_workflows("deploy backend containers")
        assert len(results) > 0

    def test_recall_for_prompt(self, registry):
        registry.save(Experience(
            title="Git commit workflow", steps=["git add", "git commit"],
            scene="coding",
        ))
        registry.save_workflow(WorkflowTemplate(
            name="Git workflow", trigger="when committing code",
            steps=[{"step": "git commit", "tool": "git"}],
            scene="coding",
        ))
        prompt_text = registry.recall_for_prompt("git commit code", scene="coding")
        assert isinstance(prompt_text, str)

    def test_stats(self, registry):
        registry.save(Experience(type="workflow", scene="ops"))
        registry.save(Experience(type="lesson", scene="ops"))
        registry.save_workflow(WorkflowTemplate(scene="ops"))

        stats = registry.stats(scene="ops")
        assert stats["total_experiences"] >= 2
        assert stats["total_workflows"] >= 1
        assert "by_type" in stats
        assert "avg_confidence" in stats


# ─── 6. SQLiteStore integration ───────────────────────────────────

class TestSQLiteStoreExperience:
    def test_put_and_get_experience(self, store):
        exp = Experience(title="Store Test", scene="dev")
        store.put_experience(exp.to_dict())
        fetched = store.get_experience(exp.id)
        assert fetched is not None
        assert fetched["title"] == "Store Test"

    def test_list_experiences_filtered(self, store):
        for i in range(3):
            e = Experience(title=f"E{i}", scene="prod", agent_id="ag-1")
            store.put_experience(e.to_dict())
        results = store.list_experiences(agent_id="ag-1", scene="prod")
        assert len(results) >= 3

    def test_delete_experience(self, store):
        exp = Experience(title="Delete Me")
        store.put_experience(exp.to_dict())
        deleted = store.delete_experience(exp.id)
        assert deleted is True
        assert store.get_experience(exp.id) is None

    def test_put_and_get_workflow_template(self, store):
        wf = WorkflowTemplate(name="WF Store Test", scene="ops")
        store.put_workflow_template(wf.to_dict())
        fetched = store.get_workflow_template(wf.id)
        assert fetched is not None
        assert fetched["name"] == "WF Store Test"

    def test_list_workflow_templates_filtered(self, store):
        for i in range(2):
            wf = WorkflowTemplate(name=f"WF{i}", scene="test-scene")
            store.put_workflow_template(wf.to_dict())
        results = store.list_workflow_templates(scene="test-scene")
        assert len(results) >= 2

    def test_steps_json_serialized(self, store):
        wf = WorkflowTemplate(
            steps=[{"step": "do A", "tool": "bash"}, {"step": "do B", "tool": ""}]
        )
        store.put_workflow_template(wf.to_dict())
        fetched = store.get_workflow_template(wf.id)
        assert isinstance(fetched["steps"], list)
        assert fetched["steps"][0]["step"] == "do A"


# ─── 7. SDK methods ───────────────────────────────────────────────

class TestSDKMethods:
    def test_extract_experiences_empty(self, memory_obj):
        result = memory_obj.extract_experiences(agent_id="no-one", scene="empty")
        assert result["experiences_extracted"] == 0
        assert result["workflows_generated"] == 0

    def test_extract_experiences_with_nodes(self, memory_obj, store):
        for i in range(3):
            make_memory_node(
                store, node_type="summary",
                content=f"[Tool Execution] tool=docker | action=step{i} | result=success",
                scene="ops", agent_id="sdk-agent",
            )
        result = memory_obj.extract_experiences(
            agent_id="sdk-agent", scene="ops", generate_workflows=True
        )
        assert isinstance(result["experiences_extracted"], int)
        assert isinstance(result["workflows_generated"], int)
        assert isinstance(result["experience_ids"], list)
        assert isinstance(result["workflow_ids"], list)

    def test_list_experiences(self, memory_obj, store):
        exp = Experience(title="SDK List Test", scene="sdk-scene")
        store.put_experience(exp.to_dict())
        results = memory_obj.list_experiences(scene="sdk-scene")
        assert len(results) >= 1

    def test_recall_experiences(self, memory_obj, store):
        exp = Experience(
            title="Docker deploy workflow",
            steps=["build", "push", "deploy"],
            scene="deployment", confidence=0.9,
        )
        store.put_experience(exp.to_dict())
        results = memory_obj.recall_experiences("docker deploy", scene="deployment")
        assert isinstance(results, list)

    def test_recall_experience_for_prompt(self, memory_obj, store):
        exp = Experience(title="Deployment workflow", steps=["build", "ship"])
        store.put_experience(exp.to_dict())
        text = memory_obj.recall_experience_for_prompt("build and ship")
        assert isinstance(text, str)

    def test_list_experience_workflows(self, memory_obj, store):
        wf = WorkflowTemplate(name="SDK WF", scene="sdk")
        store.put_workflow_template(wf.to_dict())
        results = memory_obj.list_experience_workflows(scene="sdk")
        assert len(results) >= 1

    def test_recall_workflows(self, memory_obj, store):
        wf = WorkflowTemplate(
            name="Deployment pipeline",
            trigger="when deploying",
            scene="deployment",
        )
        store.put_workflow_template(wf.to_dict())
        results = memory_obj.recall_workflows("deploy pipeline", scene="deployment")
        assert isinstance(results, list)

    def test_record_workflow_result(self, memory_obj, store):
        wf = WorkflowTemplate(name="Test WF")
        store.put_workflow_template(wf.to_dict())
        result = memory_obj.record_workflow_result(wf.id, success=True)
        assert result["updated"] is True
        assert result["success"] is True

    def test_experience_stats(self, memory_obj, store):
        store.put_experience(Experience(scene="stats-scene").to_dict())
        stats = memory_obj.experience_stats()
        assert "total_experiences" in stats
        assert "total_workflows" in stats
        assert "by_type" in stats


# ─── 8. REST API ──────────────────────────────────────────────────

class TestRESTExperience:
    """
    REST tests for Phase 23.6 endpoints.

    Uses a shared (client, store) fixture so both access the same SQLite DB.
    """

    @pytest.fixture
    def rest(self, tmp_path):
        """Returns (test_client, store) sharing the same DB path."""
        from openmemo.api.rest_server import create_app
        from openmemo.storage.sqlite_store import SQLiteStore
        db_path = str(tmp_path / "rest_test.db")
        store = SQLiteStore(db_path=db_path)
        app = create_app(db_path=db_path)
        app.config["TESTING"] = True
        client = app.test_client()
        return client, store

    def test_experience_list_empty(self, rest):
        client, _ = rest
        resp = client.get("/experience/list")
        assert resp.status_code == 200
        data = json.loads(resp.data)
        assert "results" in data
        assert "count" in data

    def test_experience_extract(self, rest):
        client, _ = rest
        resp = client.post("/experience/extract",
                           json={"agent_id": "rest-agent", "scene": "rest-test"})
        assert resp.status_code == 200
        data = json.loads(resp.data)
        assert "experiences_extracted" in data
        assert "workflows_generated" in data

    def test_experience_recall_missing_query(self, rest):
        client, _ = rest
        resp = client.post("/experience/recall", json={})
        assert resp.status_code == 400

    def test_experience_recall_with_query(self, rest):
        client, store = rest
        exp = Experience(title="Test recall", steps=["step 1"], scene="ops")
        store.put_experience(exp.to_dict())
        resp = client.post("/experience/recall",
                           json={"query": "step 1 ops", "scene": "ops"})
        assert resp.status_code == 200
        data = json.loads(resp.data)
        assert "results" in data

    def test_experience_stats(self, rest):
        client, _ = rest
        resp = client.get("/experience/stats")
        assert resp.status_code == 200
        data = json.loads(resp.data)
        assert "total_experiences" in data

    def test_experience_workflows_list(self, rest):
        client, store = rest
        wf = WorkflowTemplate(name="REST WF", scene="rest")
        store.put_workflow_template(wf.to_dict())
        resp = client.get("/experience/workflows?scene=rest")
        assert resp.status_code == 200
        data = json.loads(resp.data)
        assert data["count"] >= 1

    def test_experience_workflows_recall(self, rest):
        client, _ = rest
        resp = client.post("/experience/workflows/recall",
                           json={"query": "deploy backend"})
        assert resp.status_code == 200

    def test_experience_workflows_recall_missing_query(self, rest):
        client, _ = rest
        resp = client.post("/experience/workflows/recall", json={})
        assert resp.status_code == 400

    def test_workflow_result_endpoint(self, rest):
        client, store = rest
        wf = WorkflowTemplate(name="Result WF")
        store.put_workflow_template(wf.to_dict())
        resp = client.post(f"/experience/workflows/{wf.id}/result",
                           json={"success": True})
        assert resp.status_code == 200
        data = json.loads(resp.data)
        assert data["updated"] is True

    def test_experience_prompt_endpoint(self, rest):
        client, store = rest
        exp = Experience(title="Prompt test", steps=["a", "b"])
        store.put_experience(exp.to_dict())
        resp = client.post("/experience/prompt",
                           json={"query": "a b test"})
        assert resp.status_code == 200
        data = json.loads(resp.data)
        assert "context" in data

    def test_experience_prompt_missing_query(self, rest):
        client, _ = rest
        resp = client.post("/experience/prompt", json={})
        assert resp.status_code == 400
