org_service_test.go 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. // OrgService 作用域越权判定单测(assertInScope 纯逻辑,不连 DB)。
  2. //
  3. // assertInScope 是 Service 层"第一道防线"——请求显式 id 与 token 作用域冲突时返回 1004。
  4. // 数据层的第二道防线(Repository Scoping 强制过滤)由 handler 包 e2e 覆盖。
  5. package service
  6. import (
  7. "errors"
  8. "testing"
  9. apperrors "yuansiwei/unified-backend/internal/infra/errors"
  10. appscope "yuansiwei/unified-backend/internal/infra/scope"
  11. )
  12. // newScope 快捷构造作用域(单测用)。
  13. func newScope(inst, campus int64, super bool, role string) *appscope.Scope {
  14. return &appscope.Scope{InstitutionID: inst, CampusID: campus, SuperAdmin: super, Role: role, UserID: "01TESTUID000000000000000"}
  15. }
  16. // TestAssertInScope 覆盖 assertInScope 各分支。
  17. func TestAssertInScope(t *testing.T) {
  18. svc := &OrgService{} // 零值;assertInScope 不触碰 db/仓储
  19. cases := []struct {
  20. name string
  21. sc *appscope.Scope
  22. reqInst int64
  23. reqCamp int64
  24. wantErr bool
  25. wantCode int
  26. }{
  27. {"nil_作用域_按未授权拒绝", nil, 0, 0, true, apperrors.CodeUnauth},
  28. {"超管_跨机构放行", newScope(0, 0, true, "super_admin"), 99, 99, false, 0},
  29. {"未显式指定_放行", newScope(11, 3, false, "teacher"), 0, 0, false, 0},
  30. {"机构一致_放行", newScope(11, 3, false, "teacher"), 11, 0, false, 0},
  31. {"校区一致_放行", newScope(11, 3, false, "teacher"), 11, 3, false, 0},
  32. {"跨机构_越权1004", newScope(11, 3, false, "teacher"), 22, 0, true, apperrors.CodeScopeViolation},
  33. {"校区锁定_token请求别的校区_越权1004", newScope(11, 3, false, "teacher"), 0, 4, true, apperrors.CodeScopeViolation},
  34. {"机构级token_下钻别的校区_放行", newScope(11, 0, false, "academic"), 0, 4, false, 0},
  35. {"机构级token_跨机构_越权1004", newScope(11, 0, false, "academic"), 22, 0, true, apperrors.CodeScopeViolation},
  36. }
  37. for _, tc := range cases {
  38. t.Run(tc.name, func(t *testing.T) {
  39. err := svc.assertInScope(tc.sc, tc.reqInst, tc.reqCamp)
  40. if tc.wantErr {
  41. if !errors.Is(err, apperrors.ErrUnscoped) && !(tc.wantCode == apperrors.CodeUnauth && errors.Is(err, apperrors.ErrUnauth)) {
  42. t.Fatalf("期望 code=%d 错误,实际 %v", tc.wantCode, err)
  43. }
  44. return
  45. }
  46. if err != nil {
  47. t.Fatalf("期望放行,实际 %v", err)
  48. }
  49. })
  50. }
  51. }