@aurabase/aurabase-js 0.3.0 → 0.3.1

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 (258) hide show
  1. package/package.json +4 -4
  2. package/packages/ai/node_modules/.bin/esbuild +0 -19
  3. package/packages/ai/node_modules/.bin/tsc +0 -22
  4. package/packages/ai/node_modules/.bin/tsserver +0 -22
  5. package/packages/ai/node_modules/.bin/tsup +0 -22
  6. package/packages/ai/node_modules/.bin/tsup-node +0 -22
  7. package/packages/ai/node_modules/.bin/vite +0 -22
  8. package/packages/ai/node_modules/.bin/vitest +0 -22
  9. package/packages/ai/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  10. package/packages/ai/src/AiService.ts +0 -226
  11. package/packages/ai/src/index.ts +0 -18
  12. package/packages/ai/src/sse.ts +0 -141
  13. package/packages/ai/src/types.ts +0 -30
  14. package/packages/ai/tests/chat-stream.test.ts +0 -151
  15. package/packages/ai/tests/rag-namespaces.test.ts +0 -54
  16. package/packages/ai/tests/sse.test.ts +0 -69
  17. package/packages/ai/tsconfig.json +0 -5
  18. package/packages/ai/tsup.config.ts +0 -11
  19. package/packages/aurabase/node_modules/.bin/esbuild +0 -19
  20. package/packages/aurabase/node_modules/.bin/tsc +0 -22
  21. package/packages/aurabase/node_modules/.bin/tsserver +0 -22
  22. package/packages/aurabase/node_modules/.bin/tsup +0 -22
  23. package/packages/aurabase/node_modules/.bin/tsup-node +0 -22
  24. package/packages/aurabase/node_modules/.bin/vite +0 -22
  25. package/packages/aurabase/node_modules/.bin/vitest +0 -22
  26. package/packages/aurabase/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  27. package/packages/aurabase/src/AuraClient.ts +0 -237
  28. package/packages/aurabase/src/index.ts +0 -152
  29. package/packages/aurabase/src/ssr.ts +0 -156
  30. package/packages/aurabase/tests/ai-streaming-rag-e2e.test.ts +0 -82
  31. package/packages/aurabase/tests/ai.test.ts +0 -111
  32. package/packages/aurabase/tests/auth-admin.test.ts +0 -515
  33. package/packages/aurabase/tests/auth-e2e.test.ts +0 -135
  34. package/packages/aurabase/tests/auth-hardening.test.ts +0 -206
  35. package/packages/aurabase/tests/auth.test.ts +0 -947
  36. package/packages/aurabase/tests/database.test.ts +0 -230
  37. package/packages/aurabase/tests/db-types-e2e.test.ts +0 -217
  38. package/packages/aurabase/tests/dispose.test.ts +0 -23
  39. package/packages/aurabase/tests/foreign-token.e2e.test.ts +0 -129
  40. package/packages/aurabase/tests/functions.test.ts +0 -107
  41. package/packages/aurabase/tests/integration.test.ts +0 -413
  42. package/packages/aurabase/tests/live-production-validation.js +0 -241
  43. package/packages/aurabase/tests/live-sdk-production-validation.js +0 -293
  44. package/packages/aurabase/tests/mocks.ts +0 -139
  45. package/packages/aurabase/tests/notifications.test.ts +0 -314
  46. package/packages/aurabase/tests/observability-backup.test.ts +0 -186
  47. package/packages/aurabase/tests/observability-hook.test.ts +0 -130
  48. package/packages/aurabase/tests/production-robustness.test.ts +0 -268
  49. package/packages/aurabase/tests/public-api-surface.test.ts +0 -53
  50. package/packages/aurabase/tests/query-builder.test.ts +0 -329
  51. package/packages/aurabase/tests/readme-smoke-e2e.test.ts +0 -231
  52. package/packages/aurabase/tests/realtime-filter-e2e.test.ts +0 -344
  53. package/packages/aurabase/tests/refresh-locks.test.ts +0 -210
  54. package/packages/aurabase/tests/retry-option.test.ts +0 -17
  55. package/packages/aurabase/tests/rpc-e2e.test.ts +0 -254
  56. package/packages/aurabase/tests/rs256-local-e2e.test.ts +0 -114
  57. package/packages/aurabase/tests/rt-alias-public-e2e.test.ts +0 -235
  58. package/packages/aurabase/tests/rt-http-broadcast-e2e.test.ts +0 -124
  59. package/packages/aurabase/tests/service-role-guard.test.ts +0 -99
  60. package/packages/aurabase/tests/ssr.test.ts +0 -91
  61. package/packages/aurabase/tests/storage-backend-e2e.test.ts +0 -481
  62. package/packages/aurabase/tests/storage-sdk-parity-e2e.test.ts +0 -104
  63. package/packages/aurabase/tests/storage-transform-e2e.test.ts +0 -684
  64. package/packages/aurabase/tests/storage.test.ts +0 -55
  65. package/packages/aurabase/tests/types/client-generic.test-d.ts +0 -79
  66. package/packages/aurabase/tests/types/docs-snippets.test-d.ts +0 -571
  67. package/packages/aurabase/tests/types/error-code.test-d.ts +0 -53
  68. package/packages/aurabase/tsconfig.json +0 -5
  69. package/packages/aurabase/tsconfig.test-d.json +0 -5
  70. package/packages/aurabase/tsup.config.ts +0 -11
  71. package/packages/aurabase/vitest.config.ts +0 -16
  72. package/packages/auth/node_modules/.bin/esbuild +0 -19
  73. package/packages/auth/node_modules/.bin/tsc +0 -22
  74. package/packages/auth/node_modules/.bin/tsserver +0 -22
  75. package/packages/auth/node_modules/.bin/tsup +0 -22
  76. package/packages/auth/node_modules/.bin/tsup-node +0 -22
  77. package/packages/auth/node_modules/.bin/vite +0 -22
  78. package/packages/auth/node_modules/.bin/vitest +0 -22
  79. package/packages/auth/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  80. package/packages/auth/src/AuthAdminService.ts +0 -149
  81. package/packages/auth/src/AuthService.ts +0 -1166
  82. package/packages/auth/src/SessionManager.ts +0 -92
  83. package/packages/auth/src/index.ts +0 -17
  84. package/packages/auth/src/jwks.ts +0 -337
  85. package/packages/auth/src/pkce.ts +0 -27
  86. package/packages/auth/src/types.ts +0 -89
  87. package/packages/auth/tests/auth-lifecycle.test.ts +0 -149
  88. package/packages/auth/tests/detect-session.test.ts +0 -361
  89. package/packages/auth/tests/getclaims.test.ts +0 -780
  90. package/packages/auth/tests/is-mfa-challenge.test.ts +0 -27
  91. package/packages/auth/tests/password-recovery.test.ts +0 -124
  92. package/packages/auth/tests/pkce.test.ts +0 -192
  93. package/packages/auth/tests/session-manager.test.ts +0 -103
  94. package/packages/auth/tests/set-session.test.ts +0 -139
  95. package/packages/auth/tests/verify-otp.test.ts +0 -153
  96. package/packages/auth/tsconfig.json +0 -5
  97. package/packages/auth/tsup.config.ts +0 -11
  98. package/packages/core/node_modules/.bin/esbuild +0 -22
  99. package/packages/core/node_modules/.bin/tsc +0 -22
  100. package/packages/core/node_modules/.bin/tsserver +0 -22
  101. package/packages/core/node_modules/.bin/tsup +0 -22
  102. package/packages/core/node_modules/.bin/tsup-node +0 -22
  103. package/packages/core/node_modules/.bin/vite +0 -22
  104. package/packages/core/node_modules/.bin/vitest +0 -22
  105. package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  106. package/packages/core/src/AuraError.ts +0 -89
  107. package/packages/core/src/BaseService.ts +0 -44
  108. package/packages/core/src/client.ts +0 -34
  109. package/packages/core/src/cookie-utils.ts +0 -42
  110. package/packages/core/src/fetch.ts +0 -435
  111. package/packages/core/src/index.ts +0 -22
  112. package/packages/core/src/retry.ts +0 -63
  113. package/packages/core/src/serialize.ts +0 -6
  114. package/packages/core/src/stores/BaseAuthStore.ts +0 -164
  115. package/packages/core/src/stores/LocalAuthStore.ts +0 -199
  116. package/packages/core/src/types.ts +0 -1042
  117. package/packages/core/tests/auth-store.test.ts +0 -300
  118. package/packages/core/tests/error-code.test.ts +0 -61
  119. package/packages/core/tests/error.test.ts +0 -24
  120. package/packages/core/tests/fetch.test.ts +0 -286
  121. package/packages/core/tests/mocks.ts +0 -139
  122. package/packages/core/tests/observability.test.ts +0 -86
  123. package/packages/core/tests/refresh-hardening.test.ts +0 -164
  124. package/packages/core/tests/refresh-observability.test.ts +0 -111
  125. package/packages/core/tests/retry.test.ts +0 -106
  126. package/packages/core/tests/serialize.test.ts +0 -12
  127. package/packages/core/tests/timeout.test.ts +0 -79
  128. package/packages/core/tsconfig.json +0 -5
  129. package/packages/core/tsup.config.ts +0 -11
  130. package/packages/db/node_modules/.bin/esbuild +0 -19
  131. package/packages/db/node_modules/.bin/tsc +0 -22
  132. package/packages/db/node_modules/.bin/tsserver +0 -22
  133. package/packages/db/node_modules/.bin/tsup +0 -22
  134. package/packages/db/node_modules/.bin/tsup-node +0 -22
  135. package/packages/db/node_modules/.bin/vite +0 -22
  136. package/packages/db/node_modules/.bin/vitest +0 -22
  137. package/packages/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  138. package/packages/db/src/DatabaseService.ts +0 -170
  139. package/packages/db/src/QueryBuilder.ts +0 -830
  140. package/packages/db/src/index.ts +0 -2
  141. package/packages/db/src/pgrst-types/common/common.ts +0 -86
  142. package/packages/db/src/pgrst-types/common/rpc.ts +0 -151
  143. package/packages/db/src/pgrst-types/feature-flags.ts +0 -20
  144. package/packages/db/src/pgrst-types/types.ts +0 -134
  145. package/packages/db/src/select-query-parser/parser.ts +0 -479
  146. package/packages/db/src/select-query-parser/result.ts +0 -553
  147. package/packages/db/src/select-query-parser/types.ts +0 -132
  148. package/packages/db/src/select-query-parser/utils.ts +0 -707
  149. package/packages/db/tests/filters.test.ts +0 -81
  150. package/packages/db/tests/mutations-filtered.e2e.test.ts +0 -132
  151. package/packages/db/tests/mutations-filtered.test.ts +0 -188
  152. package/packages/db/tests/order.test.ts +0 -45
  153. package/packages/db/tests/rpc.test.ts +0 -41
  154. package/packages/db/tests/scale/build-large-schema.ts +0 -94
  155. package/packages/db/tests/scale/scale-compile.test.ts +0 -167
  156. package/packages/db/tests/schema.test.ts +0 -77
  157. package/packages/db/tests/single-accept.e2e.test.ts +0 -123
  158. package/packages/db/tests/single.test.ts +0 -211
  159. package/packages/db/tests/types/filters.test-d.ts +0 -52
  160. package/packages/db/tests/types/fixtures.ts +0 -40
  161. package/packages/db/tests/types/from-views.test-d.ts +0 -18
  162. package/packages/db/tests/types/mutations.test-d.ts +0 -23
  163. package/packages/db/tests/types/rpc.test-d.ts +0 -60
  164. package/packages/db/tests/types/schema-shape.test-d.ts +0 -8
  165. package/packages/db/tests/types/select-inference.test-d.ts +0 -38
  166. package/packages/db/tests/vendored/fixtures.ts +0 -83
  167. package/packages/db/tests/vendored/get-result.test-d.ts +0 -75
  168. package/packages/db/tsconfig.json +0 -5
  169. package/packages/db/tsconfig.test-d.json +0 -5
  170. package/packages/db/tsup.config.ts +0 -11
  171. package/packages/db/vitest.config.ts +0 -8
  172. package/packages/functions/node_modules/.bin/esbuild +0 -19
  173. package/packages/functions/node_modules/.bin/tsc +0 -22
  174. package/packages/functions/node_modules/.bin/tsserver +0 -22
  175. package/packages/functions/node_modules/.bin/tsup +0 -22
  176. package/packages/functions/node_modules/.bin/tsup-node +0 -22
  177. package/packages/functions/node_modules/.bin/vite +0 -22
  178. package/packages/functions/node_modules/.bin/vitest +0 -22
  179. package/packages/functions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  180. package/packages/functions/src/FunctionsService.ts +0 -188
  181. package/packages/functions/src/errors.ts +0 -70
  182. package/packages/functions/src/index.ts +0 -6
  183. package/packages/functions/tests/invoke.test.ts +0 -97
  184. package/packages/functions/tests/response-type-errors.test.ts +0 -135
  185. package/packages/functions/tsconfig.json +0 -5
  186. package/packages/functions/tsup.config.ts +0 -11
  187. package/packages/gen/node_modules/.bin/esbuild +0 -19
  188. package/packages/gen/node_modules/.bin/tsc +0 -22
  189. package/packages/gen/node_modules/.bin/tsserver +0 -22
  190. package/packages/gen/node_modules/.bin/tsup +0 -22
  191. package/packages/gen/node_modules/.bin/tsup-node +0 -22
  192. package/packages/gen/node_modules/.bin/vite +0 -22
  193. package/packages/gen/node_modules/.bin/vitest +0 -22
  194. package/packages/gen/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  195. package/packages/gen/src/cli.ts +0 -67
  196. package/packages/gen/src/generate.ts +0 -0
  197. package/packages/gen/src/index.ts +0 -2
  198. package/packages/gen/tests/compile.test.ts +0 -213
  199. package/packages/gen/tests/e2e.test.ts +0 -130
  200. package/packages/gen/tests/generate.test.ts +0 -396
  201. package/packages/gen/tsconfig.json +0 -5
  202. package/packages/gen/tsup.config.ts +0 -11
  203. package/packages/notifications/node_modules/.bin/esbuild +0 -19
  204. package/packages/notifications/node_modules/.bin/tsc +0 -22
  205. package/packages/notifications/node_modules/.bin/tsserver +0 -22
  206. package/packages/notifications/node_modules/.bin/tsup +0 -22
  207. package/packages/notifications/node_modules/.bin/tsup-node +0 -22
  208. package/packages/notifications/node_modules/.bin/vite +0 -22
  209. package/packages/notifications/node_modules/.bin/vitest +0 -22
  210. package/packages/notifications/src/NotificationsService.ts +0 -230
  211. package/packages/notifications/src/index.ts +0 -19
  212. package/packages/notifications/tsconfig.json +0 -5
  213. package/packages/notifications/tsup.config.ts +0 -11
  214. package/packages/realtime/node_modules/.bin/esbuild +0 -19
  215. package/packages/realtime/node_modules/.bin/tsc +0 -22
  216. package/packages/realtime/node_modules/.bin/tsserver +0 -22
  217. package/packages/realtime/node_modules/.bin/tsup +0 -22
  218. package/packages/realtime/node_modules/.bin/tsup-node +0 -22
  219. package/packages/realtime/node_modules/.bin/vite +0 -22
  220. package/packages/realtime/node_modules/.bin/vitest +0 -22
  221. package/packages/realtime/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  222. package/packages/realtime/src/AuraChannel.ts +0 -485
  223. package/packages/realtime/src/HeartbeatManager.ts +0 -73
  224. package/packages/realtime/src/RealtimeService.ts +0 -505
  225. package/packages/realtime/src/columnFilter.ts +0 -301
  226. package/packages/realtime/src/index.ts +0 -4
  227. package/packages/realtime/tests/ack-routing.test.ts +0 -81
  228. package/packages/realtime/tests/column-filter.test.ts +0 -144
  229. package/packages/realtime/tests/dispatch.test.ts +0 -191
  230. package/packages/realtime/tests/heartbeat.test.ts +0 -67
  231. package/packages/realtime/tests/http-broadcast.test.ts +0 -68
  232. package/packages/realtime/tests/realtime-token.test.ts +0 -43
  233. package/packages/realtime/tests/reconnect-race.test.ts +0 -149
  234. package/packages/realtime/tests/ref-id.test.ts +0 -34
  235. package/packages/realtime/tests/set-auth.test.ts +0 -70
  236. package/packages/realtime/tsconfig.json +0 -5
  237. package/packages/realtime/tsup.config.ts +0 -11
  238. package/packages/storage/node_modules/.bin/esbuild +0 -19
  239. package/packages/storage/node_modules/.bin/tsc +0 -22
  240. package/packages/storage/node_modules/.bin/tsserver +0 -22
  241. package/packages/storage/node_modules/.bin/tsup +0 -22
  242. package/packages/storage/node_modules/.bin/tsup-node +0 -22
  243. package/packages/storage/node_modules/.bin/vite +0 -22
  244. package/packages/storage/node_modules/.bin/vitest +0 -22
  245. package/packages/storage/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  246. package/packages/storage/src/DownloadBuilder.ts +0 -60
  247. package/packages/storage/src/StorageService.ts +0 -323
  248. package/packages/storage/src/index.ts +0 -2
  249. package/packages/storage/src/path.ts +0 -35
  250. package/packages/storage/src/transform.ts +0 -15
  251. package/packages/storage/tests/download.test.ts +0 -64
  252. package/packages/storage/tests/parity.test.ts +0 -154
  253. package/packages/storage/tests/path.test.ts +0 -87
  254. package/packages/storage/tests/transform.test.ts +0 -79
  255. package/packages/storage/tsconfig.json +0 -5
  256. package/packages/storage/tsup.config.ts +0 -11
  257. package/pnpm-workspace.yaml +0 -24
  258. package/tsconfig.base.json +0 -15
@@ -1,947 +0,0 @@
1
- // ---------------------------------------------------------------------------
2
- // AuthService tests
3
- // ---------------------------------------------------------------------------
4
-
5
- import { describe, it, expect, beforeEach, afterEach } from 'vitest'
6
- import { AuraClient } from '../src/AuraClient'
7
- import { BaseAuthStore } from '@aurabase/core'
8
- import { FetchMock, expiredJwt, fakeTokenPair, fakeUser, validJwt } from './mocks'
9
-
10
- const BASE = 'http://localhost:8080'
11
- const PROJECT = 'test-project'
12
- const PREFIX = `${BASE}/v1/auth/${PROJECT}`
13
-
14
- let fetchMock: FetchMock
15
- let client: AuraClient
16
-
17
- beforeEach(() => {
18
- fetchMock = new FetchMock()
19
- fetchMock.init()
20
- client = new AuraClient(BASE, PROJECT, {
21
- auth: { persistSession: false },
22
- })
23
- })
24
-
25
- afterEach(() => {
26
- fetchMock.restore()
27
- fetchMock.clearMocks()
28
- })
29
-
30
- // ---------------------------------------------------------------------------
31
- describe('AuthService.signUp', () => {
32
- it('saves tokens on successful registration', async () => {
33
- const tp = fakeTokenPair()
34
- fetchMock.on({
35
- method: 'POST',
36
- url: `${PREFIX}/register`,
37
- replyCode: 200,
38
- replyBody: { data: tp },
39
- })
40
-
41
- const res = await client.auth.signUp({ email: 'a@b.com', password: 'secret' })
42
-
43
- expect(res.error).toBeNull()
44
- expect(res.data).toBeDefined()
45
- expect(client.authStore.token).toBe(tp.access_token)
46
- expect(client.authStore.refreshToken).toBe(tp.refresh_token)
47
- expect(client.authStore.user?.email).toBe('test@example.com')
48
- })
49
-
50
- it('returns error on 400', async () => {
51
- fetchMock.on({
52
- method: 'POST',
53
- url: `${PREFIX}/register`,
54
- replyCode: 400,
55
- replyBody: { error: { code: 'invalid_request', message: 'Email taken' } },
56
- })
57
-
58
- const res = await client.auth.signUp({ email: 'a@b.com', password: 'x' })
59
-
60
- expect(res.error).toBeDefined()
61
- expect(res.error!.code).toBe('invalid_request')
62
- expect(client.authStore.token).toBeNull()
63
- })
64
- })
65
-
66
- // ---------------------------------------------------------------------------
67
- describe('AuthService.signInWithPassword', () => {
68
- it('saves tokens on success', async () => {
69
- const tp = fakeTokenPair()
70
- fetchMock.on({
71
- method: 'POST',
72
- url: `${PREFIX}/login`,
73
- replyCode: 200,
74
- replyBody: { data: tp },
75
- })
76
-
77
- const res = await client.auth.signInWithPassword({ email: 'a@b.com', password: 'pass' })
78
-
79
- expect(res.error).toBeNull()
80
- expect(client.authStore.token).toBe(tp.access_token)
81
- expect(client.authStore.user?.id).toBe('user-1')
82
- })
83
-
84
- it('detects MFA challenge', async () => {
85
- const mfa = { mfa_required: true, mfa_token: 'mfa_tok_123' }
86
- fetchMock.on({
87
- method: 'POST',
88
- url: `${PREFIX}/login`,
89
- replyCode: 200,
90
- replyBody: { data: mfa },
91
- })
92
-
93
- const events: string[] = []
94
- client.auth.onAuthStateChange((payload) => {
95
- events.push(payload.event)
96
- })
97
-
98
- const res = await client.auth.signInWithPassword({ email: 'a@b.com', password: 'pass' })
99
-
100
- expect(res.data).toBeDefined()
101
- expect((res.data as any).mfa_required).toBe(true)
102
- // Token should NOT be saved for MFA challenge
103
- expect(client.authStore.token).toBeNull()
104
- expect(events).toContain('MFA_CHALLENGE')
105
- })
106
-
107
- it('MFA_CHALLENGE porte mfaToken + mfaSetupRequired', async () => {
108
- fetchMock.on({
109
- method: 'POST',
110
- url: `${PREFIX}/login`,
111
- replyCode: 200,
112
- replyBody: { data: { mfa_required: true, mfa_token: 'MT', mfa_setup_required: true } },
113
- })
114
-
115
- const payloads: any[] = []
116
- client.auth.onAuthStateChange((p) => payloads.push(p))
117
-
118
- await client.auth.signInWithPassword({ email: 'e', password: 'p' })
119
-
120
- const c = payloads.find((p) => p.event === 'MFA_CHALLENGE')
121
- expect(c).toBeDefined()
122
- expect(c.mfaToken).toBe('MT')
123
- expect(c.mfaSetupRequired).toBe(true)
124
- })
125
- })
126
-
127
- // ---------------------------------------------------------------------------
128
- describe('AuthService.signOut', () => {
129
- it('clears store on signOut', async () => {
130
- // Pre-populate the store
131
- const tp = fakeTokenPair()
132
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
133
-
134
- fetchMock.on({
135
- method: 'POST',
136
- url: `${PREFIX}/logout`,
137
- replyCode: 200,
138
- replyBody: { data: null },
139
- })
140
-
141
- const res = await client.auth.signOut()
142
-
143
- expect(res.error).toBeNull()
144
- // LOT 3 (I5) : `logout` backend (`services/aura-auth/src/handlers/auth.rs::logout`)
145
- // renvoie réellement `ApiResponse::<()>::empty()` — `fetch.ts` honore maintenant ce
146
- // `null` explicite (`'data' in body`) au lieu de retomber sur l'enveloppe entière.
147
- expect(res.data).toBeNull()
148
- expect(client.authStore.token).toBeNull()
149
- expect(client.authStore.refreshToken).toBeNull()
150
- expect(client.authStore.user).toBeNull()
151
- })
152
-
153
- it('does not refresh before logout', async () => {
154
- const tp = fakeTokenPair({ access_token: expiredJwt(), refresh_token: 'rt_old' })
155
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
156
-
157
- fetchMock.on({
158
- method: 'POST',
159
- url: `${PREFIX}/logout`,
160
- replyCode: 200,
161
- replyBody: { data: null },
162
- })
163
-
164
- const res = await client.auth.signOut()
165
-
166
- expect(res.error).toBeNull()
167
- expect(fetchMock.calls).toHaveLength(1)
168
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/logout`)
169
- expect((fetchMock.calls[0].body as any).refresh_token).toBe('rt_old')
170
- })
171
-
172
- it('clears local store without network when no session exists', async () => {
173
- const res = await client.auth.signOut()
174
-
175
- expect(res.error).toBeNull()
176
- expect(fetchMock.calls).toHaveLength(0)
177
- })
178
- })
179
-
180
- // ---------------------------------------------------------------------------
181
- describe('AuthService.refreshSession', () => {
182
- it('updates store with new tokens', async () => {
183
- const oldTp = fakeTokenPair()
184
- client.authStore.save(oldTp.access_token, oldTp.refresh_token, oldTp.user)
185
-
186
- const newTp = fakeTokenPair({
187
- access_token: validJwt(),
188
- refresh_token: 'rt_new',
189
- user: fakeUser({ email: 'new@example.com' }),
190
- })
191
- fetchMock.on({
192
- method: 'POST',
193
- url: `${PREFIX}/refresh`,
194
- replyCode: 200,
195
- replyBody: { data: newTp },
196
- })
197
-
198
- const events: string[] = []
199
- client.auth.onAuthStateChange((p) => events.push(p.event))
200
-
201
- const res = await client.auth.refreshSession()
202
-
203
- expect(res.error).toBeNull()
204
- expect(client.authStore.refreshToken).toBe('rt_new')
205
- expect(client.authStore.user?.email).toBe('new@example.com')
206
- expect(events).toContain('TOKEN_REFRESHED')
207
- })
208
-
209
- it('returns a local error when no refresh token exists', async () => {
210
- const res = await client.auth.refreshSession()
211
-
212
- expect(res.data).toBeNull()
213
- expect(res.error?.code).toBe('no_session')
214
- expect(fetchMock.calls).toHaveLength(0)
215
- })
216
- })
217
-
218
- // ---------------------------------------------------------------------------
219
- describe('AuthService.sessions', () => {
220
- it('sends X-Refresh-Token when listing sessions', async () => {
221
- const tp = fakeTokenPair({ refresh_token: 'rt_current' })
222
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
223
- fetchMock.on({
224
- method: 'GET',
225
- url: `${PREFIX}/sessions`,
226
- replyCode: 200,
227
- replyBody: { data: [] },
228
- })
229
-
230
- const res = await client.auth.listSessions()
231
-
232
- expect(res.error).toBeNull()
233
- expect(fetchMock.calls[0].headers.get('x-refresh-token')).toBe('rt_current')
234
- })
235
-
236
- it('clears store after global revoke succeeds', async () => {
237
- const tp = fakeTokenPair({ refresh_token: 'rt_global' })
238
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
239
- fetchMock.on({
240
- method: 'POST',
241
- url: `${PREFIX}/logout`,
242
- replyCode: 200,
243
- replyBody: { data: null },
244
- })
245
-
246
- const res = await client.auth.revokeAllSessions()
247
-
248
- expect(res.error).toBeNull()
249
- expect(client.authStore.token).toBeNull()
250
- expect((fetchMock.calls[0].body as any).scope).toBe('global')
251
- expect((fetchMock.calls[0].body as any).refresh_token).toBe('rt_global')
252
- })
253
- })
254
-
255
- // ---------------------------------------------------------------------------
256
- describe('AuthService.getUser', () => {
257
- it('returns user from /me endpoint', async () => {
258
- const tp = fakeTokenPair()
259
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
260
-
261
- const user = fakeUser({ email: 'me@example.com' })
262
- fetchMock.on({
263
- method: 'GET',
264
- url: `${PREFIX}/user`,
265
- replyCode: 200,
266
- replyBody: { data: user },
267
- })
268
-
269
- const res = await client.auth.getUser()
270
-
271
- expect(res.error).toBeNull()
272
- expect(res.data?.email).toBe('me@example.com')
273
- })
274
-
275
- it('returns 401 when not authenticated', async () => {
276
- fetchMock.on({
277
- method: 'GET',
278
- url: `${PREFIX}/user`,
279
- replyCode: 401,
280
- replyBody: { error: { code: 'unauthorized', message: 'Not authenticated' } },
281
- })
282
-
283
- const res = await client.auth.getUser()
284
-
285
- expect(res.error).toBeDefined()
286
- expect(res.error!.code).toBe('unauthorized')
287
- })
288
- })
289
-
290
- // ---------------------------------------------------------------------------
291
- describe('AuthService.onAuthStateChange', () => {
292
- it('fires callback on sign in and sign out', async () => {
293
- const tp = fakeTokenPair()
294
- fetchMock.on({
295
- method: 'POST',
296
- url: `${PREFIX}/login`,
297
- replyCode: 200,
298
- replyBody: { data: tp },
299
- })
300
- fetchMock.on({
301
- method: 'POST',
302
- url: `${PREFIX}/logout`,
303
- replyCode: 200,
304
- replyBody: { data: null },
305
- })
306
-
307
- const events: string[] = []
308
- const { unsubscribe } = client.auth.onAuthStateChange((p) => {
309
- events.push(p.event)
310
- })
311
-
312
- await client.auth.signInWithPassword({ email: 'a@b.com', password: 'pass' })
313
- await client.auth.signOut()
314
-
315
- expect(events).toContain('SIGNED_IN')
316
- expect(events).toContain('SIGNED_OUT')
317
-
318
- unsubscribe()
319
- })
320
-
321
- it('unsubscribe stops further events', async () => {
322
- const tp = fakeTokenPair()
323
- fetchMock.on({
324
- method: 'POST',
325
- url: `${PREFIX}/login`,
326
- replyCode: 200,
327
- replyBody: { data: tp },
328
- })
329
-
330
- const events: string[] = []
331
- const { unsubscribe } = client.auth.onAuthStateChange((p) => {
332
- events.push(p.event)
333
- })
334
-
335
- await client.auth.signInWithPassword({ email: 'a@b.com', password: 'pass' })
336
- expect(events.length).toBeGreaterThan(0)
337
-
338
- const countBefore = events.length
339
- unsubscribe()
340
-
341
- // Manually trigger another change -- should not fire
342
- client.authStore.clear()
343
- expect(events.length).toBe(countBefore)
344
- })
345
- })
346
-
347
- // ---------------------------------------------------------------------------
348
- describe('AuthService.signInAnonymously', () => {
349
- it('saves anonymous session', async () => {
350
- const tp = fakeTokenPair({
351
- user: fakeUser({ is_anonymous: true, email: null }),
352
- })
353
- fetchMock.on({
354
- method: 'POST',
355
- url: `${PREFIX}/anonymous`,
356
- replyCode: 200,
357
- replyBody: { data: tp },
358
- })
359
-
360
- const res = await client.auth.signInAnonymously()
361
-
362
- expect(res.error).toBeNull()
363
- expect(client.authStore.user?.is_anonymous).toBe(true)
364
- expect(client.authStore.token).toBe(tp.access_token)
365
- })
366
- })
367
-
368
- // ---------------------------------------------------------------------------
369
- describe('AuthService.signInWithMagicLink', () => {
370
- it('sends POST /magic-link with email', async () => {
371
- fetchMock.on({
372
- method: 'POST',
373
- url: `${PREFIX}/magic-link`,
374
- replyCode: 200,
375
- replyBody: { data: { message: 'Magic link sent' } },
376
- })
377
-
378
- const res = await client.auth.signInWithMagicLink({ email: 'a@b.com' })
379
-
380
- expect(res.error).toBeNull()
381
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/magic-link`)
382
- expect(fetchMock.calls[0].method).toBe('POST')
383
- expect((fetchMock.calls[0].body as any).email).toBe('a@b.com')
384
- })
385
-
386
- it('returns error on 400', async () => {
387
- fetchMock.on({
388
- method: 'POST',
389
- url: `${PREFIX}/magic-link`,
390
- replyCode: 400,
391
- replyBody: { error: { code: 'invalid_email', message: 'Invalid email' } },
392
- })
393
-
394
- const res = await client.auth.signInWithMagicLink({ email: 'bad' })
395
-
396
- expect(res.error).toBeDefined()
397
- expect(res.error!.code).toBe('invalid_email')
398
- })
399
- })
400
-
401
- // ---------------------------------------------------------------------------
402
- describe('AuthService.signInWithOAuth', () => {
403
- it('renvoie { url } et envoie redirect_uri (pas redirect_to)', async () => {
404
- const { data, error } = await client.auth.signInWithOAuth('github', {
405
- redirectTo: 'http://app/cb',
406
- })
407
- // No fetch call should be made — OAuth start is a redirect
408
- expect(fetchMock.calls.length).toBe(0)
409
- expect(error).toBeNull()
410
- expect(data?.url).toContain('/oauth/github/start')
411
- expect(data?.url).toContain('redirect_uri=')
412
- expect(data?.url).not.toContain('redirect_to=')
413
- })
414
- })
415
-
416
- // ---------------------------------------------------------------------------
417
- describe('AuthService.signInWithEmailOtp', () => {
418
- it('sends POST /email-otp/send with email', async () => {
419
- fetchMock.on({
420
- method: 'POST',
421
- url: `${PREFIX}/email-otp/send`,
422
- replyCode: 200,
423
- replyBody: { data: { message: 'OTP sent' } },
424
- })
425
-
426
- const res = await client.auth.signInWithEmailOtp('otp@example.com')
427
-
428
- expect(res.error).toBeNull()
429
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/email-otp/send`)
430
- expect((fetchMock.calls[0].body as any).email).toBe('otp@example.com')
431
- })
432
- })
433
-
434
- // ---------------------------------------------------------------------------
435
- describe('AuthService.verifyEmailOtp', () => {
436
- it('saves tokens on success', async () => {
437
- const tp = fakeTokenPair()
438
- fetchMock.on({
439
- method: 'POST',
440
- url: `${PREFIX}/email-otp/verify`,
441
- replyCode: 200,
442
- replyBody: { data: tp },
443
- })
444
-
445
- const res = await client.auth.verifyEmailOtp('otp@example.com', '123456')
446
-
447
- expect(res.error).toBeNull()
448
- expect(client.authStore.token).toBe(tp.access_token)
449
- expect((fetchMock.calls[0].body as any).email).toBe('otp@example.com')
450
- expect((fetchMock.calls[0].body as any).code).toBe('123456')
451
- })
452
-
453
- it('returns error on invalid code', async () => {
454
- fetchMock.on({
455
- method: 'POST',
456
- url: `${PREFIX}/email-otp/verify`,
457
- replyCode: 400,
458
- replyBody: { error: { code: 'invalid_otp', message: 'Code invalide' } },
459
- })
460
-
461
- const res = await client.auth.verifyEmailOtp('otp@example.com', '000000')
462
-
463
- expect(res.error).toBeDefined()
464
- expect(res.error!.code).toBe('invalid_otp')
465
- expect(client.authStore.token).toBeNull()
466
- })
467
- })
468
-
469
- // ---------------------------------------------------------------------------
470
- describe('AuthService.signInWithSmsOtp', () => {
471
- it('sends POST /sms/send with phone', async () => {
472
- fetchMock.on({
473
- method: 'POST',
474
- url: `${PREFIX}/sms/send`,
475
- replyCode: 200,
476
- replyBody: { data: { message: 'SMS sent' } },
477
- })
478
-
479
- const res = await client.auth.signInWithSmsOtp('+33612345678')
480
-
481
- expect(res.error).toBeNull()
482
- expect((fetchMock.calls[0].body as any).phone).toBe('+33612345678')
483
- })
484
- })
485
-
486
- // ---------------------------------------------------------------------------
487
- describe('AuthService.verifySmsOtp', () => {
488
- it('saves tokens on success', async () => {
489
- const tp = fakeTokenPair()
490
- fetchMock.on({
491
- method: 'POST',
492
- url: `${PREFIX}/sms/verify`,
493
- replyCode: 200,
494
- replyBody: { data: tp },
495
- })
496
-
497
- const res = await client.auth.verifySmsOtp('+33612345678', '654321')
498
-
499
- expect(res.error).toBeNull()
500
- expect(client.authStore.token).toBe(tp.access_token)
501
- expect((fetchMock.calls[0].body as any).phone).toBe('+33612345678')
502
- expect((fetchMock.calls[0].body as any).code).toBe('654321')
503
- })
504
- })
505
-
506
- // ---------------------------------------------------------------------------
507
- describe('AuthService.verifyEmail', () => {
508
- it('saves tokens after email verification', async () => {
509
- const tp = fakeTokenPair()
510
- fetchMock.on({
511
- method: 'POST',
512
- url: `${PREFIX}/verify-email`,
513
- replyCode: 200,
514
- replyBody: { data: tp },
515
- })
516
-
517
- const res = await client.auth.verifyEmail('verify-token-123')
518
-
519
- expect(res.error).toBeNull()
520
- expect(client.authStore.token).toBe(tp.access_token)
521
- expect((fetchMock.calls[0].body as any).token).toBe('verify-token-123')
522
- })
523
- })
524
-
525
- // ---------------------------------------------------------------------------
526
- describe('AuthService.resendVerificationEmail', () => {
527
- it('sends POST /resend-verification', async () => {
528
- fetchMock.on({
529
- method: 'POST',
530
- url: `${PREFIX}/resend-verification`,
531
- replyCode: 200,
532
- replyBody: { data: { message: 'Email sent' } },
533
- })
534
-
535
- const res = await client.auth.resendVerificationEmail('a@b.com')
536
-
537
- expect(res.error).toBeNull()
538
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/resend-verification`)
539
- expect((fetchMock.calls[0].body as any).email).toBe('a@b.com')
540
- })
541
-
542
- it('returns a local error when email is missing', async () => {
543
- const res = await client.auth.resendVerificationEmail()
544
-
545
- expect(res.data).toBeNull()
546
- expect(res.error?.code).toBe('missing_email')
547
- expect(fetchMock.calls).toHaveLength(0)
548
- })
549
- })
550
-
551
- // ---------------------------------------------------------------------------
552
- describe('AuthService.updateUser', () => {
553
- it('sends PUT /user with body and updates store', async () => {
554
- const tp = fakeTokenPair()
555
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
556
-
557
- const updatedUser = fakeUser({ email: 'updated@example.com' })
558
- fetchMock.on({
559
- method: 'PUT',
560
- url: `${PREFIX}/user`,
561
- replyCode: 200,
562
- replyBody: { data: updatedUser },
563
- })
564
-
565
- const res = await client.auth.updateUser({ email: 'updated@example.com' })
566
-
567
- expect(res.error).toBeNull()
568
- expect(client.authStore.user?.email).toBe('updated@example.com')
569
- expect(fetchMock.calls[0].method).toBe('PUT')
570
- })
571
- })
572
-
573
- // ---------------------------------------------------------------------------
574
- describe('AuthService.resetPasswordForEmail', () => {
575
- it('sends POST /forgot-password with email', async () => {
576
- fetchMock.on({
577
- method: 'POST',
578
- url: `${PREFIX}/forgot-password`,
579
- replyCode: 200,
580
- replyBody: { data: { message: 'Email sent' } },
581
- })
582
-
583
- const res = await client.auth.resetPasswordForEmail({ email: 'reset@example.com' })
584
-
585
- expect(res.error).toBeNull()
586
- expect((fetchMock.calls[0].body as any).email).toBe('reset@example.com')
587
- })
588
- })
589
-
590
- // ---------------------------------------------------------------------------
591
- describe('AuthService.updatePassword', () => {
592
- it('sends PUT /user with password fields', async () => {
593
- const tp = fakeTokenPair()
594
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
595
-
596
- const updatedUser = fakeUser()
597
- fetchMock.on({
598
- method: 'PUT',
599
- url: `${PREFIX}/user`,
600
- replyCode: 200,
601
- replyBody: { data: updatedUser },
602
- })
603
-
604
- const res = await client.auth.updatePassword('newpass123', 'oldpass')
605
-
606
- expect(res.error).toBeNull()
607
- expect((fetchMock.calls[0].body as any).password).toBe('newpass123')
608
- expect((fetchMock.calls[0].body as any).current_password).toBe('oldpass')
609
- })
610
-
611
- it('réussi → clear store + SIGNED_OUT (le backend révoque toutes les sessions)', async () => {
612
- const tp = fakeTokenPair()
613
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
614
-
615
- fetchMock.on({
616
- method: 'PUT',
617
- url: `${PREFIX}/user`,
618
- replyCode: 200,
619
- replyBody: { data: fakeUser() },
620
- })
621
-
622
- const events: string[] = []
623
- client.auth.onAuthStateChange(({ event }) => {
624
- if (event !== 'INITIAL_SESSION') events.push(event)
625
- })
626
-
627
- const res = await client.auth.updatePassword('New123!', 'Old123!')
628
-
629
- expect(res.error).toBeNull()
630
- expect(client.authStore.token).toBeNull()
631
- expect(client.authStore.refreshToken).toBeNull()
632
- expect(events).toContain('SIGNED_OUT')
633
- })
634
-
635
- it('échec → ne déconnecte pas', async () => {
636
- const tp = fakeTokenPair()
637
- client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
638
-
639
- fetchMock.on({
640
- method: 'PUT',
641
- url: `${PREFIX}/user`,
642
- replyCode: 400,
643
- replyBody: { error: { code: 'invalid_password', message: 'Wrong current password' } },
644
- })
645
-
646
- const res = await client.auth.updatePassword('New123!', 'WrongOld')
647
-
648
- expect(res.error).toBeDefined()
649
- expect(client.authStore.token).toBe(tp.access_token)
650
- })
651
- })
652
-
653
- // ---------------------------------------------------------------------------
654
- describe('AuthService.mfaEnroll', () => {
655
- it('returns qr_code and secret', async () => {
656
- fetchMock.on({
657
- method: 'POST',
658
- url: `${PREFIX}/mfa/setup`,
659
- replyCode: 200,
660
- replyBody: { data: { qr_code: 'data:image/png;base64,abc', secret: 'JBSWY3DPEHPK3PXP' } },
661
- })
662
-
663
- const res = await client.auth.mfaEnroll()
664
-
665
- expect(res.error).toBeNull()
666
- expect((res.data as any).qr_code).toContain('data:image')
667
- expect((res.data as any).secret).toBe('JBSWY3DPEHPK3PXP')
668
- })
669
- })
670
-
671
- // ---------------------------------------------------------------------------
672
- describe('AuthService.mfaVerifySetup', () => {
673
- it('returns recovery_codes', async () => {
674
- const codes = ['AAAA-BBBB', 'CCCC-DDDD', 'EEEE-FFFF']
675
- fetchMock.on({
676
- method: 'POST',
677
- url: `${PREFIX}/mfa/setup/verify`,
678
- replyCode: 200,
679
- replyBody: { data: { recovery_codes: codes } },
680
- })
681
-
682
- const res = await client.auth.mfaVerifySetup('123456')
683
-
684
- expect(res.error).toBeNull()
685
- expect((res.data as any).recovery_codes).toEqual(codes)
686
- expect((fetchMock.calls[0].body as any).code).toBe('123456')
687
- })
688
- })
689
-
690
- // ---------------------------------------------------------------------------
691
- describe('AuthService.mfaVerify', () => {
692
- it('saves tokens after MFA verification', async () => {
693
- const tp = fakeTokenPair()
694
- fetchMock.on({
695
- method: 'POST',
696
- url: `${PREFIX}/mfa/verify-login`,
697
- replyCode: 200,
698
- replyBody: { data: tp },
699
- })
700
-
701
- const res = await client.auth.mfaVerify({ mfa_token: 'mfa_tok', code: '654321' })
702
-
703
- expect(res.error).toBeNull()
704
- expect(client.authStore.token).toBe(tp.access_token)
705
- expect(client.authStore.user?.id).toBe('user-1')
706
- })
707
- })
708
-
709
- // ---------------------------------------------------------------------------
710
- describe('AuthService.mfaUnenroll', () => {
711
- it('sends POST /mfa/disable with password', async () => {
712
- fetchMock.on({
713
- method: 'POST',
714
- url: `${PREFIX}/mfa/disable`,
715
- replyCode: 200,
716
- replyBody: { data: { message: 'MFA disabled' } },
717
- })
718
-
719
- const res = await client.auth.mfaUnenroll('mypassword')
720
-
721
- expect(res.error).toBeNull()
722
- expect((fetchMock.calls[0].body as any).password).toBe('mypassword')
723
- })
724
- })
725
-
726
- // ---------------------------------------------------------------------------
727
- describe('AuthService.listIdentities', () => {
728
- it('returns identity list', async () => {
729
- const identities = [{ id: 'id-1', provider: 'google', provider_id: 'g123' }]
730
- fetchMock.on({
731
- method: 'GET',
732
- url: `${PREFIX}/user/identities`,
733
- replyCode: 200,
734
- replyBody: { data: identities },
735
- })
736
-
737
- const res = await client.auth.listIdentities()
738
-
739
- expect(res.error).toBeNull()
740
- expect(res.data).toHaveLength(1)
741
- expect(fetchMock.calls[0].method).toBe('GET')
742
- })
743
- })
744
-
745
- // ---------------------------------------------------------------------------
746
- describe('AuthService.linkIdentity', () => {
747
- it('sends POST to link provider', async () => {
748
- fetchMock.on({
749
- method: 'POST',
750
- url: `${PREFIX}/user/identities/link/github`,
751
- replyCode: 200,
752
- replyBody: { data: { url: 'https://github.com/login/oauth', provider: 'github' } },
753
- })
754
-
755
- const res = await client.auth.linkIdentity('github')
756
-
757
- expect(res.error).toBeNull()
758
- expect((res.data as any).provider).toBe('github')
759
- })
760
- })
761
-
762
- // ---------------------------------------------------------------------------
763
- describe('AuthService.unlinkIdentity', () => {
764
- it('sends DELETE to unlink identity', async () => {
765
- fetchMock.on({
766
- method: 'DELETE',
767
- url: `${PREFIX}/user/identities/id-42`,
768
- replyCode: 200,
769
- replyBody: { data: { message: 'Unlinked' } },
770
- })
771
-
772
- const res = await client.auth.unlinkIdentity('id-42')
773
-
774
- expect(res.error).toBeNull()
775
- expect(fetchMock.calls[0].method).toBe('DELETE')
776
- expect(fetchMock.calls[0].url).toContain('id-42')
777
- })
778
- })
779
-
780
- // ---------------------------------------------------------------------------
781
- describe('AuthService.confirmEmailChange', () => {
782
- it('sends POST with token', async () => {
783
- const user = fakeUser({ email: 'newemail@example.com' })
784
- fetchMock.on({
785
- method: 'POST',
786
- url: `${PREFIX}/user/confirm-email-change`,
787
- replyCode: 200,
788
- replyBody: { data: user },
789
- })
790
-
791
- const res = await client.auth.confirmEmailChange('change-token-abc')
792
-
793
- expect(res.error).toBeNull()
794
- expect((fetchMock.calls[0].body as any).token).toBe('change-token-abc')
795
- })
796
- })
797
-
798
- // ---------------------------------------------------------------------------
799
- describe('AuthService.signInWithIdToken', () => {
800
- it('saves tokens on native mobile sign-in', async () => {
801
- const tp = fakeTokenPair()
802
- fetchMock.on({
803
- method: 'POST',
804
- url: `${PREFIX}/token`,
805
- replyCode: 200,
806
- replyBody: { data: tp },
807
- })
808
-
809
- const res = await client.auth.signInWithIdToken('google', 'google-id-token-abc')
810
-
811
- expect(res.error).toBeNull()
812
- expect(client.authStore.token).toBe(tp.access_token)
813
- expect((fetchMock.calls[0].body as any).provider).toBe('google')
814
- expect((fetchMock.calls[0].body as any).id_token).toBe('google-id-token-abc')
815
- })
816
- })
817
-
818
- // ---------------------------------------------------------------------------
819
- describe('AuthService.getSettings', () => {
820
- it('sends GET /settings and returns the public settings', async () => {
821
- fetchMock.on({
822
- method: 'GET',
823
- url: `${PREFIX}/settings`,
824
- replyCode: 200,
825
- replyBody: {
826
- data: {
827
- disable_signup: false,
828
- external: { google: true, github: false },
829
- mailer_autoconfirm: true,
830
- mfa_enabled: false,
831
- },
832
- },
833
- })
834
-
835
- const res = await client.auth.getSettings()
836
-
837
- expect(res.error).toBeNull()
838
- expect(fetchMock.calls[0].method).toBe('GET')
839
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/settings`)
840
- expect(res.data?.disable_signup).toBe(false)
841
- expect(res.data?.external).toEqual({ google: true, github: false })
842
- expect(res.data?.mailer_autoconfirm).toBe(true)
843
- expect(res.data?.mfa_enabled).toBe(false)
844
- })
845
- })
846
-
847
- // ---------------------------------------------------------------------------
848
- describe('AuthService.getAnonKey', () => {
849
- it('sends GET /anon-key and returns the anon key', async () => {
850
- fetchMock.on({
851
- method: 'GET',
852
- url: `${PREFIX}/anon-key`,
853
- replyCode: 200,
854
- replyBody: { data: { anon_key: 'anon-key-abc123' } },
855
- })
856
-
857
- const res = await client.auth.getAnonKey()
858
-
859
- expect(res.error).toBeNull()
860
- expect(fetchMock.calls[0].method).toBe('GET')
861
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/anon-key`)
862
- expect(res.data?.anon_key).toBe('anon-key-abc123')
863
- })
864
- })
865
-
866
- // ---------------------------------------------------------------------------
867
- describe('AuthService.resetPassword', () => {
868
- it('sends POST /reset-password with token+password and saves tokens (SIGNED_IN)', async () => {
869
- const tp = fakeTokenPair()
870
- fetchMock.on({
871
- method: 'POST',
872
- url: `${PREFIX}/reset-password`,
873
- replyCode: 200,
874
- replyBody: { data: tp },
875
- })
876
-
877
- const events: string[] = []
878
- client.auth.onAuthStateChange((p) => events.push(p.event))
879
-
880
- const res = await client.auth.resetPassword('recovery-token-xyz', 'newSecret123')
881
-
882
- expect(res.error).toBeNull()
883
- expect(fetchMock.calls[0].method).toBe('POST')
884
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/reset-password`)
885
- expect((fetchMock.calls[0].body as any).token).toBe('recovery-token-xyz')
886
- expect((fetchMock.calls[0].body as any).password).toBe('newSecret123')
887
- expect(client.authStore.token).toBe(tp.access_token)
888
- expect(client.authStore.refreshToken).toBe(tp.refresh_token)
889
- await Promise.resolve()
890
- expect(events).toContain('SIGNED_IN')
891
- })
892
- })
893
-
894
- // ---------------------------------------------------------------------------
895
- describe('AuthService.promoteAnonymous', () => {
896
- it('sends POST /anonymous/promote with email+password and saves tokens (USER_UPDATED)', async () => {
897
- const tp = fakeTokenPair()
898
- fetchMock.on({
899
- method: 'POST',
900
- url: `${PREFIX}/anonymous/promote`,
901
- replyCode: 200,
902
- replyBody: { data: tp },
903
- })
904
-
905
- const events: string[] = []
906
- client.auth.onAuthStateChange((p) => events.push(p.event))
907
-
908
- const res = await client.auth.promoteAnonymous({ email: 'promo@example.com', password: 'secret123' })
909
-
910
- expect(res.error).toBeNull()
911
- expect(fetchMock.calls[0].method).toBe('POST')
912
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/anonymous/promote`)
913
- expect((fetchMock.calls[0].body as any).email).toBe('promo@example.com')
914
- expect((fetchMock.calls[0].body as any).password).toBe('secret123')
915
- expect(client.authStore.token).toBe(tp.access_token)
916
- expect(client.authStore.refreshToken).toBe(tp.refresh_token)
917
- await Promise.resolve()
918
- expect(events).toContain('USER_UPDATED')
919
- })
920
- })
921
-
922
- // ---------------------------------------------------------------------------
923
- describe('AuthService.verifyMagicLink', () => {
924
- it('sends POST /magic-link/verify with token and saves tokens (SIGNED_IN)', async () => {
925
- const tp = fakeTokenPair()
926
- fetchMock.on({
927
- method: 'POST',
928
- url: `${PREFIX}/magic-link/verify`,
929
- replyCode: 200,
930
- replyBody: { data: tp },
931
- })
932
-
933
- const events: string[] = []
934
- client.auth.onAuthStateChange((p) => events.push(p.event))
935
-
936
- const res = await client.auth.verifyMagicLink('magic-token-789')
937
-
938
- expect(res.error).toBeNull()
939
- expect(fetchMock.calls[0].method).toBe('POST')
940
- expect(fetchMock.calls[0].url).toBe(`${PREFIX}/magic-link/verify`)
941
- expect((fetchMock.calls[0].body as any).token).toBe('magic-token-789')
942
- expect(client.authStore.token).toBe(tp.access_token)
943
- expect(client.authStore.refreshToken).toBe(tp.refresh_token)
944
- await Promise.resolve()
945
- expect(events).toContain('SIGNED_IN')
946
- })
947
- })