/ tests / test_project_clone.py
test_project_clone.py
  1  import random
  2  import pytest
  3  from fastapi.testclient import TestClient
  4  
  5  from restai.config import RESTAI_DEFAULT_PASSWORD
  6  from restai.main import app
  7  
  8  suffix = str(random.randint(0, 10000000))
  9  team_name = f"clone_team_{suffix}"
 10  llm_name = f"clone_llm_{suffix}"
 11  project_name = f"clone_proj_{suffix}"
 12  cloned_name = f"cloned_proj_{suffix}"
 13  
 14  team_id = None
 15  project_id = None
 16  cloned_id = None
 17  
 18  ADMIN = ("admin", RESTAI_DEFAULT_PASSWORD)
 19  
 20  
 21  @pytest.fixture(scope="module")
 22  def client():
 23      with TestClient(app) as c:
 24          yield c
 25  
 26  
 27  def test_setup(client):
 28      """Create a team, LLM, and block project with system prompt and censorship."""
 29      global team_id, project_id
 30      # Create LLM
 31      client.post(
 32          "/llms",
 33          json={
 34              "name": llm_name,
 35              "class_name": "OpenAI",
 36              "options": {"model": "gpt-test", "api_key": "sk-fake"},
 37              "privacy": "public",
 38          },
 39          auth=ADMIN,
 40      )
 41  
 42      # Create team with the LLM
 43      resp = client.post(
 44          "/teams",
 45          json={"name": team_name, "users": [], "admins": [], "llms": [llm_name]},
 46          auth=ADMIN,
 47      )
 48      assert resp.status_code == 201
 49      team_id = resp.json()["id"]
 50  
 51      # Create block project (no LLM required)
 52      resp = client.post(
 53          "/projects",
 54          json={"name": project_name, "type": "block", "team_id": team_id},
 55          auth=ADMIN,
 56      )
 57      assert resp.status_code == 201
 58      project_id = resp.json()["project"]
 59  
 60      # Set system prompt and censorship via PATCH
 61      resp = client.patch(
 62          f"/projects/{project_id}",
 63          json={
 64              "system": "You are a helpful test assistant.",
 65              "censorship": "This content is not allowed.",
 66          },
 67          auth=ADMIN,
 68      )
 69      assert resp.status_code == 200
 70  
 71  
 72  def test_clone_project(client):
 73      """Clone a project and verify settings are copied."""
 74      global cloned_id
 75      resp = client.post(
 76          f"/projects/{project_id}/clone",
 77          json={"name": cloned_name},
 78          auth=ADMIN,
 79      )
 80      assert resp.status_code == 201
 81      cloned_id = resp.json()["project"]
 82      assert cloned_id != project_id
 83  
 84      # Verify cloned project exists and settings match
 85      resp = client.get(f"/projects/{cloned_id}", auth=ADMIN)
 86      assert resp.status_code == 200
 87      data = resp.json()
 88      assert data["system"] == "You are a helpful test assistant."
 89      assert data["censorship"] == "This content is not allowed."
 90      assert data["type"] == "block"
 91  
 92  
 93  def test_clone_duplicate_name(client):
 94      """Cloning with a name that already exists returns 409."""
 95      resp = client.post(
 96          f"/projects/{project_id}/clone",
 97          json={"name": cloned_name},
 98          auth=ADMIN,
 99      )
100      assert resp.status_code == 409
101  
102  
103  def test_clone_nonexistent(client):
104      """Cloning a project that doesn't exist returns 404."""
105      resp = client.post(
106          "/projects/999999/clone",
107          json={"name": f"ghost_{suffix}"},
108          auth=ADMIN,
109      )
110      assert resp.status_code in (404, 403)
111  
112  
113  def test_cleanup(client):
114      """Remove all test resources."""
115      if cloned_id:
116          client.delete(f"/projects/{cloned_id}", auth=ADMIN)
117      if project_id:
118          client.delete(f"/projects/{project_id}", auth=ADMIN)
119      if team_id:
120          client.delete(f"/teams/{team_id}", auth=ADMIN)
121      client.delete(f"/llms/{llm_name}", auth=ADMIN)