test_pl_vehicle_recharge_mine_vehicles.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. """mineVehicles:车辆人员归属过滤与写校验。"""
  2. from __future__ import annotations
  3. from datetime import date, datetime
  4. from types import SimpleNamespace
  5. import pytest
  6. from sqlalchemy import create_engine
  7. from sqlalchemy.orm import sessionmaker
  8. from app.models.models import SysRole, SysUserRole
  9. from app.models.pl_models import (
  10. PlPersonnel,
  11. PlVehicle,
  12. PlVehiclePersonnel,
  13. PlVehicleRechargeApply,
  14. )
  15. def _make_session():
  16. engine = create_engine("sqlite://")
  17. for table in (
  18. PlVehicle.__table__,
  19. PlPersonnel.__table__,
  20. PlVehiclePersonnel.__table__,
  21. PlVehicleRechargeApply.__table__,
  22. SysRole.__table__,
  23. SysUserRole.__table__,
  24. ):
  25. table.create(engine)
  26. return sessionmaker(bind=engine)()
  27. def _user(uid: str = "u-driver") -> SimpleNamespace:
  28. return SimpleNamespace(id=uid, name="司机甲", dept_id="d1", role="driver", tenant_no="t1")
  29. def _seed_person_vehicle(db, *, user_id="u-driver", plate="鄂A11111", vehicle_id="veh-a"):
  30. db.add(
  31. PlVehicle(
  32. id=vehicle_id,
  33. vehicle_type="self",
  34. license_plate=plate,
  35. tenant_no="t1",
  36. create_dept_id="d1",
  37. del_flag=0,
  38. )
  39. )
  40. db.add(
  41. PlPersonnel(
  42. id="p-1",
  43. personnel_name="司机甲",
  44. personnel_type="driver",
  45. personnel_affiliation="self",
  46. status="1",
  47. sys_user_id=user_id,
  48. tenant_no="t1",
  49. create_dept_id="d1",
  50. del_flag=0,
  51. )
  52. )
  53. db.add(
  54. PlVehiclePersonnel(
  55. id="vp-1",
  56. vehicle_id=vehicle_id,
  57. personnel_id="p-1",
  58. role="driver",
  59. is_default=1,
  60. tenant_no="t1",
  61. create_dept_id="d1",
  62. del_flag=0,
  63. )
  64. )
  65. def _seed_admin_role(db, *, user_id="u-admin", role_name="管理员", role_id="role-admin"):
  66. db.add(
  67. SysRole(
  68. id=role_id,
  69. role_name=role_name,
  70. status="1",
  71. tenant_no="t1",
  72. create_dept_id="d1",
  73. create_time=datetime.now(),
  74. update_time=datetime.now(),
  75. del_flag=0,
  76. )
  77. )
  78. db.add(
  79. SysUserRole(
  80. id=f"ur-{role_id}",
  81. user_id=user_id,
  82. role_id=role_id,
  83. tenant_no="t1",
  84. create_dept_id="d1",
  85. create_time=datetime.now(),
  86. update_time=datetime.now(),
  87. del_flag=0,
  88. )
  89. )
  90. def test_license_plates_for_user_personnel():
  91. from app.services.pl_vehicle_mine_vehicles import license_plates_for_user_personnel
  92. db = _make_session()
  93. _seed_person_vehicle(db)
  94. db.add(
  95. PlVehicle(
  96. id="veh-b",
  97. vehicle_type="self",
  98. license_plate="鄂A22222",
  99. tenant_no="t1",
  100. create_dept_id="d1",
  101. del_flag=0,
  102. )
  103. )
  104. db.commit()
  105. plates = license_plates_for_user_personnel(db, "t1", "u-driver")
  106. assert plates == {"鄂A11111"}
  107. def test_license_plates_empty_when_no_personnel():
  108. from app.services.pl_vehicle_mine_vehicles import license_plates_for_user_personnel
  109. db = _make_session()
  110. db.commit()
  111. assert license_plates_for_user_personnel(db, "t1", "u-nobody") == set()
  112. def test_user_is_pl_recharge_admin_by_role_name():
  113. from app.services.pl_vehicle_mine_vehicles import (
  114. should_restrict_mine_vehicles,
  115. user_is_pl_recharge_admin,
  116. )
  117. db = _make_session()
  118. _seed_admin_role(db, user_id="u-admin", role_name="管理员")
  119. _seed_admin_role(
  120. db, user_id="u-super", role_name="超级管理员", role_id="role-super"
  121. )
  122. db.commit()
  123. assert user_is_pl_recharge_admin(db, "t1", "u-admin") is True
  124. assert user_is_pl_recharge_admin(db, "t1", "u-super") is True
  125. assert user_is_pl_recharge_admin(db, "t1", "u-driver") is False
  126. assert (
  127. should_restrict_mine_vehicles(db, "t1", "u-admin", mine_vehicles=True) is False
  128. )
  129. assert (
  130. should_restrict_mine_vehicles(db, "t1", "u-driver", mine_vehicles=True) is True
  131. )
  132. assert (
  133. should_restrict_mine_vehicles(db, "t1", "u-admin", mine_vehicles=False) is False
  134. )
  135. def _seed_apply(db, *, apply_id, plate, applicant_id, status="draft"):
  136. db.add(
  137. PlVehicleRechargeApply(
  138. id=apply_id,
  139. apply_no=f"SXSQ{apply_id}",
  140. license_plate=plate,
  141. recharge_type="etc",
  142. apply_date=date.today(),
  143. applicant_id=applicant_id,
  144. status=status,
  145. tenant_no="t1",
  146. create_by=applicant_id,
  147. create_dept_id="d1",
  148. create_time=datetime.now(),
  149. update_time=datetime.now(),
  150. del_flag=0,
  151. )
  152. )
  153. def test_list_applies_mine_vehicles_includes_others_on_my_plate(monkeypatch):
  154. from app.services import pl_vehicle_recharge_service as svc
  155. db = _make_session()
  156. _seed_person_vehicle(db)
  157. _seed_apply(db, apply_id="a1", plate="鄂A11111", applicant_id="u-office")
  158. _seed_apply(db, apply_id="a2", plate="鄂A99999", applicant_id="u-office")
  159. db.commit()
  160. monkeypatch.setattr(svc, "apply_pl_scope", lambda query, *_a, **_k: query)
  161. monkeypatch.setattr(svc, "assert_pl_row_in_scope", lambda *_a, **_k: None)
  162. monkeypatch.setattr(svc, "_resolve_user_name", lambda *_a, **_k: "X")
  163. monkeypatch.setattr(
  164. svc,
  165. "apply_to_read",
  166. lambda row, db, **kw: SimpleNamespace(
  167. id=row.id, licensePlate=row.license_plate, applicantId=row.applicant_id
  168. ),
  169. )
  170. items, total = svc.list_applies(
  171. db, "t1", _user(), mine_vehicles=True, page=1, page_size=20
  172. )
  173. assert total == 1
  174. assert items[0].id == "a1"
  175. def test_list_applies_mine_vehicles_admin_sees_all_plates(monkeypatch):
  176. from app.services import pl_vehicle_recharge_service as svc
  177. db = _make_session()
  178. _seed_admin_role(db, user_id="u-admin")
  179. _seed_apply(db, apply_id="a1", plate="鄂A11111", applicant_id="u-office")
  180. _seed_apply(db, apply_id="a2", plate="鄂A99999", applicant_id="u-office")
  181. db.commit()
  182. monkeypatch.setattr(svc, "apply_pl_scope", lambda query, *_a, **_k: query)
  183. monkeypatch.setattr(
  184. svc,
  185. "apply_to_read",
  186. lambda row, db, **kw: SimpleNamespace(id=row.id),
  187. )
  188. items, total = svc.list_applies(
  189. db, "t1", _user("u-admin"), mine_vehicles=True, page=1, page_size=20
  190. )
  191. assert total == 2
  192. assert {i.id for i in items} == {"a1", "a2"}
  193. def test_get_apply_mine_vehicles_allows_others_on_my_plate(monkeypatch):
  194. from app.services import pl_vehicle_recharge_service as svc
  195. db = _make_session()
  196. _seed_person_vehicle(db)
  197. _seed_apply(db, apply_id="a1", plate="鄂A11111", applicant_id="u-office")
  198. db.commit()
  199. monkeypatch.setattr(
  200. svc,
  201. "assert_pl_row_in_scope",
  202. lambda *_a, **_k: (_ for _ in ()).throw(
  203. AssertionError("should not use data scope when mine_vehicles")
  204. ),
  205. )
  206. monkeypatch.setattr(svc, "_resolve_user_name", lambda *_a, **_k: "X")
  207. monkeypatch.setattr(
  208. svc,
  209. "apply_to_read",
  210. lambda row, db, **kw: SimpleNamespace(id=row.id),
  211. )
  212. result = svc.get_apply(db, "t1", _user(), "a1", mine_vehicles=True)
  213. assert result.id == "a1" or getattr(result, "id", None) == "a1"
  214. def test_get_apply_mine_vehicles_rejects_foreign_plate(monkeypatch):
  215. from fastapi import HTTPException
  216. from app.services import pl_vehicle_recharge_service as svc
  217. db = _make_session()
  218. _seed_person_vehicle(db)
  219. _seed_apply(db, apply_id="a2", plate="鄂A99999", applicant_id="u-office")
  220. db.commit()
  221. monkeypatch.setattr(svc, "_resolve_user_name", lambda *_a, **_k: "X")
  222. with pytest.raises(HTTPException) as exc:
  223. svc.get_apply(db, "t1", _user(), "a2", mine_vehicles=True)
  224. assert exc.value.status_code == 403
  225. assert "无权" in exc.value.detail
  226. def test_get_apply_mine_vehicles_admin_allows_foreign_plate(monkeypatch):
  227. from app.services import pl_vehicle_recharge_service as svc
  228. db = _make_session()
  229. _seed_admin_role(db, user_id="u-admin")
  230. _seed_apply(db, apply_id="a2", plate="鄂A99999", applicant_id="u-office")
  231. db.commit()
  232. monkeypatch.setattr(
  233. svc,
  234. "assert_pl_row_in_scope",
  235. lambda *_a, **_k: (_ for _ in ()).throw(
  236. AssertionError("admin mineVehicles should skip data scope")
  237. ),
  238. )
  239. monkeypatch.setattr(
  240. svc,
  241. "apply_to_read",
  242. lambda row, db, **kw: SimpleNamespace(id=row.id),
  243. )
  244. result = svc.get_apply(db, "t1", _user("u-admin"), "a2", mine_vehicles=True)
  245. assert result.id == "a2"
  246. def test_create_apply_mine_vehicles_rejects_foreign_plate(monkeypatch):
  247. from fastapi import HTTPException
  248. from app.schemas.pl.vehicle_recharge import VehicleRechargeApplyCreate
  249. from app.services import pl_vehicle_recharge_service as svc
  250. db = _make_session()
  251. _seed_person_vehicle(db)
  252. db.commit()
  253. monkeypatch.setattr(svc, "apply_pl_scope", lambda query, *_a, **_k: query)
  254. monkeypatch.setattr(svc, "assert_pl_row_in_scope", lambda *_a, **_k: None)
  255. monkeypatch.setattr(svc, "_resolve_user_name", lambda *_a, **_k: "X")
  256. monkeypatch.setattr(svc, "_resolve_applicant_id", lambda *_a, **_k: "u-driver")
  257. monkeypatch.setattr(svc, "_assert_vehicle_exists", lambda *_a, **_k: None)
  258. monkeypatch.setattr(svc, "generate_apply_no", lambda *_a, **_k: "SXSQTEST1")
  259. payload = VehicleRechargeApplyCreate(
  260. licensePlate="鄂A99999",
  261. rechargeType="etc",
  262. applyDate=date.today(),
  263. odometerKm=1000,
  264. attachmentUrls="http://example.com/a.jpg",
  265. )
  266. with pytest.raises(HTTPException) as ei:
  267. svc.create_apply(db, "t1", _user(), payload, mine_vehicles=True)
  268. assert ei.value.status_code in (400, 403)
  269. def test_create_apply_mine_vehicles_admin_allows_foreign_plate(monkeypatch):
  270. from app.schemas.pl.vehicle_recharge import VehicleRechargeApplyCreate
  271. from app.services import pl_vehicle_recharge_service as svc
  272. db = _make_session()
  273. _seed_admin_role(db, user_id="u-admin")
  274. db.commit()
  275. monkeypatch.setattr(svc, "_assert_vehicle_exists", lambda *_a, **_k: None)
  276. monkeypatch.setattr(svc, "generate_apply_no", lambda *_a, **_k: "SXSQADMIN1")
  277. monkeypatch.setattr(
  278. svc,
  279. "apply_to_read",
  280. lambda row, db, **kw: SimpleNamespace(
  281. id=row.id, licensePlate=row.license_plate, applicantId=row.applicant_id
  282. ),
  283. )
  284. payload = VehicleRechargeApplyCreate(
  285. licensePlate="鄂A99999",
  286. rechargeType="etc",
  287. applyDate=date.today(),
  288. odometerKm=1000,
  289. attachmentUrls="http://example.com/a.jpg",
  290. )
  291. result = svc.create_apply(
  292. db, "t1", _user("u-admin"), payload, mine_vehicles=True
  293. )
  294. assert result.licensePlate == "鄂A99999"
  295. assert result.applicantId == "u-admin"
  296. def test_update_cancel_submit_require_own_applicant(monkeypatch):
  297. from fastapi import HTTPException
  298. from app.services import pl_vehicle_recharge_service as svc
  299. db = _make_session()
  300. _seed_person_vehicle(db)
  301. _seed_apply(
  302. db, apply_id="a1", plate="鄂A11111", applicant_id="u-office", status="draft"
  303. )
  304. db.commit()
  305. monkeypatch.setattr(svc, "assert_pl_row_in_scope", lambda *_a, **_k: None)
  306. monkeypatch.setattr(svc, "_resolve_user_name", lambda *_a, **_k: "X")
  307. with pytest.raises(HTTPException) as ei:
  308. svc.cancel_apply(db, "t1", _user(), "a1", mine_vehicles=True)
  309. assert ei.value.status_code == 403
  310. def test_admin_cancel_still_requires_own_applicant(monkeypatch):
  311. from fastapi import HTTPException
  312. from app.services import pl_vehicle_recharge_service as svc
  313. db = _make_session()
  314. _seed_admin_role(db, user_id="u-admin")
  315. _seed_apply(
  316. db, apply_id="a1", plate="鄂A99999", applicant_id="u-office", status="draft"
  317. )
  318. db.commit()
  319. monkeypatch.setattr(svc, "_resolve_user_name", lambda *_a, **_k: "X")
  320. with pytest.raises(HTTPException) as ei:
  321. svc.cancel_apply(db, "t1", _user("u-admin"), "a1", mine_vehicles=True)
  322. assert ei.value.status_code == 403