@things-factory/auth-base 10.1.278 → 10.1.280

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/package.json +24 -7
  2. package/CHANGELOG.md +0 -55
  3. package/dist-client/actions/auth.js.map +0 -1
  4. package/dist-client/apply-domain-theme.js.map +0 -1
  5. package/dist-client/auth.js.map +0 -1
  6. package/dist-client/bootstrap.js.map +0 -1
  7. package/dist-client/directive/privileged.js.map +0 -1
  8. package/dist-client/domain-required.js.map +0 -1
  9. package/dist-client/index.js.map +0 -1
  10. package/dist-client/profiled.js.map +0 -1
  11. package/dist-client/reducers/auth.js.map +0 -1
  12. package/dist-client/tsconfig.tsbuildinfo +0 -1
  13. package/dist-client/verify-webauthn.js.map +0 -1
  14. package/dist-server/ai-entry/access-assistant.js.map +0 -1
  15. package/dist-server/ai-entry/access-entry-targets.js.map +0 -1
  16. package/dist-server/ai-entry/entry-target-shape.js.map +0 -1
  17. package/dist-server/constants/error-code.js.map +0 -1
  18. package/dist-server/constants/error-message.js.map +0 -1
  19. package/dist-server/constants/max-age.js.map +0 -1
  20. package/dist-server/controllers/access-request.js.map +0 -1
  21. package/dist-server/controllers/account-mail.js.map +0 -1
  22. package/dist-server/controllers/auth.js.map +0 -1
  23. package/dist-server/controllers/change-pwd.js.map +0 -1
  24. package/dist-server/controllers/confirm-identity.js.map +0 -1
  25. package/dist-server/controllers/delete-user.js.map +0 -1
  26. package/dist-server/controllers/passkey.js.map +0 -1
  27. package/dist-server/controllers/password-setup.js.map +0 -1
  28. package/dist-server/controllers/profile.js.map +0 -1
  29. package/dist-server/controllers/reset-password.js.map +0 -1
  30. package/dist-server/controllers/signin.js.map +0 -1
  31. package/dist-server/controllers/signup.js.map +0 -1
  32. package/dist-server/controllers/unlock-user.js.map +0 -1
  33. package/dist-server/controllers/utils/find-verification-token.js.map +0 -1
  34. package/dist-server/controllers/utils/make-verification-token.js.map +0 -1
  35. package/dist-server/controllers/utils/password-rule.js.map +0 -1
  36. package/dist-server/controllers/utils/save-verification-token.js.map +0 -1
  37. package/dist-server/controllers/verification.js.map +0 -1
  38. package/dist-server/errors/auth-error.js.map +0 -1
  39. package/dist-server/errors/index.js.map +0 -1
  40. package/dist-server/errors/user-domain-not-match-error.js.map +0 -1
  41. package/dist-server/index.js.map +0 -1
  42. package/dist-server/middlewares/audit-scope-middleware.js.map +0 -1
  43. package/dist-server/middlewares/authenticate-401-middleware.js.map +0 -1
  44. package/dist-server/middlewares/bypass-signin-middleware.js.map +0 -1
  45. package/dist-server/middlewares/domain-authenticate-middleware.js.map +0 -1
  46. package/dist-server/middlewares/graphql-authenticate-middleware.js.map +0 -1
  47. package/dist-server/middlewares/index.js.map +0 -1
  48. package/dist-server/middlewares/jwt-authenticate-middleware.js.map +0 -1
  49. package/dist-server/middlewares/signin-middleware.js.map +0 -1
  50. package/dist-server/middlewares/verify-recaptcha-middleware.js.map +0 -1
  51. package/dist-server/migrations/1548206416130-SeedUser.js.map +0 -1
  52. package/dist-server/migrations/1566805283882-SeedPrivilege.js.map +0 -1
  53. package/dist-server/migrations/1745000000000-MigrateDomainOwnersToTable.js.map +0 -1
  54. package/dist-server/migrations/index.js.map +0 -1
  55. package/dist-server/role-tasks.js.map +0 -1
  56. package/dist-server/router/auth-checkin-router.js.map +0 -1
  57. package/dist-server/router/auth-private-process-router.js.map +0 -1
  58. package/dist-server/router/auth-public-process-router.js.map +0 -1
  59. package/dist-server/router/auth-signin-router.js.map +0 -1
  60. package/dist-server/router/auth-signup-router.js.map +0 -1
  61. package/dist-server/router/index.js.map +0 -1
  62. package/dist-server/router/oauth2/index.js.map +0 -1
  63. package/dist-server/router/oauth2/oauth2-authorize-router.js.map +0 -1
  64. package/dist-server/router/oauth2/oauth2-router.js.map +0 -1
  65. package/dist-server/router/oauth2/oauth2-server.js.map +0 -1
  66. package/dist-server/router/oauth2/passport-oauth2-client-password.js.map +0 -1
  67. package/dist-server/router/oauth2/passport-refresh-token.js.map +0 -1
  68. package/dist-server/router/path-base-domain-router.js.map +0 -1
  69. package/dist-server/router/site-root-router.js.map +0 -1
  70. package/dist-server/router/webauthn-router.js.map +0 -1
  71. package/dist-server/routes.js.map +0 -1
  72. package/dist-server/service/access-request/access-request-mutation.js.map +0 -1
  73. package/dist-server/service/access-request/access-request-query.js.map +0 -1
  74. package/dist-server/service/access-request/access-request-types.js.map +0 -1
  75. package/dist-server/service/access-request/access-request.js.map +0 -1
  76. package/dist-server/service/access-request/index.js.map +0 -1
  77. package/dist-server/service/app-binding/app-binding-mutation.js.map +0 -1
  78. package/dist-server/service/app-binding/app-binding-query.js.map +0 -1
  79. package/dist-server/service/app-binding/app-binding-types.js.map +0 -1
  80. package/dist-server/service/app-binding/app-binding.js.map +0 -1
  81. package/dist-server/service/app-binding/index.js.map +0 -1
  82. package/dist-server/service/appliance/appliance-mutation.js.map +0 -1
  83. package/dist-server/service/appliance/appliance-query.js.map +0 -1
  84. package/dist-server/service/appliance/appliance-types.js.map +0 -1
  85. package/dist-server/service/appliance/appliance.js.map +0 -1
  86. package/dist-server/service/appliance/index.js.map +0 -1
  87. package/dist-server/service/application/application-mutation.js.map +0 -1
  88. package/dist-server/service/application/application-query.js.map +0 -1
  89. package/dist-server/service/application/application-types.js.map +0 -1
  90. package/dist-server/service/application/application.js.map +0 -1
  91. package/dist-server/service/application/index.js.map +0 -1
  92. package/dist-server/service/audit/audit-action.js.map +0 -1
  93. package/dist-server/service/audit/audit-kinds.js.map +0 -1
  94. package/dist-server/service/audit/audit-query.js.map +0 -1
  95. package/dist-server/service/audit/audit-recorder.js.map +0 -1
  96. package/dist-server/service/audit/audit-request.js.map +0 -1
  97. package/dist-server/service/audit/audit-retention.js.map +0 -1
  98. package/dist-server/service/audit/audit-subscriber.js.map +0 -1
  99. package/dist-server/service/audit/audit-target.js.map +0 -1
  100. package/dist-server/service/audit/audit-types.js.map +0 -1
  101. package/dist-server/service/audit/audit-work.js.map +0 -1
  102. package/dist-server/service/audit/index.js.map +0 -1
  103. package/dist-server/service/auth-provider/auth-provider-mutation.js.map +0 -1
  104. package/dist-server/service/auth-provider/auth-provider-parameter-spec.js.map +0 -1
  105. package/dist-server/service/auth-provider/auth-provider-query.js.map +0 -1
  106. package/dist-server/service/auth-provider/auth-provider-type.js.map +0 -1
  107. package/dist-server/service/auth-provider/auth-provider.js.map +0 -1
  108. package/dist-server/service/auth-provider/index.js.map +0 -1
  109. package/dist-server/service/domain-generator/domain-generator-mutation.js.map +0 -1
  110. package/dist-server/service/domain-generator/domain-generator-types.js.map +0 -1
  111. package/dist-server/service/domain-generator/index.js.map +0 -1
  112. package/dist-server/service/domain-link/domain-link-mutation.js.map +0 -1
  113. package/dist-server/service/domain-link/domain-link-query.js.map +0 -1
  114. package/dist-server/service/domain-link/domain-link-types.js.map +0 -1
  115. package/dist-server/service/domain-link/domain-link.js.map +0 -1
  116. package/dist-server/service/domain-link/index.js.map +0 -1
  117. package/dist-server/service/domain-owner/domain-owner-mutation.js.map +0 -1
  118. package/dist-server/service/domain-owner/domain-owner-query.js.map +0 -1
  119. package/dist-server/service/domain-owner/domain-owner.js.map +0 -1
  120. package/dist-server/service/domain-owner/index.js.map +0 -1
  121. package/dist-server/service/granted-role/granted-role-mutation.js.map +0 -1
  122. package/dist-server/service/granted-role/granted-role-query.js.map +0 -1
  123. package/dist-server/service/granted-role/granted-role.js.map +0 -1
  124. package/dist-server/service/granted-role/index.js.map +0 -1
  125. package/dist-server/service/identity-confirmation/identity-confirmation.js.map +0 -1
  126. package/dist-server/service/index.js.map +0 -1
  127. package/dist-server/service/open-fields.js.map +0 -1
  128. package/dist-server/service/partner/index.js.map +0 -1
  129. package/dist-server/service/partner/partner-mutation.js.map +0 -1
  130. package/dist-server/service/partner/partner-query.js.map +0 -1
  131. package/dist-server/service/partner/partner-types.js.map +0 -1
  132. package/dist-server/service/partner/partner.js.map +0 -1
  133. package/dist-server/service/password-history/index.js.map +0 -1
  134. package/dist-server/service/password-history/password-history.js.map +0 -1
  135. package/dist-server/service/privilege/index.js.map +0 -1
  136. package/dist-server/service/privilege/open-field-rule.js.map +0 -1
  137. package/dist-server/service/privilege/privilege-axis.js.map +0 -1
  138. package/dist-server/service/privilege/privilege-deprecation.js.map +0 -1
  139. package/dist-server/service/privilege/privilege-directive.js.map +0 -1
  140. package/dist-server/service/privilege/privilege-mutation.js.map +0 -1
  141. package/dist-server/service/privilege/privilege-query.js.map +0 -1
  142. package/dist-server/service/privilege/privilege-rejection.js.map +0 -1
  143. package/dist-server/service/privilege/privilege-sync.js.map +0 -1
  144. package/dist-server/service/privilege/privilege-types.js.map +0 -1
  145. package/dist-server/service/privilege/privilege.js.map +0 -1
  146. package/dist-server/service/role/index.js.map +0 -1
  147. package/dist-server/service/role/role-mutation.js.map +0 -1
  148. package/dist-server/service/role/role-privilege-query.js.map +0 -1
  149. package/dist-server/service/role/role-query.js.map +0 -1
  150. package/dist-server/service/role/role-types.js.map +0 -1
  151. package/dist-server/service/role/role.js.map +0 -1
  152. package/dist-server/service/role-template/index.js.map +0 -1
  153. package/dist-server/service/role-template/role-task.js.map +0 -1
  154. package/dist-server/service/role-template/role-template-gap-resolver.js.map +0 -1
  155. package/dist-server/service/role-template/role-template-mutation.js.map +0 -1
  156. package/dist-server/service/role-template/role-template-query.js.map +0 -1
  157. package/dist-server/service/role-template/role-template-types.js.map +0 -1
  158. package/dist-server/service/role-template/role-template.js.map +0 -1
  159. package/dist-server/service/user/domain-query.js.map +0 -1
  160. package/dist-server/service/user/index.js.map +0 -1
  161. package/dist-server/service/user/user-mutation.js.map +0 -1
  162. package/dist-server/service/user/user-query.js.map +0 -1
  163. package/dist-server/service/user/user-types.js.map +0 -1
  164. package/dist-server/service/user/user.js.map +0 -1
  165. package/dist-server/service/users-auth-providers/index.js.map +0 -1
  166. package/dist-server/service/users-auth-providers/users-auth-providers.js.map +0 -1
  167. package/dist-server/service/verification-token/index.js.map +0 -1
  168. package/dist-server/service/verification-token/verification-token.js.map +0 -1
  169. package/dist-server/service/web-auth-credential/index.js.map +0 -1
  170. package/dist-server/service/web-auth-credential/web-auth-credential-mutation.js.map +0 -1
  171. package/dist-server/service/web-auth-credential/web-auth-credential-query.js.map +0 -1
  172. package/dist-server/service/web-auth-credential/web-auth-credential.js.map +0 -1
  173. package/dist-server/tsconfig.tsbuildinfo +0 -1
  174. package/dist-server/types.js.map +0 -1
  175. package/dist-server/utils/accepts.js.map +0 -1
  176. package/dist-server/utils/access-request-review.js.map +0 -1
  177. package/dist-server/utils/access-token-cookie.js.map +0 -1
  178. package/dist-server/utils/account-lock-rule.js.map +0 -1
  179. package/dist-server/utils/assignable-roles.js.map +0 -1
  180. package/dist-server/utils/build-auth-url.js.map +0 -1
  181. package/dist-server/utils/can-user-checkin-domain.js.map +0 -1
  182. package/dist-server/utils/check-permission.js.map +0 -1
  183. package/dist-server/utils/check-user-belongs-domain.js.map +0 -1
  184. package/dist-server/utils/check-user-has-role.js.map +0 -1
  185. package/dist-server/utils/checkin-domain-facts.js.map +0 -1
  186. package/dist-server/utils/checkin-domain-rule.js.map +0 -1
  187. package/dist-server/utils/credential-at-rest.js.map +0 -1
  188. package/dist-server/utils/credential-features.js.map +0 -1
  189. package/dist-server/utils/credential-serial-rule.js.map +0 -1
  190. package/dist-server/utils/domain-ownership.js.map +0 -1
  191. package/dist-server/utils/encrypt-state.js.map +0 -1
  192. package/dist-server/utils/get-aes-256-key.js.map +0 -1
  193. package/dist-server/utils/get-domain-users.js.map +0 -1
  194. package/dist-server/utils/get-secret.js.map +0 -1
  195. package/dist-server/utils/get-user-domains.js.map +0 -1
  196. package/dist-server/utils/lock-duration.js.map +0 -1
  197. package/dist-server/utils/password-reset-rule.js.map +0 -1
  198. package/dist-server/utils/profile-user.js.map +0 -1
  199. package/dist-server/utils/refuse-role-name.js.map +0 -1
  200. package/dist-server/utils/role-name-standing.js.map +0 -1
  201. package/dist-server/utils/same-origin-redirect.js.map +0 -1
  202. package/dist-server/utils/verification-token-rule.js.map +0 -1
  203. package/spec/unit/checkin-domain.spec.ts +0 -307
  204. package/spec/unit/checkin-privilege.spec.ts +0 -205
  205. package/spec/unit/domain-owner-entity.spec.ts +0 -179
  206. package/spec/unit/domain-owner-granted.spec.ts +0 -215
  207. package/spec/unit/domain-owner-migration.spec.ts +0 -236
  208. package/spec/unit/domain-owner-mutation.spec.ts +0 -337
  209. package/spec/unit/domain-ownership.spec.ts +0 -188
  210. package/tests/access-assistant-db.test.ts +0 -155
  211. package/tests/access-entry-targets-db.test.ts +0 -176
  212. package/tests/access-request-db.test.ts +0 -394
  213. package/tests/access-request-pg.test.ts +0 -83
  214. package/tests/access-request-review.test.ts +0 -193
  215. package/tests/account-lock-db.test.ts +0 -216
  216. package/tests/account-lock-rule.test.ts +0 -141
  217. package/tests/account-mail.test.ts +0 -69
  218. package/tests/activate-page-case.test.ts +0 -53
  219. package/tests/add-user-gates.test.ts +0 -34
  220. package/tests/app-binding-db.test.ts +0 -210
  221. package/tests/app-binding-delete-db.test.ts +0 -188
  222. package/tests/appliance-credential-state-db.test.ts +0 -207
  223. package/tests/appliance-delete-db.test.ts +0 -119
  224. package/tests/appliance-revoke-db.test.ts +0 -294
  225. package/tests/appliance-schema.test.ts +0 -129
  226. package/tests/application-delete-db.test.ts +0 -190
  227. package/tests/application-token-payload-db.test.ts +0 -185
  228. package/tests/assignable-roles-db.test.ts +0 -219
  229. package/tests/audit-db.test.ts +0 -410
  230. package/tests/audit-work.test.ts +0 -58
  231. package/tests/auth-base-role-tasks.test.ts +0 -58
  232. package/tests/checkin-domain-rule.test.ts +0 -128
  233. package/tests/checkin-privilege-seam-db.test.ts +0 -158
  234. package/tests/compiled.ts +0 -41
  235. package/tests/confirm-identity-db.test.ts +0 -85
  236. package/tests/credential-at-rest-db.test.ts +0 -246
  237. package/tests/credential-features-off-db.test.ts +0 -189
  238. package/tests/credential-serial-rule.test.ts +0 -84
  239. package/tests/declare-privileges.test.ts +0 -29
  240. package/tests/domain-assignees-db.test.ts +0 -95
  241. package/tests/domain-inheritance-sentinel.test.ts +0 -93
  242. package/tests/domain-required.test.ts +0 -127
  243. package/tests/has-privilege-db.test.ts +0 -198
  244. package/tests/identity-confirmation.test.ts +0 -146
  245. package/tests/list-inheritance-db.test.ts +0 -127
  246. package/tests/lock-recovery.test.ts +0 -206
  247. package/tests/mail-request-answers-db.test.ts +0 -142
  248. package/tests/open-doors.test.ts +0 -121
  249. package/tests/open-fields-enforcement.test.ts +0 -215
  250. package/tests/open-fields-framework.test.ts +0 -212
  251. package/tests/open-redirect-db.test.ts +0 -143
  252. package/tests/passkey-authenticator.ts +0 -104
  253. package/tests/passkey-db.test.ts +0 -295
  254. package/tests/passkey-signin-route-db.test.ts +0 -139
  255. package/tests/password-hashing.test.ts +0 -131
  256. package/tests/password-persistence.test.ts +0 -147
  257. package/tests/password-reset-rule-db.test.ts +0 -159
  258. package/tests/password-setup-db.test.ts +0 -215
  259. package/tests/permission-gate.test.ts +0 -228
  260. package/tests/picture-reducer.test.ts +0 -25
  261. package/tests/privilege-axis.test.ts +0 -157
  262. package/tests/privilege-deprecation.test.ts +0 -65
  263. package/tests/privilege-directive.test.ts +0 -332
  264. package/tests/privilege-rejection.test.ts +0 -104
  265. package/tests/privilege-sync-db.test.ts +0 -58
  266. package/tests/profile-user.test.ts +0 -24
  267. package/tests/refusal-cause.test.ts +0 -80
  268. package/tests/refusals-have-korean.test.ts +0 -24
  269. package/tests/request-holds.test.ts +0 -53
  270. package/tests/role-inheritance-db.test.ts +0 -286
  271. package/tests/role-mutation-db.test.ts +0 -293
  272. package/tests/role-name-lookup-sentinel.test.ts +0 -79
  273. package/tests/role-name-standing.test.ts +0 -83
  274. package/tests/role-privileges-db.test.ts +0 -252
  275. package/tests/role-task.test.ts +0 -168
  276. package/tests/role-template-gap-db.test.ts +0 -268
  277. package/tests/role-template-seed-db.test.ts +0 -180
  278. package/tests/role-template.test.ts +0 -162
  279. package/tests/role-users-gate.test.ts +0 -22
  280. package/tests/signin-policy.test.ts +0 -200
  281. package/tests/signup-duplicate-db.test.ts +0 -68
  282. package/tests/token-issuance.test.ts +0 -244
  283. package/tests/user-roles-field-db.test.ts +0 -134
  284. package/tests/verification-token-db.test.ts +0 -285
@@ -1,337 +0,0 @@
1
- /**
2
- * DomainOwner Mutation 로직 테스트
3
- *
4
- * addDomainOwner / removeDomainOwner mutation 의 핵심 비즈니스 로직
5
- * (멤버 확인, 중복 방지, 마지막 오너 보호, legacy cache 동기화)을
6
- * 테스트 엔티티 스키마로 재현해 검증한다.
7
- */
8
-
9
- import { EntityManager } from 'typeorm'
10
- import { TestDatabase } from '../../../../test/test-database'
11
- import {
12
- domainFactory,
13
- userFactory,
14
- domainOwnerFactory
15
- } from '../../../../test/factories'
16
- import { Domain, DomainOwner, User } from '../../../../test/entities/schemas'
17
-
18
- // ============================================================
19
- // 테스트용 헬퍼: source 로직을 테스트 스키마로 재현
20
- // ============================================================
21
-
22
- /**
23
- * addDomainOwner 의 핵심 로직
24
- * - 대상 유저가 domain 멤버인지 확인
25
- * - 이미 owner인지 확인
26
- * - 엔트리 생성
27
- * - Domain.owner 캐시가 비어있으면 세팅
28
- */
29
- async function addDomainOwnerLogic(
30
- manager: EntityManager,
31
- domain: Domain,
32
- targetUser: User,
33
- actor: User,
34
- options: { isMember: boolean; reason?: string }
35
- ): Promise<DomainOwner> {
36
- if (!options.isMember) {
37
- throw new Error(`User '${targetUser.name}' is not a member of this domain.`)
38
- }
39
-
40
- const ownerRepo = manager.getRepository<DomainOwner>('DomainOwner')
41
-
42
- const existing = await ownerRepo.findOne({
43
- where: { domain: { id: domain.id }, user: { id: targetUser.id } }
44
- })
45
- if (existing) {
46
- throw new Error(`User '${targetUser.name}' is already an owner of this domain.`)
47
- }
48
-
49
- const entry = ownerRepo.create({
50
- domain: { id: domain.id } as Domain,
51
- user: { id: targetUser.id } as User,
52
- grantedBy: actor,
53
- reason: options.reason
54
- } as Partial<DomainOwner>)
55
- const saved = await ownerRepo.save(entry)
56
-
57
- if (!domain.owner) {
58
- await manager.update('Domain', domain.id, { owner: targetUser.id })
59
- }
60
-
61
- return saved
62
- }
63
-
64
- /**
65
- * removeDomainOwner 의 핵심 로직
66
- * - 대상 유저의 엔트리 혹은 legacy owner 여부 확인
67
- * - 남을 owner가 1명 이상 보장
68
- * - legacy cache가 제거 대상이면 남은 오너 중 하나로 재설정
69
- */
70
- async function removeDomainOwnerLogic(
71
- manager: EntityManager,
72
- domain: Domain,
73
- targetUser: User
74
- ): Promise<boolean> {
75
- const ownerRepo = manager.getRepository<DomainOwner>('DomainOwner')
76
-
77
- const entry = await ownerRepo.findOne({
78
- where: { domain: { id: domain.id }, user: { id: targetUser.id } }
79
- })
80
- const hasLegacyOwner = domain.owner === targetUser.id
81
-
82
- if (!entry && !hasLegacyOwner) {
83
- throw new Error(`User '${targetUser.name}' is not an owner of this domain.`)
84
- }
85
-
86
- const ownerCount = await ownerRepo.count({
87
- where: { domain: { id: domain.id } }
88
- })
89
-
90
- let remaining = ownerCount
91
- if (entry) remaining -= 1
92
- if (domain.owner && domain.owner !== targetUser.id) {
93
- const legacyInTable = await ownerRepo.findOne({
94
- where: { domain: { id: domain.id }, user: { id: domain.owner } }
95
- })
96
- if (!legacyInTable) remaining += 1
97
- }
98
-
99
- if (remaining < 1) {
100
- throw new Error('Cannot remove the last owner of a domain. Add another owner first.')
101
- }
102
-
103
- if (entry) {
104
- await ownerRepo.delete(entry.id)
105
- }
106
-
107
- if (hasLegacyOwner) {
108
- const next = await ownerRepo.findOne({
109
- where: { domain: { id: domain.id } },
110
- order: { grantedAt: 'ASC' }
111
- })
112
- const nextOwnerId = next
113
- ? (await manager.findOne<DomainOwner>('DomainOwner', {
114
- where: { id: next.id },
115
- relations: ['user']
116
- }))?.user.id ?? null
117
- : null
118
- await manager.update('Domain', domain.id, { owner: nextOwnerId })
119
- }
120
-
121
- return true
122
- }
123
-
124
- // ============================================================
125
- // 테스트
126
- // ============================================================
127
-
128
- describe('addDomainOwner 로직', () => {
129
- let testDb: TestDatabase
130
-
131
- beforeAll(() => {
132
- testDb = TestDatabase.getInstance()
133
- })
134
-
135
- it('도메인 멤버를 owner로 추가할 수 있다', async () => {
136
- const ds = testDb.getDataSource()
137
- const domain = await domainFactory.create({})
138
- const actor = await userFactory.create({ name: 'actor' })
139
- const target = await userFactory.create({ name: 'target' })
140
-
141
- const entry = await addDomainOwnerLogic(ds.manager, domain, target, actor, {
142
- isMember: true
143
- })
144
-
145
- expect(entry.id).toBeDefined()
146
-
147
- const reloaded = await ds.manager.findOne<DomainOwner>('DomainOwner', {
148
- where: { id: entry.id },
149
- relations: ['user', 'grantedBy']
150
- })
151
- expect(reloaded?.user.id).toBe(target.id)
152
- expect(reloaded?.grantedBy?.id).toBe(actor.id)
153
- })
154
-
155
- it('Domain.owner 캐시가 비어있으면 추가된 user로 세팅한다', async () => {
156
- const ds = testDb.getDataSource()
157
- const domain = await domainFactory.create({ owner: null as any })
158
- const actor = await userFactory.create({ name: 'actor' })
159
- const target = await userFactory.create({ name: 'target' })
160
-
161
- await addDomainOwnerLogic(ds.manager, domain, target, actor, {
162
- isMember: true
163
- })
164
-
165
- const reloadedDomain = await ds.manager.findOne<Domain>('Domain', {
166
- where: { id: domain.id }
167
- })
168
- expect(reloadedDomain?.owner).toBe(target.id)
169
- })
170
-
171
- it('Domain.owner 캐시가 이미 있으면 덮어쓰지 않는다', async () => {
172
- const ds = testDb.getDataSource()
173
- const existingOwner = await userFactory.create({ name: 'existing' })
174
- const domain = await domainFactory.create({ owner: existingOwner.id })
175
- const actor = await userFactory.create({ name: 'actor' })
176
- const target = await userFactory.create({ name: 'target' })
177
-
178
- await addDomainOwnerLogic(ds.manager, domain, target, actor, {
179
- isMember: true
180
- })
181
-
182
- const reloadedDomain = await ds.manager.findOne<Domain>('Domain', {
183
- where: { id: domain.id }
184
- })
185
- expect(reloadedDomain?.owner).toBe(existingOwner.id)
186
- })
187
-
188
- it('도메인 비멤버는 owner로 추가할 수 없다', async () => {
189
- const ds = testDb.getDataSource()
190
- const domain = await domainFactory.create({})
191
- const actor = await userFactory.create({ name: 'actor' })
192
- const outsider = await userFactory.create({ name: 'outsider' })
193
-
194
- await expect(
195
- addDomainOwnerLogic(ds.manager, domain, outsider, actor, { isMember: false })
196
- ).rejects.toThrow(/not a member/)
197
- })
198
-
199
- it('이미 owner인 user를 중복 추가할 수 없다', async () => {
200
- const ds = testDb.getDataSource()
201
- const domain = await domainFactory.create({})
202
- const actor = await userFactory.create({ name: 'actor' })
203
- const target = await userFactory.create({ name: 'target' })
204
-
205
- await addDomainOwnerLogic(ds.manager, domain, target, actor, { isMember: true })
206
-
207
- await expect(
208
- addDomainOwnerLogic(ds.manager, domain, target, actor, { isMember: true })
209
- ).rejects.toThrow(/already an owner/)
210
- })
211
- })
212
-
213
- describe('removeDomainOwner 로직', () => {
214
- let testDb: TestDatabase
215
-
216
- beforeAll(() => {
217
- testDb = TestDatabase.getInstance()
218
- })
219
-
220
- it('여러 owner 중 한 명을 제거할 수 있다', async () => {
221
- const ds = testDb.getDataSource()
222
- const domain = await domainFactory.create({})
223
- const u1 = await userFactory.create({ name: 'U1' })
224
- const u2 = await userFactory.create({ name: 'U2' })
225
- await domainOwnerFactory.grant(domain, u1)
226
- await domainOwnerFactory.grant(domain, u2)
227
-
228
- const result = await removeDomainOwnerLogic(ds.manager, domain, u2)
229
- expect(result).toBe(true)
230
-
231
- const remaining = await ds.manager.count('DomainOwner', {
232
- where: { domain: { id: domain.id } }
233
- })
234
- expect(remaining).toBe(1)
235
- })
236
-
237
- it('마지막 남은 owner는 제거할 수 없다 (최소 1명 유지)', async () => {
238
- const ds = testDb.getDataSource()
239
- const domain = await domainFactory.create({})
240
- const soleOwner = await userFactory.create({ name: 'sole' })
241
- await domainOwnerFactory.grant(domain, soleOwner)
242
-
243
- await expect(
244
- removeDomainOwnerLogic(ds.manager, domain, soleOwner)
245
- ).rejects.toThrow(/last owner/)
246
- })
247
-
248
- it('legacy cache 만 있고 join 테이블에 없어도 "owner"로 인정되어 제거 거부 (마지막 1인)', async () => {
249
- const ds = testDb.getDataSource()
250
- const legacyOwner = await userFactory.create({ name: 'legacy' })
251
- const domain = await domainFactory.create({ owner: legacyOwner.id })
252
- // join table 에 엔트리 없음
253
-
254
- await expect(
255
- removeDomainOwnerLogic(ds.manager, domain, legacyOwner)
256
- ).rejects.toThrow(/last owner/)
257
- })
258
-
259
- it('legacy cache + 다른 오너가 join 테이블에 있으면 legacy 제거 가능', async () => {
260
- const ds = testDb.getDataSource()
261
- const legacyOwner = await userFactory.create({ name: 'legacy' })
262
- const newOwner = await userFactory.create({ name: 'new' })
263
- const domain = await domainFactory.create({ owner: legacyOwner.id })
264
- await domainOwnerFactory.grant(domain, newOwner)
265
-
266
- await removeDomainOwnerLogic(ds.manager, domain, legacyOwner)
267
-
268
- const reloadedDomain = await ds.manager.findOne<Domain>('Domain', {
269
- where: { id: domain.id }
270
- })
271
- // legacy cache 는 남은 오너(newOwner)로 재설정되어야 함
272
- expect(reloadedDomain?.owner).toBe(newOwner.id)
273
- })
274
-
275
- it('legacy cache 대상 제거 시 남은 DomainOwner 중 한 명으로 cache 재설정', async () => {
276
- const ds = testDb.getDataSource()
277
- const u1 = await userFactory.create({ name: 'U1' })
278
- const u2 = await userFactory.create({ name: 'U2' })
279
- const u3 = await userFactory.create({ name: 'U3' })
280
- const domain = await domainFactory.create({ owner: u1.id })
281
- await domainOwnerFactory.grant(domain, u1)
282
- await domainOwnerFactory.grant(domain, u2)
283
- await domainOwnerFactory.grant(domain, u3)
284
-
285
- await removeDomainOwnerLogic(ds.manager, domain, u1)
286
-
287
- const reloadedDomain = await ds.manager.findOne<Domain>('Domain', {
288
- where: { id: domain.id }
289
- })
290
- // SQLite datetime 은 초 단위 해상도라 grantedAt 이 동일할 수 있음 → 순서 비결정적.
291
- // 핵심 불변조건: cache 는 u1 이 아니고, 남은 오너 (u2 또는 u3) 중 하나로 갱신됨.
292
- expect(reloadedDomain?.owner).not.toBe(u1.id)
293
- expect([u2.id, u3.id]).toContain(reloadedDomain?.owner)
294
- })
295
-
296
- it('owner가 아닌 user는 제거할 수 없다', async () => {
297
- const ds = testDb.getDataSource()
298
- const domain = await domainFactory.create({})
299
- const owner = await userFactory.create({ name: 'owner' })
300
- const other = await userFactory.create({ name: 'other' })
301
- await domainOwnerFactory.grant(domain, owner)
302
-
303
- await expect(
304
- removeDomainOwnerLogic(ds.manager, domain, other)
305
- ).rejects.toThrow(/not an owner/)
306
- })
307
-
308
- it('legacy cache 만 있는 마지막 오너 + 새 오너 추가 후에는 legacy 제거 가능', async () => {
309
- const ds = testDb.getDataSource()
310
- const legacy = await userFactory.create({ name: 'legacy' })
311
- const domain = await domainFactory.create({ owner: legacy.id })
312
- // 이 시점에서 legacy는 cache로만 유일한 owner → 제거 불가
313
- await expect(
314
- removeDomainOwnerLogic(ds.manager, domain, legacy)
315
- ).rejects.toThrow(/last owner/)
316
-
317
- // 새 오너를 추가하면
318
- const newOwner = await userFactory.create({ name: 'new' })
319
- const actor = await userFactory.create({ name: 'actor' })
320
- await addDomainOwnerLogic(ds.manager, domain, newOwner, actor, { isMember: true })
321
-
322
- // legacy를 제거할 수 있음 (remaining = legacy 1 + new 1 - 1 = 1)
323
- // NOTE: addDomainOwnerLogic은 domain.owner가 이미 legacy.id 이므로 덮어쓰지 않음
324
- // 따라서 domain 은 여전히 legacy.id 가 cache
325
- const reloadedDomain = await ds.manager.findOne<Domain>('Domain', {
326
- where: { id: domain.id }
327
- })
328
- expect(reloadedDomain!.owner).toBe(legacy.id)
329
-
330
- await removeDomainOwnerLogic(ds.manager, reloadedDomain!, legacy)
331
-
332
- const finalDomain = await ds.manager.findOne<Domain>('Domain', {
333
- where: { id: domain.id }
334
- })
335
- expect(finalDomain?.owner).toBe(newOwner.id)
336
- })
337
- })
@@ -1,188 +0,0 @@
1
- /**
2
- * 도메인 소유권 판정 — 실제 엔티티·실제 쿼리 검증.
3
- *
4
- * 도메인 관리자는 여러 명일 수 있다(`DomainOwner` 테이블). `Domain.owner` 컬럼은 대표 오너
5
- * 표시용 캐시이므로, 도메인 범위의 판정은 두 경로를 함께 봐야 한다.
6
- *
7
- * 주의: 이 스펙은 빌드 산출물(`dist-server`)을 대상으로 한다.
8
- */
9
-
10
- import { DataSource } from 'typeorm'
11
-
12
- import { NamingStrategy, addDataSource, entities as shellEntities, Domain } from '@things-factory/shell'
13
- import {
14
- DomainOwner,
15
- User,
16
- entities as authEntities,
17
- findDomainOwnerUserIds,
18
- isDomainOwner,
19
- setPrimaryDomainOwner
20
- } from '@things-factory/auth-base'
21
-
22
- let dataSource: DataSource
23
-
24
- async function domain(name: string): Promise<Domain> {
25
- return await dataSource.getRepository(Domain).save({ name, subdomain: name } as any)
26
- }
27
-
28
- async function usr(name: string): Promise<User> {
29
- return await dataSource.getRepository(User).save({
30
- name,
31
- email: `${name}@test.com`,
32
- username: name
33
- } as any)
34
- }
35
-
36
- async function reload(target: Domain): Promise<Domain> {
37
- return await dataSource.getRepository(Domain).findOneBy({ id: target.id })
38
- }
39
-
40
- async function ownerEntryCount(target: Domain): Promise<number> {
41
- return await dataSource.getRepository(DomainOwner).count({ where: { domain: { id: target.id } } })
42
- }
43
-
44
- beforeAll(async () => {
45
- dataSource = new DataSource({
46
- type: 'sqlite',
47
- database: ':memory:',
48
- synchronize: true,
49
- dropSchema: true,
50
- namingStrategy: new NamingStrategy(),
51
- entities: [...shellEntities, ...authEntities],
52
- logging: false
53
- })
54
-
55
- await dataSource.initialize()
56
- addDataSource('default', dataSource)
57
- })
58
-
59
- afterAll(async () => {
60
- await dataSource?.destroy()
61
- })
62
-
63
- beforeEach(async () => {
64
- for (const junction of dataSource.entityMetadatas.filter(metadata => metadata.isJunction)) {
65
- await dataSource.query(`DELETE FROM "${junction.tableName}"`)
66
- }
67
-
68
- for (const target of [DomainOwner, User, Domain]) {
69
- await dataSource.query(`DELETE FROM "${dataSource.getMetadata(target).tableName}"`)
70
- }
71
- })
72
-
73
- describe('isDomainOwner', () => {
74
- it('대표 오너 컬럼으로 인정한다', async () => {
75
- const target = await domain('acme')
76
- const owner = await usr('boss')
77
- await dataSource.getRepository(Domain).update(target.id, { owner: owner.id })
78
-
79
- expect(await isDomainOwner(await reload(target), owner)).toBe(true)
80
- })
81
-
82
- it('DomainOwner 테이블의 공동 오너도 인정한다', async () => {
83
- const target = await domain('acme')
84
- const primary = await usr('boss')
85
- const coOwner = await usr('deputy')
86
- await dataSource.getRepository(Domain).update(target.id, { owner: primary.id })
87
- await dataSource.getRepository(DomainOwner).save({ domain: target, user: coOwner } as any)
88
-
89
- expect(await isDomainOwner(await reload(target), coOwner)).toBe(true)
90
- })
91
-
92
- it('오너가 아닌 사용자는 인정하지 않는다', async () => {
93
- const target = await domain('acme')
94
- const owner = await usr('boss')
95
- const other = await usr('lee')
96
- await dataSource.getRepository(Domain).update(target.id, { owner: owner.id })
97
-
98
- expect(await isDomainOwner(await reload(target), other)).toBe(false)
99
- })
100
-
101
- it('다른 도메인의 오너 엔트리는 인정하지 않는다', async () => {
102
- const target = await domain('acme')
103
- const another = await domain('other')
104
- const coOwner = await usr('deputy')
105
- await dataSource.getRepository(DomainOwner).save({ domain: another, user: coOwner } as any)
106
-
107
- expect(await isDomainOwner(await reload(target), coOwner)).toBe(false)
108
- })
109
-
110
- it('도메인이나 사용자가 없으면 인정하지 않는다', async () => {
111
- const target = await domain('acme')
112
- const owner = await usr('boss')
113
-
114
- expect(await isDomainOwner(null, owner)).toBe(false)
115
- expect(await isDomainOwner(await reload(target), null)).toBe(false)
116
- })
117
- })
118
-
119
- describe('findDomainOwnerUserIds', () => {
120
- it('대표 오너와 공동 오너를 합쳐서 반환한다', async () => {
121
- const target = await domain('acme')
122
- const primary = await usr('boss')
123
- const coOwner = await usr('deputy')
124
- const plain = await usr('lee')
125
- await dataSource.getRepository(Domain).update(target.id, { owner: primary.id })
126
- await dataSource.getRepository(DomainOwner).save({ domain: target, user: coOwner } as any)
127
-
128
- const ownerIds = await findDomainOwnerUserIds(await reload(target))
129
-
130
- expect(ownerIds.has(primary.id)).toBe(true)
131
- expect(ownerIds.has(coOwner.id)).toBe(true)
132
- expect(ownerIds.has(plain.id)).toBe(false)
133
- expect(ownerIds.size).toBe(2)
134
- })
135
-
136
- it('대표 오너가 테이블에도 있으면 중복 계산하지 않는다', async () => {
137
- const target = await domain('acme')
138
- const primary = await usr('boss')
139
- await dataSource.getRepository(Domain).update(target.id, { owner: primary.id })
140
- await dataSource.getRepository(DomainOwner).save({ domain: target, user: primary } as any)
141
-
142
- expect((await findDomainOwnerUserIds(await reload(target))).size).toBe(1)
143
- })
144
- })
145
-
146
- describe('setPrimaryDomainOwner', () => {
147
- it('컬럼과 테이블을 함께 세팅한다', async () => {
148
- const target = await domain('acme')
149
- const owner = await usr('boss')
150
-
151
- await setPrimaryDomainOwner(target, owner.id)
152
-
153
- expect((await reload(target)).owner).toBe(owner.id)
154
- expect(await ownerEntryCount(target)).toBe(1)
155
- expect(await isDomainOwner(await reload(target), owner)).toBe(true)
156
- })
157
-
158
- it('기존 공동 오너 엔트리를 제거하지 않는다', async () => {
159
- const target = await domain('acme')
160
- const previous = await usr('boss')
161
- const next = await usr('deputy')
162
- await setPrimaryDomainOwner(target, previous.id)
163
-
164
- await setPrimaryDomainOwner(target, next.id)
165
-
166
- expect((await reload(target)).owner).toBe(next.id)
167
- expect(await ownerEntryCount(target)).toBe(2)
168
- /* 이관 후에도 이전 오너는 공동 오너로 남는다 — 제거는 removeDomainOwner 의 책임. */
169
- expect(await isDomainOwner(await reload(target), previous)).toBe(true)
170
- })
171
-
172
- it('같은 오너를 다시 지정해도 엔트리를 중복 생성하지 않는다', async () => {
173
- const target = await domain('acme')
174
- const owner = await usr('boss')
175
-
176
- await setPrimaryDomainOwner(target, owner.id)
177
- await setPrimaryDomainOwner(target, owner.id)
178
-
179
- expect(await ownerEntryCount(target)).toBe(1)
180
- })
181
-
182
- it('도메인이나 사용자가 없으면 명시적으로 실패한다', async () => {
183
- const target = await domain('acme')
184
-
185
- await expect(setPrimaryDomainOwner(target, '')).rejects.toThrow()
186
- await expect(setPrimaryDomainOwner(null as any, 'some-user')).rejects.toThrow()
187
- })
188
- })
@@ -1,155 +0,0 @@
1
- /**
2
- * What the assistant reads to draft access decisions (ADR-0103 decision 6).
3
- *
4
- * Two facts are held here. The read tool answers only this domain, only to someone who may decide, never the
5
- * caller's own requests. And a pending request handed over as the screen's context does not reach the model's
6
- * prompt - which is why the tool exists; if that ever changes, this file says so.
7
- */
8
- import { registryAssistantChat } from '../../ai-client-base/server/service/assistant/registry-assistant'
9
- import { clearToolRegistry, registerToolCategory } from '../../ai-client-base/server/service/tool-registry/index.js'
10
-
11
- import { closeAuthDatabase, Domain, getRepository, openAuthDatabase, resetAuthDatabase } from './db'
12
- import { loadCompiled } from './compiled'
13
-
14
- import uiEn from '../../auth-ui/translations/en.json'
15
- import uiKo from '../../auth-ui/translations/ko.json'
16
-
17
- const { Role } = loadCompiled('service/role/role')
18
- const { Privilege } = loadCompiled('service/privilege/privilege')
19
- const { User } = loadCompiled('service/user/user')
20
- const { DomainOwner } = loadCompiled('service/domain-owner/domain-owner')
21
- const { AccessRequest } = loadCompiled('service/access-request/access-request')
22
- const access = loadCompiled('controllers/access-request')
23
- const assistant = loadCompiled('ai-entry/access-assistant')
24
- const targets = loadCompiled('ai-entry/access-entry-targets')
25
-
26
- let domain: any
27
- let other: any
28
- let kim: any
29
- let lee: any
30
- let park: any
31
- let owner: any
32
- let worker: any
33
- let otherAdmin: any
34
-
35
- async function makeUser(name: string, domains: any[], roles: any[] = []) {
36
- return await getRepository(User).save({
37
- username: name, email: `${name}@example.com`, name, salt: User.generateSalt(), status: 'activated', domains, roles
38
- })
39
- }
40
-
41
- const ask = (user: any, privilege = 'mutation', inDomain = domain) =>
42
- access.requestAccess({ category: 'warehouse', privilege, reason: 'needed for work' }, { state: { domain: inDomain, user, afterCommit: [] } } as any)
43
-
44
- beforeAll(async () => {
45
- await openAuthDatabase()
46
- })
47
-
48
- afterAll(async () => {
49
- await closeAuthDatabase()
50
- })
51
-
52
- beforeEach(async () => {
53
- await resetAuthDatabase()
54
- ;(process as any).domainOwnerGranted = async (d: any, u: any) =>
55
- !!(await getRepository(DomainOwner).findOne({ where: { domain: { id: d.id }, user: { id: u.id } } }))
56
- ;(process as any).superUserGranted = async () => false
57
-
58
- domain = await getRepository(Domain).findOneBy({ id: (await getRepository(Domain).save({ name: 'Plant A', subdomain: 'plant-a' })).id })
59
- other = await getRepository(Domain).findOneBy({ id: (await getRepository(Domain).save({ name: 'Other', subdomain: 'other' })).id })
60
-
61
- const mutation = await getRepository(Privilege).save({ name: 'mutation', category: 'warehouse' })
62
- await getRepository(Privilege).save({ name: 'query', category: 'warehouse' })
63
- const grant = await getRepository(Privilege).save({ name: 'mutation', category: 'user' })
64
- await getRepository(Role).save({ name: 'inbound', domain, privileges: [mutation] })
65
- const admins = await getRepository(Role).save({ name: 'admins', domain, privileges: [grant] })
66
- const otherAdmins = await getRepository(Role).save({ name: 'admins', domain: other, privileges: [grant] })
67
-
68
- kim = await makeUser('kim', [domain])
69
- lee = await makeUser('lee', [domain])
70
- park = await makeUser('park', [domain], [admins])
71
- owner = await makeUser('owner', [domain])
72
- worker = await makeUser('worker', [domain])
73
- otherAdmin = await makeUser('otheradmin', [other], [otherAdmins])
74
- await getRepository(DomainOwner).save({ domain, user: owner })
75
- })
76
-
77
- afterEach(() => {
78
- delete (process as any).domainOwnerGranted
79
- delete (process as any).superUserGranted
80
- })
81
-
82
- describe('listPendingAccessRequests', () => {
83
- it('★ a decider reads this domain\'s waiting requests with the roles that grant each', async () => {
84
- await ask(kim)
85
- const answer = await assistant.pendingRequestsToDecide({ domain, user: park })
86
- expect(answer.requests).toEqual([
87
- expect.objectContaining({ requester: 'kim', privilege: 'warehouse:mutation', task: null, roles: [{ name: 'inbound', privilegeCount: 1, holdsTask: false }] })
88
- ])
89
- })
90
-
91
- it('★ another domain\'s requests are not read - even by a decider there', async () => {
92
- await ask(kim)
93
- const there = await assistant.pendingRequestsToDecide({ domain: other, user: otherAdmin })
94
- expect(there.requests).toEqual([])
95
- })
96
-
97
- it('★ someone who may not decide is refused - nothing is read', async () => {
98
- await ask(kim)
99
- expect(await assistant.pendingRequestsToDecide({ domain, user: worker })).toEqual({ refused: 'not-a-decider' })
100
- })
101
-
102
- it('★ the caller\'s own requests are left out - they cannot decide them', async () => {
103
- await ask(kim)
104
- await ask(owner, 'query')
105
- const answer = await assistant.pendingRequestsToDecide({ domain, user: owner })
106
- expect(answer.requests.map((r: any) => r.requester)).toEqual(['kim'])
107
- })
108
-
109
- it('decided and expired requests are left out', async () => {
110
- const decided = await ask(kim)
111
- await access.decideAccessRequest({ requestId: decided.id, decision: 'DENY', reason: 'no' }, { state: { domain, user: park, afterCommit: [] } } as any)
112
- const late = await ask(lee)
113
- await getRepository(AccessRequest).update({ id: late.id }, { expiresAt: new Date(Date.now() - 1000) })
114
- expect((await assistant.pendingRequestsToDecide({ domain, user: park })).requests).toEqual([])
115
- })
116
- })
117
-
118
- describe('the declarations fit together', () => {
119
- afterEach(() => clearToolRegistry())
120
-
121
- it('the category registers, the mode names it and the decision target, the words exist', () => {
122
- const category = assistant.accessToolCategory()
123
- expect(() => registerToolCategory(category)).not.toThrow()
124
- const mode = assistant.accessAssistantMode()
125
- expect(mode.toolCategories).toEqual([category.name])
126
- expect(mode.entryTargets).toEqual([targets.AUTH_ACCESS_DECISION])
127
- expect(mode.subjects).toEqual([assistant.ACCESS_REQUESTS_SUBJECT])
128
- for (const dict of [uiEn, uiKo] as Record<string, string>[])
129
- for (const key of [mode.labelKey, mode.placeholderKey]) expect([key, typeof dict[key]]).toEqual([key, 'string'])
130
- })
131
-
132
- it('★ requests handed over as the screen\'s context do not reach the model\'s prompt - the tool is how it reads them', async () => {
133
- registerToolCategory(assistant.accessToolCategory())
134
- const seen: any[] = []
135
- const client = {
136
- chatWithTools: async (messages: any, tools: any[], options: any) => {
137
- seen.push({ messages, tools, options })
138
- return { text: 'ok', toolCalls: [], stopReason: 'end_turn' }
139
- }
140
- }
141
- await registryAssistantChat({
142
- client,
143
- messages: [{ role: 'user', content: 'sort out the access requests' }],
144
- systemPrompt: assistant.accessAssistantMode().systemPrompt,
145
- toolCategories: [assistant.AUTH_ACCESS_TOOLS],
146
- hostContext: { pendingRequests: ['MARKER-kim-warehouse-mutation'] },
147
- state: { domain, user: park },
148
- toolAccess: async () => true
149
- } as any)
150
-
151
- expect(JSON.stringify(seen[0].options)).not.toContain('MARKER-kim')
152
- expect(JSON.stringify(seen[0].messages)).not.toContain('MARKER-kim')
153
- expect(seen[0].tools.map((t: any) => t.name)).toEqual(['listPendingAccessRequests'])
154
- })
155
- })