- test DB 세션마다 자동 create/drop (팀원은 Postgres만 있으면 pytest 한 방) - auth_headers 시드 픽스처(무인증 /auth/create 제거 대응) + other_company_id - 커버: 회사 스코프(견적·상품·협력사·대시보드·세팅), 견적 마감 재견적 O/X + 알림, 견적 생성·목표가, 알림함 읽기, 회사유저 OWNER 게이팅, 기존 파일 검증/기대결과 주석 정비 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
89 lines
4.0 KiB
Python
89 lines
4.0 KiB
Python
"""직원 계정 관리(/v1/company/user/*) 테스트 — '최고관리자만' 쓸 수 있고, '자기 회사'만 다뤄지는지 확인.
|
|
|
|
- 일반 직원 계정으로는 이 기능을 못 쓴다(HTTP 403 으로 막힘).
|
|
- 최고관리자는 자기 회사 직원만 목록에 보이고, 생성도 자기 회사로 된다(남의 회사 직원은 안 보임).
|
|
- 로그인 아이디는 전체에서 유일해야 해서, 같은 아이디로 또 만들면 거부된다(코드 1201).
|
|
"""
|
|
import uuid
|
|
|
|
from sqlalchemy import text
|
|
|
|
from common.enums import UserRole, UserStatus
|
|
|
|
|
|
async def test_regular_user_forbidden_on_owner_endpoints(client, auth_headers):
|
|
"""검증: 일반 USER 토큰으로 최고관리자 전용 엔드포인트(list·create) 호출.
|
|
기대결과: 둘 다 HTTP 403(RequireOwner 차단)."""
|
|
h = await auth_headers("plainuser") # role=USER 기본
|
|
|
|
r = await client.get("/v1/company/user/list", headers=h)
|
|
assert r.status_code == 403
|
|
|
|
r = await client.post(
|
|
"/v1/company/user/create", json={"id": "x", "password": "p", "name": "n"}, headers=h
|
|
)
|
|
assert r.status_code == 403
|
|
|
|
|
|
async def test_owner_lists_only_own_company_users(client, auth_headers, company_id, other_company_id, db_engine):
|
|
"""검증: 회사A OWNER + A직원 + B직원(타사)을 두고 OWNER 가 유저 목록 조회.
|
|
기대결과: 본인·A직원은 목록에 있고 타사(B) 직원은 없음(회사 스코프)."""
|
|
owner_h = await auth_headers("ownerA", role=UserRole.OWNER.value) # 회사 A owner
|
|
await _seed_user(db_engine, company_id, "empA") # 같은 회사 직원
|
|
await _seed_user(db_engine, other_company_id, "empB") # 다른 회사 직원
|
|
|
|
r = await client.get("/v1/company/user/list", headers=owner_h)
|
|
ids = {u["id"] for u in r.json()["users"]}
|
|
assert "ownerA" in ids # 본인
|
|
assert "empA" in ids # 자기 회사 직원
|
|
assert "empB" not in ids # 타사 직원은 안 보임
|
|
|
|
|
|
async def test_owner_creates_user_in_own_company(client, auth_headers):
|
|
"""검증: OWNER 가 직원 계정을 생성한 뒤 목록 조회.
|
|
기대결과: 생성 success=True, 생성한 유저가 자기 회사 목록에 노출."""
|
|
owner_h = await auth_headers("ownerC", role=UserRole.OWNER.value)
|
|
|
|
r = await client.post(
|
|
"/v1/company/user/create",
|
|
json={"id": "newemp", "password": "pw1234", "name": "직원"},
|
|
headers=owner_h,
|
|
)
|
|
assert r.json()["result"]["success"] is True
|
|
|
|
r = await client.get("/v1/company/user/list", headers=owner_h)
|
|
ids = {u["id"] for u in r.json()["users"]}
|
|
assert "newemp" in ids
|
|
|
|
|
|
async def test_duplicate_login_id_rejected(client, auth_headers):
|
|
"""검증: OWNER 가 같은 로그인 ID 로 직원 계정을 2번 생성.
|
|
기대결과: 1번째 success=True, 2번째 success=False, code=1201(ACCOUNT_ALREADY_EXIST)."""
|
|
owner_h = await auth_headers("ownerD", role=UserRole.OWNER.value)
|
|
|
|
r1 = await client.post(
|
|
"/v1/company/user/create", json={"id": "dup", "password": "pw1234", "name": "n"}, headers=owner_h
|
|
)
|
|
assert r1.json()["result"]["success"] is True
|
|
|
|
r2 = await client.post(
|
|
"/v1/company/user/create", json={"id": "dup", "password": "pw5678", "name": "n2"}, headers=owner_h
|
|
)
|
|
body = r2.json()
|
|
assert body["result"]["success"] is False
|
|
assert body["result"]["code"] == 1201
|
|
|
|
|
|
# ===== 헬퍼 (위 테스트들이 쓰는 도우미) =====
|
|
async def _seed_user(engine, company_id, login_id, *, role=UserRole.USER.value):
|
|
"""로그인 안 하는 소속 직원 시드(목록 스코프 확인용). 비번은 임의값."""
|
|
async with engine.begin() as conn:
|
|
await conn.execute(
|
|
text(
|
|
"INSERT INTO users (user_id, company_id, id, password, name, status, role, last_accessed_at) "
|
|
"VALUES (:uid, :cid, :id, 'x', 'n', :status, :role, now())"
|
|
),
|
|
{"uid": uuid.uuid4(), "cid": uuid.UUID(company_id), "id": login_id,
|
|
"status": UserStatus.ACTIVE.value, "role": role},
|
|
)
|