"""合作方通讯录的每一个端点都必须挡住未认证请求。 这个文件存在的理由是一次真实的生产泄漏:六个端点的 `Depends()` 全都漏了 调用括号(`Depends(require_admin_read)` 而不是 `Depends(require_admin_read())`), FastAPI 于是把工厂本身当依赖调用一次、拿里面那个 checker 当作「值」注入, **鉴权一次都没跑**。未认证 curl 打 `/overview` 拿到 200 和全部 381 人名册。 仓库里本来就有一道静态闸(`tests/test_core/test_dependency_factory_guard.py`, 正则扫 `Depends()` 的裸引用),它会抓到这个 —— 只是那轮我没跑 `tests/test_core`。这里再补一道**运行时**的:静态闸认的是写法,这个认的是 真实响应码,写法换个花样(比如换个工厂名)也漏不掉。 名册里是别家公司员工的个人信息(姓名、企业邮箱、GitHub ID、部门), 这个前缀下**没有任何**面向员工的入口,更不该有面向匿名者的。 """ from __future__ import annotations import pytest pytestmark = pytest.mark.asyncio # 每个端点一行:(method, path)。新增端点必须同时加到这里 —— 一个不在 # 这张表里的端点等于没有被这道闸覆盖。 _ENDPOINTS = [ ("GET", "/api/v1/partner-directory/overview"), ("GET", "/api/v1/partner-directory/members"), ("GET", "/api/v1/partner-directory/members/export"), ("GET", "/api/v1/partner-directory/links"), ("POST", "/api/v1/partner-directory/links/00000000-0000-0000-0000-000000000000/review"), ("POST", "/api/v1/partner-directory/sync"), ] async def test_every_endpoint_rejects_an_anonymous_caller(client): """🔴 未认证一律不能是 2xx。""" leaked: list[str] = [] for method, path in _ENDPOINTS: resp = await client.request(method, path, json={}) if resp.status_code < 400: leaked.append(f"{method} {path} -> {resp.status_code}") assert not leaked, ( "这些端点未认证就能调通,鉴权没生效:\n" + "\n".join(leaked) ) async def test_the_endpoint_list_matches_the_router(client): """防空转:端点表必须和路由表一致。 没有这条,有人新增一个端点而忘了加进 `_ENDPOINTS`,上面那个用例会 继续全绿 —— 一道只覆盖旧端点的闸比没有闸更糟,因为它让人以为覆盖了。 """ from app.main import app # 用 openapi() 而不是 app.routes:本仓 ``app.routes`` 遍历不出这些 # 路径(永远是空集),照它写这条断言会永远真空通过。这条断言的 # 第一版就是那么写的,是下面那句 ``assert actual`` 把它抓出来的。 spec = app.openapi() actual = { (method.upper(), path) for path, ops in spec["paths"].items() if path.startswith("/api/v1/partner-directory") for method in ops if method.upper() not in {"HEAD", "OPTIONS"} } listed = { (m, p.replace("00000000-0000-0000-0000-000000000000", "{link_id}")) for m, p in _ENDPOINTS } assert actual, "路由一个都没扫到 —— 这条断言本身失效了,先修它" assert actual == listed, ( f"端点表和路由表对不上。\n只在路由里:{actual - listed}\n" f"只在表里:{listed - actual}" ) async def test_an_employee_token_is_not_enough(client, employee_headers): """普通员工也不行 —— 名册是别家公司员工的个人信息。""" resp = await client.get( "/api/v1/partner-directory/overview", headers=employee_headers, ) assert resp.status_code == 403, resp.text async def test_an_admin_can_read_it(client, admin_headers): """闸别关过头:管理员必须读得到,否则「修好了」和「关掉了」分不出来。""" resp = await client.get( "/api/v1/partner-directory/overview", headers=admin_headers, ) assert resp.status_code == 200, resp.text assert resp.json()["success"] is True