mirror of
https://github.com/superdesigndev/treg.git
synced 2026-10-02 03:24:35 +08:00
PATCH /orgs/{id} (admin+) changes name and/or slug. The slug is baked into signed team
keys, ~/.treg, MCP pins and agent addresses, so a slug change retires the old one into
Org.previous_slug: _resolve_org still honours it, no other team may claim it, and
pre-rename agents keep their short names. Dashboard form under Team settings and
`treg org rename`. Migration 0040.
Two migration-pinned tests inserted an Org through the current ORM at an old revision;
they now insert only the columns that exist there.
Fragments: multi-tenancy, data-model, api, cli, dashboard; USAGE.md, skill.md, llms.txt.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
62 lines
3.3 KiB
Python
62 lines
3.3 KiB
Python
"""PATCH /orgs/{id}: change a team's name and slug; the retired slug stays an alias."""
|
|
from httpx import AsyncClient
|
|
|
|
from tests.test_orgs_mgmt import _h, _register, _team_with_member, c # noqa: F401 (fixture)
|
|
|
|
|
|
async def test_admin_renames_name_and_slug_member_cannot(c: AsyncClient):
|
|
org_id, otok, mtok, *_ = await _team_with_member(c)
|
|
r = await c.patch(f"/orgs/{org_id}", headers=_h(mtok), json={"name": "Nope"})
|
|
assert r.status_code == 403
|
|
r = await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={"name": " Team B "})
|
|
assert r.status_code == 200, r.text
|
|
assert r.json() == {"org_id": org_id, "org": "team-a", "previous_slug": None, "name": "Team B"}
|
|
r = await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={"slug": "team-b"})
|
|
assert r.status_code == 200, r.text
|
|
assert r.json() == {"org_id": org_id, "org": "team-b", "previous_slug": "team-a", "name": "Team B"}
|
|
orgs = (await c.get("/orgs", headers=_h(otok))).json()
|
|
assert [o["slug"] for o in orgs if o["org_id"] == org_id] == ["team-b"]
|
|
|
|
|
|
async def test_old_slug_keeps_resolving_for_pinned_credentials(c: AsyncClient):
|
|
org_id, otok, mtok, *_ = await _team_with_member(c)
|
|
# otok was minted by POST /orgs with org=team-a baked into its claim
|
|
await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={"slug": "team-b"})
|
|
for slug in ("team-a", "team-b"):
|
|
r = await c.post("/agents/checkin", headers={**_h(otok), "X-Treg-Org": slug})
|
|
assert r.status_code == 200, (slug, r.text)
|
|
assert r.json()["org"] == "team-b"
|
|
assert (await c.get("/tools", headers=_h(mtok))).status_code == 200
|
|
|
|
|
|
async def test_retired_slug_cannot_be_taken_by_another_team(c: AsyncClient):
|
|
org_id, otok, *_ = await _team_with_member(c)
|
|
await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={"slug": "team-b"})
|
|
other = await _register(c, "other@x.dev")
|
|
r = await c.post("/orgs", headers=_h(other), json={"name": "Team A"})
|
|
assert r.json()["org"] == "team-a-2"
|
|
other_id, other_tok = r.json()["org_id"], r.json()["token"]
|
|
for taken in ("team-a", "team-b"):
|
|
r = await c.patch(f"/orgs/{other_id}", headers=_h(other_tok), json={"slug": taken})
|
|
assert r.status_code == 409, (taken, r.text)
|
|
|
|
|
|
async def test_bad_slugs_rejected(c: AsyncClient):
|
|
org_id, otok, *_ = await _team_with_member(c)
|
|
for bad in ("Bad Slug!", "ab", "sbx-0123456789ab", "x" * 41):
|
|
r = await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={"slug": bad})
|
|
assert r.status_code == 400, (bad, r.text)
|
|
assert (await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={"name": " "})).status_code == 400
|
|
assert (await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={})).status_code == 422
|
|
|
|
|
|
async def test_agent_named_before_rename_keeps_its_short_name(c: AsyncClient):
|
|
org_id, otok, *_ = await _team_with_member(c)
|
|
r = await c.post(f"/orgs/{org_id}/agents", headers=_h(otok), json={"name": "deploy"})
|
|
assert r.status_code == 200, r.text
|
|
await c.patch(f"/orgs/{org_id}", headers=_h(otok), json={"slug": "team-b"})
|
|
names = {a["name"] for a in (await c.get(f"/orgs/{org_id}/agents", headers=_h(otok))).json()}
|
|
assert "deploy" in names
|
|
keys = (await c.get(f"/orgs/{org_id}/api-keys", headers=_h(otok))).json()
|
|
assert any(k.get("assigned_name") == "deploy" or k.get("name") == "deploy" for k in keys), keys
|