@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.
- package/package.json +4 -4
- package/packages/ai/node_modules/.bin/esbuild +0 -19
- package/packages/ai/node_modules/.bin/tsc +0 -22
- package/packages/ai/node_modules/.bin/tsserver +0 -22
- package/packages/ai/node_modules/.bin/tsup +0 -22
- package/packages/ai/node_modules/.bin/tsup-node +0 -22
- package/packages/ai/node_modules/.bin/vite +0 -22
- package/packages/ai/node_modules/.bin/vitest +0 -22
- package/packages/ai/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/ai/src/AiService.ts +0 -226
- package/packages/ai/src/index.ts +0 -18
- package/packages/ai/src/sse.ts +0 -141
- package/packages/ai/src/types.ts +0 -30
- package/packages/ai/tests/chat-stream.test.ts +0 -151
- package/packages/ai/tests/rag-namespaces.test.ts +0 -54
- package/packages/ai/tests/sse.test.ts +0 -69
- package/packages/ai/tsconfig.json +0 -5
- package/packages/ai/tsup.config.ts +0 -11
- package/packages/aurabase/node_modules/.bin/esbuild +0 -19
- package/packages/aurabase/node_modules/.bin/tsc +0 -22
- package/packages/aurabase/node_modules/.bin/tsserver +0 -22
- package/packages/aurabase/node_modules/.bin/tsup +0 -22
- package/packages/aurabase/node_modules/.bin/tsup-node +0 -22
- package/packages/aurabase/node_modules/.bin/vite +0 -22
- package/packages/aurabase/node_modules/.bin/vitest +0 -22
- package/packages/aurabase/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/aurabase/src/AuraClient.ts +0 -237
- package/packages/aurabase/src/index.ts +0 -152
- package/packages/aurabase/src/ssr.ts +0 -156
- package/packages/aurabase/tests/ai-streaming-rag-e2e.test.ts +0 -82
- package/packages/aurabase/tests/ai.test.ts +0 -111
- package/packages/aurabase/tests/auth-admin.test.ts +0 -515
- package/packages/aurabase/tests/auth-e2e.test.ts +0 -135
- package/packages/aurabase/tests/auth-hardening.test.ts +0 -206
- package/packages/aurabase/tests/auth.test.ts +0 -947
- package/packages/aurabase/tests/database.test.ts +0 -230
- package/packages/aurabase/tests/db-types-e2e.test.ts +0 -217
- package/packages/aurabase/tests/dispose.test.ts +0 -23
- package/packages/aurabase/tests/foreign-token.e2e.test.ts +0 -129
- package/packages/aurabase/tests/functions.test.ts +0 -107
- package/packages/aurabase/tests/integration.test.ts +0 -413
- package/packages/aurabase/tests/live-production-validation.js +0 -241
- package/packages/aurabase/tests/live-sdk-production-validation.js +0 -293
- package/packages/aurabase/tests/mocks.ts +0 -139
- package/packages/aurabase/tests/notifications.test.ts +0 -314
- package/packages/aurabase/tests/observability-backup.test.ts +0 -186
- package/packages/aurabase/tests/observability-hook.test.ts +0 -130
- package/packages/aurabase/tests/production-robustness.test.ts +0 -268
- package/packages/aurabase/tests/public-api-surface.test.ts +0 -53
- package/packages/aurabase/tests/query-builder.test.ts +0 -329
- package/packages/aurabase/tests/readme-smoke-e2e.test.ts +0 -231
- package/packages/aurabase/tests/realtime-filter-e2e.test.ts +0 -344
- package/packages/aurabase/tests/refresh-locks.test.ts +0 -210
- package/packages/aurabase/tests/retry-option.test.ts +0 -17
- package/packages/aurabase/tests/rpc-e2e.test.ts +0 -254
- package/packages/aurabase/tests/rs256-local-e2e.test.ts +0 -114
- package/packages/aurabase/tests/rt-alias-public-e2e.test.ts +0 -235
- package/packages/aurabase/tests/rt-http-broadcast-e2e.test.ts +0 -124
- package/packages/aurabase/tests/service-role-guard.test.ts +0 -99
- package/packages/aurabase/tests/ssr.test.ts +0 -91
- package/packages/aurabase/tests/storage-backend-e2e.test.ts +0 -481
- package/packages/aurabase/tests/storage-sdk-parity-e2e.test.ts +0 -104
- package/packages/aurabase/tests/storage-transform-e2e.test.ts +0 -684
- package/packages/aurabase/tests/storage.test.ts +0 -55
- package/packages/aurabase/tests/types/client-generic.test-d.ts +0 -79
- package/packages/aurabase/tests/types/docs-snippets.test-d.ts +0 -571
- package/packages/aurabase/tests/types/error-code.test-d.ts +0 -53
- package/packages/aurabase/tsconfig.json +0 -5
- package/packages/aurabase/tsconfig.test-d.json +0 -5
- package/packages/aurabase/tsup.config.ts +0 -11
- package/packages/aurabase/vitest.config.ts +0 -16
- package/packages/auth/node_modules/.bin/esbuild +0 -19
- package/packages/auth/node_modules/.bin/tsc +0 -22
- package/packages/auth/node_modules/.bin/tsserver +0 -22
- package/packages/auth/node_modules/.bin/tsup +0 -22
- package/packages/auth/node_modules/.bin/tsup-node +0 -22
- package/packages/auth/node_modules/.bin/vite +0 -22
- package/packages/auth/node_modules/.bin/vitest +0 -22
- package/packages/auth/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/auth/src/AuthAdminService.ts +0 -149
- package/packages/auth/src/AuthService.ts +0 -1166
- package/packages/auth/src/SessionManager.ts +0 -92
- package/packages/auth/src/index.ts +0 -17
- package/packages/auth/src/jwks.ts +0 -337
- package/packages/auth/src/pkce.ts +0 -27
- package/packages/auth/src/types.ts +0 -89
- package/packages/auth/tests/auth-lifecycle.test.ts +0 -149
- package/packages/auth/tests/detect-session.test.ts +0 -361
- package/packages/auth/tests/getclaims.test.ts +0 -780
- package/packages/auth/tests/is-mfa-challenge.test.ts +0 -27
- package/packages/auth/tests/password-recovery.test.ts +0 -124
- package/packages/auth/tests/pkce.test.ts +0 -192
- package/packages/auth/tests/session-manager.test.ts +0 -103
- package/packages/auth/tests/set-session.test.ts +0 -139
- package/packages/auth/tests/verify-otp.test.ts +0 -153
- package/packages/auth/tsconfig.json +0 -5
- package/packages/auth/tsup.config.ts +0 -11
- package/packages/core/node_modules/.bin/esbuild +0 -22
- package/packages/core/node_modules/.bin/tsc +0 -22
- package/packages/core/node_modules/.bin/tsserver +0 -22
- package/packages/core/node_modules/.bin/tsup +0 -22
- package/packages/core/node_modules/.bin/tsup-node +0 -22
- package/packages/core/node_modules/.bin/vite +0 -22
- package/packages/core/node_modules/.bin/vitest +0 -22
- package/packages/core/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/core/src/AuraError.ts +0 -89
- package/packages/core/src/BaseService.ts +0 -44
- package/packages/core/src/client.ts +0 -34
- package/packages/core/src/cookie-utils.ts +0 -42
- package/packages/core/src/fetch.ts +0 -435
- package/packages/core/src/index.ts +0 -22
- package/packages/core/src/retry.ts +0 -63
- package/packages/core/src/serialize.ts +0 -6
- package/packages/core/src/stores/BaseAuthStore.ts +0 -164
- package/packages/core/src/stores/LocalAuthStore.ts +0 -199
- package/packages/core/src/types.ts +0 -1042
- package/packages/core/tests/auth-store.test.ts +0 -300
- package/packages/core/tests/error-code.test.ts +0 -61
- package/packages/core/tests/error.test.ts +0 -24
- package/packages/core/tests/fetch.test.ts +0 -286
- package/packages/core/tests/mocks.ts +0 -139
- package/packages/core/tests/observability.test.ts +0 -86
- package/packages/core/tests/refresh-hardening.test.ts +0 -164
- package/packages/core/tests/refresh-observability.test.ts +0 -111
- package/packages/core/tests/retry.test.ts +0 -106
- package/packages/core/tests/serialize.test.ts +0 -12
- package/packages/core/tests/timeout.test.ts +0 -79
- package/packages/core/tsconfig.json +0 -5
- package/packages/core/tsup.config.ts +0 -11
- package/packages/db/node_modules/.bin/esbuild +0 -19
- package/packages/db/node_modules/.bin/tsc +0 -22
- package/packages/db/node_modules/.bin/tsserver +0 -22
- package/packages/db/node_modules/.bin/tsup +0 -22
- package/packages/db/node_modules/.bin/tsup-node +0 -22
- package/packages/db/node_modules/.bin/vite +0 -22
- package/packages/db/node_modules/.bin/vitest +0 -22
- package/packages/db/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/db/src/DatabaseService.ts +0 -170
- package/packages/db/src/QueryBuilder.ts +0 -830
- package/packages/db/src/index.ts +0 -2
- package/packages/db/src/pgrst-types/common/common.ts +0 -86
- package/packages/db/src/pgrst-types/common/rpc.ts +0 -151
- package/packages/db/src/pgrst-types/feature-flags.ts +0 -20
- package/packages/db/src/pgrst-types/types.ts +0 -134
- package/packages/db/src/select-query-parser/parser.ts +0 -479
- package/packages/db/src/select-query-parser/result.ts +0 -553
- package/packages/db/src/select-query-parser/types.ts +0 -132
- package/packages/db/src/select-query-parser/utils.ts +0 -707
- package/packages/db/tests/filters.test.ts +0 -81
- package/packages/db/tests/mutations-filtered.e2e.test.ts +0 -132
- package/packages/db/tests/mutations-filtered.test.ts +0 -188
- package/packages/db/tests/order.test.ts +0 -45
- package/packages/db/tests/rpc.test.ts +0 -41
- package/packages/db/tests/scale/build-large-schema.ts +0 -94
- package/packages/db/tests/scale/scale-compile.test.ts +0 -167
- package/packages/db/tests/schema.test.ts +0 -77
- package/packages/db/tests/single-accept.e2e.test.ts +0 -123
- package/packages/db/tests/single.test.ts +0 -211
- package/packages/db/tests/types/filters.test-d.ts +0 -52
- package/packages/db/tests/types/fixtures.ts +0 -40
- package/packages/db/tests/types/from-views.test-d.ts +0 -18
- package/packages/db/tests/types/mutations.test-d.ts +0 -23
- package/packages/db/tests/types/rpc.test-d.ts +0 -60
- package/packages/db/tests/types/schema-shape.test-d.ts +0 -8
- package/packages/db/tests/types/select-inference.test-d.ts +0 -38
- package/packages/db/tests/vendored/fixtures.ts +0 -83
- package/packages/db/tests/vendored/get-result.test-d.ts +0 -75
- package/packages/db/tsconfig.json +0 -5
- package/packages/db/tsconfig.test-d.json +0 -5
- package/packages/db/tsup.config.ts +0 -11
- package/packages/db/vitest.config.ts +0 -8
- package/packages/functions/node_modules/.bin/esbuild +0 -19
- package/packages/functions/node_modules/.bin/tsc +0 -22
- package/packages/functions/node_modules/.bin/tsserver +0 -22
- package/packages/functions/node_modules/.bin/tsup +0 -22
- package/packages/functions/node_modules/.bin/tsup-node +0 -22
- package/packages/functions/node_modules/.bin/vite +0 -22
- package/packages/functions/node_modules/.bin/vitest +0 -22
- package/packages/functions/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/functions/src/FunctionsService.ts +0 -188
- package/packages/functions/src/errors.ts +0 -70
- package/packages/functions/src/index.ts +0 -6
- package/packages/functions/tests/invoke.test.ts +0 -97
- package/packages/functions/tests/response-type-errors.test.ts +0 -135
- package/packages/functions/tsconfig.json +0 -5
- package/packages/functions/tsup.config.ts +0 -11
- package/packages/gen/node_modules/.bin/esbuild +0 -19
- package/packages/gen/node_modules/.bin/tsc +0 -22
- package/packages/gen/node_modules/.bin/tsserver +0 -22
- package/packages/gen/node_modules/.bin/tsup +0 -22
- package/packages/gen/node_modules/.bin/tsup-node +0 -22
- package/packages/gen/node_modules/.bin/vite +0 -22
- package/packages/gen/node_modules/.bin/vitest +0 -22
- package/packages/gen/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/gen/src/cli.ts +0 -67
- package/packages/gen/src/generate.ts +0 -0
- package/packages/gen/src/index.ts +0 -2
- package/packages/gen/tests/compile.test.ts +0 -213
- package/packages/gen/tests/e2e.test.ts +0 -130
- package/packages/gen/tests/generate.test.ts +0 -396
- package/packages/gen/tsconfig.json +0 -5
- package/packages/gen/tsup.config.ts +0 -11
- package/packages/notifications/node_modules/.bin/esbuild +0 -19
- package/packages/notifications/node_modules/.bin/tsc +0 -22
- package/packages/notifications/node_modules/.bin/tsserver +0 -22
- package/packages/notifications/node_modules/.bin/tsup +0 -22
- package/packages/notifications/node_modules/.bin/tsup-node +0 -22
- package/packages/notifications/node_modules/.bin/vite +0 -22
- package/packages/notifications/node_modules/.bin/vitest +0 -22
- package/packages/notifications/src/NotificationsService.ts +0 -230
- package/packages/notifications/src/index.ts +0 -19
- package/packages/notifications/tsconfig.json +0 -5
- package/packages/notifications/tsup.config.ts +0 -11
- package/packages/realtime/node_modules/.bin/esbuild +0 -19
- package/packages/realtime/node_modules/.bin/tsc +0 -22
- package/packages/realtime/node_modules/.bin/tsserver +0 -22
- package/packages/realtime/node_modules/.bin/tsup +0 -22
- package/packages/realtime/node_modules/.bin/tsup-node +0 -22
- package/packages/realtime/node_modules/.bin/vite +0 -22
- package/packages/realtime/node_modules/.bin/vitest +0 -22
- package/packages/realtime/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/realtime/src/AuraChannel.ts +0 -485
- package/packages/realtime/src/HeartbeatManager.ts +0 -73
- package/packages/realtime/src/RealtimeService.ts +0 -505
- package/packages/realtime/src/columnFilter.ts +0 -301
- package/packages/realtime/src/index.ts +0 -4
- package/packages/realtime/tests/ack-routing.test.ts +0 -81
- package/packages/realtime/tests/column-filter.test.ts +0 -144
- package/packages/realtime/tests/dispatch.test.ts +0 -191
- package/packages/realtime/tests/heartbeat.test.ts +0 -67
- package/packages/realtime/tests/http-broadcast.test.ts +0 -68
- package/packages/realtime/tests/realtime-token.test.ts +0 -43
- package/packages/realtime/tests/reconnect-race.test.ts +0 -149
- package/packages/realtime/tests/ref-id.test.ts +0 -34
- package/packages/realtime/tests/set-auth.test.ts +0 -70
- package/packages/realtime/tsconfig.json +0 -5
- package/packages/realtime/tsup.config.ts +0 -11
- package/packages/storage/node_modules/.bin/esbuild +0 -19
- package/packages/storage/node_modules/.bin/tsc +0 -22
- package/packages/storage/node_modules/.bin/tsserver +0 -22
- package/packages/storage/node_modules/.bin/tsup +0 -22
- package/packages/storage/node_modules/.bin/tsup-node +0 -22
- package/packages/storage/node_modules/.bin/vite +0 -22
- package/packages/storage/node_modules/.bin/vitest +0 -22
- package/packages/storage/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/storage/src/DownloadBuilder.ts +0 -60
- package/packages/storage/src/StorageService.ts +0 -323
- package/packages/storage/src/index.ts +0 -2
- package/packages/storage/src/path.ts +0 -35
- package/packages/storage/src/transform.ts +0 -15
- package/packages/storage/tests/download.test.ts +0 -64
- package/packages/storage/tests/parity.test.ts +0 -154
- package/packages/storage/tests/path.test.ts +0 -87
- package/packages/storage/tests/transform.test.ts +0 -79
- package/packages/storage/tsconfig.json +0 -5
- package/packages/storage/tsup.config.ts +0 -11
- package/pnpm-workspace.yaml +0 -24
- package/tsconfig.base.json +0 -15
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// DatabaseService CRUD tests
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
6
|
-
import { AuraClient } from '../src/AuraClient'
|
|
7
|
-
import { FetchMock, fakeTokenPair } from './mocks'
|
|
8
|
-
|
|
9
|
-
const BASE = 'http://localhost:8080'
|
|
10
|
-
const PROJECT = 'test-project'
|
|
11
|
-
const TABLE_URL = `${BASE}/v1/db/${PROJECT}/posts`
|
|
12
|
-
|
|
13
|
-
let fetchMock: FetchMock
|
|
14
|
-
let client: AuraClient
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
fetchMock = new FetchMock()
|
|
18
|
-
fetchMock.init()
|
|
19
|
-
client = new AuraClient(BASE, PROJECT, {
|
|
20
|
-
auth: { persistSession: false },
|
|
21
|
-
})
|
|
22
|
-
// Pre-populate so fetchWithAuth does not attempt a refresh
|
|
23
|
-
const tp = fakeTokenPair()
|
|
24
|
-
client.authStore.save(tp.access_token, tp.refresh_token, tp.user)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
afterEach(() => {
|
|
28
|
-
fetchMock.restore()
|
|
29
|
-
fetchMock.clearMocks()
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
describe('SELECT (list with filters)', () => {
|
|
34
|
-
it('fetches rows with eq filter', async () => {
|
|
35
|
-
const rows = [
|
|
36
|
-
{ id: '1', title: 'A', published: true },
|
|
37
|
-
{ id: '2', title: 'B', published: true },
|
|
38
|
-
]
|
|
39
|
-
fetchMock.on({
|
|
40
|
-
method: 'GET',
|
|
41
|
-
url: /\/posts\?/,
|
|
42
|
-
replyCode: 200,
|
|
43
|
-
replyBody: { data: rows, meta: { total: 2 } },
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const res = await client.db
|
|
47
|
-
.from('posts')
|
|
48
|
-
.eq('published', true)
|
|
49
|
-
.limit(10)
|
|
50
|
-
|
|
51
|
-
expect(res.error).toBeNull()
|
|
52
|
-
expect(res.data).toHaveLength(2)
|
|
53
|
-
expect(res.meta?.total).toBe(2)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
it('returns empty array when no rows', async () => {
|
|
57
|
-
fetchMock.on({
|
|
58
|
-
method: 'GET',
|
|
59
|
-
url: /\/posts/,
|
|
60
|
-
replyCode: 200,
|
|
61
|
-
replyBody: { data: [] },
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
const res = await client.db.from('posts').eq('id', 'none')
|
|
65
|
-
|
|
66
|
-
expect(res.error).toBeNull()
|
|
67
|
-
expect(res.data).toEqual([])
|
|
68
|
-
})
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
describe('GET single record', () => {
|
|
73
|
-
it('fetches a single record via single()', async () => {
|
|
74
|
-
fetchMock.on({
|
|
75
|
-
method: 'GET',
|
|
76
|
-
url: /\/posts\?/,
|
|
77
|
-
replyCode: 200,
|
|
78
|
-
replyBody: { data: [{ id: '1', title: 'Hello' }] },
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
const res = await client.db.from('posts').eq('id', '1').single()
|
|
82
|
-
|
|
83
|
-
expect(res.error).toBeNull()
|
|
84
|
-
expect(res.data).toEqual({ id: '1', title: 'Hello' })
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
describe('INSERT', () => {
|
|
90
|
-
it('inserts a single record', async () => {
|
|
91
|
-
const newPost = { id: '3', title: 'New Post' }
|
|
92
|
-
fetchMock.on({
|
|
93
|
-
method: 'POST',
|
|
94
|
-
url: TABLE_URL,
|
|
95
|
-
replyCode: 201,
|
|
96
|
-
replyBody: { data: newPost },
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
const res = await client.db
|
|
100
|
-
.from('posts')
|
|
101
|
-
.insert({ title: 'New Post' })
|
|
102
|
-
|
|
103
|
-
expect(res.error).toBeNull()
|
|
104
|
-
expect(res.data).toBeDefined()
|
|
105
|
-
// DB-F3 (parité postgrest-js) : `insert()` renvoie TOUJOURS un tableau `Row[]`,
|
|
106
|
-
// même sur une ligne unique (le gateway déballe le 201 mono-ligne en objet, le
|
|
107
|
-
// SDK le re-emballe). Avant : objet nu (divergence).
|
|
108
|
-
expect(Array.isArray(res.data)).toBe(true)
|
|
109
|
-
expect((res.data as any)[0].title).toBe('New Post')
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('inserts a batch of records', async () => {
|
|
113
|
-
const batch = [
|
|
114
|
-
{ id: '4', title: 'A' },
|
|
115
|
-
{ id: '5', title: 'B' },
|
|
116
|
-
]
|
|
117
|
-
fetchMock.on({
|
|
118
|
-
method: 'POST',
|
|
119
|
-
url: TABLE_URL,
|
|
120
|
-
replyCode: 201,
|
|
121
|
-
replyBody: { data: batch },
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
const res = await client.db
|
|
125
|
-
.from('posts')
|
|
126
|
-
.insert([{ title: 'A' }, { title: 'B' }])
|
|
127
|
-
|
|
128
|
-
expect(res.error).toBeNull()
|
|
129
|
-
expect(Array.isArray(res.data)).toBe(true)
|
|
130
|
-
expect((res.data as any[]).length).toBe(2)
|
|
131
|
-
})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
// ---------------------------------------------------------------------------
|
|
135
|
-
describe('UPDATE (PATCH)', () => {
|
|
136
|
-
it('updates a record by id', async () => {
|
|
137
|
-
const updated = { id: '1', title: 'Updated' }
|
|
138
|
-
fetchMock.on({
|
|
139
|
-
method: 'PATCH',
|
|
140
|
-
url: `${TABLE_URL}/1`,
|
|
141
|
-
replyCode: 200,
|
|
142
|
-
replyBody: { data: updated },
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const res = await client.db
|
|
146
|
-
.from('posts')
|
|
147
|
-
.update('1', { title: 'Updated' })
|
|
148
|
-
|
|
149
|
-
expect(res.error).toBeNull()
|
|
150
|
-
expect((res.data as any).title).toBe('Updated')
|
|
151
|
-
})
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
// ---------------------------------------------------------------------------
|
|
155
|
-
describe('DELETE', () => {
|
|
156
|
-
it('deletes a record by id', async () => {
|
|
157
|
-
fetchMock.on({
|
|
158
|
-
method: 'DELETE',
|
|
159
|
-
url: `${TABLE_URL}/1`,
|
|
160
|
-
replyCode: 204,
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
const res = await client.db.from('posts').delete('1')
|
|
164
|
-
|
|
165
|
-
expect(res.error).toBeNull()
|
|
166
|
-
})
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
// ---------------------------------------------------------------------------
|
|
170
|
-
describe('Raw SQL', () => {
|
|
171
|
-
it('executes raw SQL via POST /raw', async () => {
|
|
172
|
-
const rows = [{ count: 42 }]
|
|
173
|
-
fetchMock.on({
|
|
174
|
-
method: 'POST',
|
|
175
|
-
url: `${BASE}/v1/db/${PROJECT}/raw`,
|
|
176
|
-
replyCode: 200,
|
|
177
|
-
replyBody: { data: rows },
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
const res = await client.db.sql('SELECT count(*) FROM posts')
|
|
181
|
-
|
|
182
|
-
expect(res.error).toBeNull()
|
|
183
|
-
expect(res.data).toEqual([{ count: 42 }])
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('returns error for invalid SQL', async () => {
|
|
187
|
-
fetchMock.on({
|
|
188
|
-
method: 'POST',
|
|
189
|
-
url: `${BASE}/v1/db/${PROJECT}/raw`,
|
|
190
|
-
replyCode: 400,
|
|
191
|
-
replyBody: { error: { code: 'sql_error', message: 'syntax error' } },
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
const res = await client.db.sql('SELEC invalid')
|
|
195
|
-
|
|
196
|
-
expect(res.error).toBeDefined()
|
|
197
|
-
expect(res.error!.code).toBe('sql_error')
|
|
198
|
-
})
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
// ---------------------------------------------------------------------------
|
|
202
|
-
describe('Schema', () => {
|
|
203
|
-
it('fetches schema information', async () => {
|
|
204
|
-
const tables = [
|
|
205
|
-
{
|
|
206
|
-
name: 'posts',
|
|
207
|
-
columns: [
|
|
208
|
-
{ name: 'id', data_type: 'uuid', is_nullable: false, is_primary_key: true },
|
|
209
|
-
{ name: 'title', data_type: 'text', is_nullable: false, is_primary_key: false },
|
|
210
|
-
],
|
|
211
|
-
},
|
|
212
|
-
]
|
|
213
|
-
// Le backend renvoie un SchemaDefinition `{ project_id, engine, tables, functions }` ;
|
|
214
|
-
// `db.schema()` expose `{ tables, functions }`.
|
|
215
|
-
fetchMock.on({
|
|
216
|
-
method: 'GET',
|
|
217
|
-
url: `${BASE}/v1/db/${PROJECT}/schema`,
|
|
218
|
-
replyCode: 200,
|
|
219
|
-
replyBody: { data: { project_id: PROJECT, engine: 'postgres', tables } },
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
const res = await client.db.schema()
|
|
223
|
-
|
|
224
|
-
expect(res.error).toBeNull()
|
|
225
|
-
expect(res.data?.tables).toHaveLength(1)
|
|
226
|
-
expect(res.data?.tables[0].name).toBe('posts')
|
|
227
|
-
expect(res.data?.tables[0].columns).toHaveLength(2)
|
|
228
|
-
expect(res.data?.functions).toEqual([])
|
|
229
|
-
})
|
|
230
|
-
})
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// E2E réel typé (stack live) — Phase 2 Task 7
|
|
3
|
-
//
|
|
4
|
-
// Preuve bout-en-bout que le typage généré (`@aurabase/gen`) pilote une vraie
|
|
5
|
-
// inférence via `createClient<Database>` :
|
|
6
|
-
// 1. crée 2 tables liées par FK (authors 1—N books) via SQL brut ;
|
|
7
|
-
// 2. lit le schéma réel et génère `Database` avec `generateDatabaseTypes` ;
|
|
8
|
-
// 3. écrit ce `Database` généré + un "probe" TS consommant
|
|
9
|
-
// `createClient<Database>(...).db.from('authors').select('id, books(title)').single()`
|
|
10
|
-
// dans un dossier temporaire sous `tests/types/` ;
|
|
11
|
-
// 4. compile ce probe avec le pipeline `tsc` du package
|
|
12
|
-
// (`tsconfig.test-d.json`, mêmes options strictes que `npm run test:types`) —
|
|
13
|
-
// si l'inférence casse, la compilation échoue ;
|
|
14
|
-
// 5. exécute une vraie requête (même forme) contre le backend live et vérifie
|
|
15
|
-
// la forme runtime réelle des données ;
|
|
16
|
-
// 6. nettoie (drop tables + fichiers temporaires) en `finally`.
|
|
17
|
-
//
|
|
18
|
-
// Gaté par RUN_E2E + credentials — squelette calqué sur
|
|
19
|
-
// `packages/gen/tests/e2e.test.ts` (provisioning-free, env-based) et
|
|
20
|
-
// `packages/gen/tests/compile.test.ts` (mécanique de compilation tsc).
|
|
21
|
-
//
|
|
22
|
-
// Approche de résolution du probe : import RELATIF vers `../../../src/index`
|
|
23
|
-
// (source, pas le package `aurabase` construit) — cf. `tests/types/*.test-d.ts`
|
|
24
|
-
// qui font déjà exactement ça. Le probe est nommé `*.test-d.ts` et placé sous
|
|
25
|
-
// `tests/types/` pour être ramassé par le même `tsconfig.test-d.json` que les
|
|
26
|
-
// tests de type existants (include: `tests/**/*.test-d.ts`), ce qui réutilise
|
|
27
|
-
// telles quelles les options de compilation strictes du package (strict,
|
|
28
|
-
// moduleResolution bundler, lib ES2022+DOM) sans avoir à les dupliquer sur la
|
|
29
|
-
// ligne de commande — plus robuste que d'assembler des flags `tsc` ad hoc.
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
|
|
32
|
-
import { describe, it, expect } from 'vitest'
|
|
33
|
-
import { execFileSync } from 'node:child_process'
|
|
34
|
-
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'
|
|
35
|
-
import { join, dirname, relative, sep } from 'node:path'
|
|
36
|
-
import { fileURLToPath } from 'node:url'
|
|
37
|
-
import { createClient } from '../src/index'
|
|
38
|
-
import type { AuraResponse } from '../src/index'
|
|
39
|
-
import { generateDatabaseTypes } from '../../gen/src/generate'
|
|
40
|
-
|
|
41
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
42
|
-
const PACKAGE_DIR = join(__dirname, '..')
|
|
43
|
-
|
|
44
|
-
const URL = process.env.AURA_E2E_URL
|
|
45
|
-
const PROJECT = process.env.AURA_E2E_PROJECT
|
|
46
|
-
const KEY = process.env.AURA_E2E_KEY
|
|
47
|
-
const RUN = Boolean(process.env.RUN_E2E && URL && PROJECT && KEY)
|
|
48
|
-
|
|
49
|
-
/** Chemin relatif POSIX, normalisé en spécificateur d'import (`./x` / `../x`). */
|
|
50
|
-
function relativeImport(fromDir: string, toFileNoExt: string): string {
|
|
51
|
-
const rel = relative(fromDir, toFileNoExt).split(sep).join('/')
|
|
52
|
-
return rel.startsWith('.') ? rel : `./${rel}`
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Rejoue une requête tant qu'elle renvoie un 503 (reload/indispo PostgREST, transitoire),
|
|
56
|
-
* borné. Renvoie la dernière réponse — le caller décide de skip si elle est encore 503. */
|
|
57
|
-
async function retry503<T>(fn: () => Promise<AuraResponse<T>>): Promise<AuraResponse<T>> {
|
|
58
|
-
let res = await fn()
|
|
59
|
-
for (let i = 0; i < 20 && res.error?.status === 503; i++) {
|
|
60
|
-
await new Promise((r) => setTimeout(r, 1000))
|
|
61
|
-
res = await fn()
|
|
62
|
-
}
|
|
63
|
-
return res
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
describe.runIf(RUN)('db types E2E — schéma réel → génère → createClient<Database> compile + requête typée', () => {
|
|
67
|
-
it(
|
|
68
|
-
'crée authors/books (FK), génère Database, compile un probe typé (tsc), exécute une requête réelle typée',
|
|
69
|
-
async () => {
|
|
70
|
-
const client = createClient(URL!, PROJECT!, KEY!)
|
|
71
|
-
const suffix = process.env.AURA_E2E_SUFFIX ?? 'dbtypes'
|
|
72
|
-
const authors = `e2e_authors_${suffix}`
|
|
73
|
-
const books = `e2e_books_${suffix}`
|
|
74
|
-
|
|
75
|
-
// Nettoyage + création via SQL brut (service_role requis).
|
|
76
|
-
await client.db.sql(`DROP TABLE IF EXISTS ${books}`)
|
|
77
|
-
await client.db.sql(`DROP TABLE IF EXISTS ${authors}`)
|
|
78
|
-
await client.db.sql(
|
|
79
|
-
`CREATE TABLE ${authors} (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL)`,
|
|
80
|
-
)
|
|
81
|
-
await client.db.sql(
|
|
82
|
-
`CREATE TABLE ${books} (id uuid PRIMARY KEY DEFAULT gen_random_uuid(), author_id uuid NOT NULL REFERENCES ${authors}(id), title text NOT NULL)`,
|
|
83
|
-
)
|
|
84
|
-
// Les tables créées en SQL brut ne sont PAS accessibles à la couche CRUD/embed
|
|
85
|
-
// PostgREST tant que (a) les GRANTs ne sont pas posés et (b) le cache de schéma
|
|
86
|
-
// n'est pas rechargé. Approximation large (GRANT ALL TO PUBLIC) suffisante pour
|
|
87
|
-
// débloquer la couche CRUD/embed sur ces tables de test jetables — le chemin
|
|
88
|
-
// console applique des GRANTs plus fins par rôle (aura_anon/aura_authenticated/
|
|
89
|
-
// aura_service_role), pas un blanket ALL TO PUBLIC.
|
|
90
|
-
await client.db.sql(`GRANT ALL ON ${authors}, ${books} TO PUBLIC`)
|
|
91
|
-
await client.db.sql(`SELECT pg_notify('pgrst', 'reload schema')`)
|
|
92
|
-
|
|
93
|
-
let probeDir: string | null = null
|
|
94
|
-
try {
|
|
95
|
-
// -- 1. Schéma réel → types générés -----------------------------------
|
|
96
|
-
const { data: schemaData, error: schemaError } = await client.db.schema()
|
|
97
|
-
expect(schemaError).toBeNull()
|
|
98
|
-
const tables = (schemaData?.tables ?? []).filter((t) => t.name === authors || t.name === books)
|
|
99
|
-
expect(tables.length).toBe(2)
|
|
100
|
-
|
|
101
|
-
const typesOut = generateDatabaseTypes(tables)
|
|
102
|
-
expect(typesOut).toContain(`referencedRelation: '${authors}'`)
|
|
103
|
-
expect(typesOut).toContain(`columns: ['author_id']`)
|
|
104
|
-
|
|
105
|
-
// -- 2. Écrit database.types.ts + probe.test-d.ts dans un dossier temp --
|
|
106
|
-
// sous tests/types/, ramassé par tsconfig.test-d.json (include:
|
|
107
|
-
// "tests/**/*.test-d.ts"), nettoyé dans `finally`.
|
|
108
|
-
probeDir = mkdtempSync(join(__dirname, 'types', '.e2e-probe-'))
|
|
109
|
-
const typesFile = join(probeDir, 'database.types.ts')
|
|
110
|
-
writeFileSync(typesFile, typesOut)
|
|
111
|
-
|
|
112
|
-
const probeFile = join(probeDir, 'probe.test-d.ts')
|
|
113
|
-
const createClientImport = relativeImport(probeDir, join(PACKAGE_DIR, 'src', 'index'))
|
|
114
|
-
writeFileSync(
|
|
115
|
-
probeFile,
|
|
116
|
-
[
|
|
117
|
-
`// Fichier généré par tests/db-types-e2e.test.ts — ne pas committer, nettoyé en finally.`,
|
|
118
|
-
`import { createClient } from '${createClientImport}'`,
|
|
119
|
-
`import type { Database } from './database.types'`,
|
|
120
|
-
``,
|
|
121
|
-
`async function probe() {`,
|
|
122
|
-
` const c = createClient<Database>('${URL}', '${PROJECT}', '${KEY}')`,
|
|
123
|
-
` const { data } = await c.db.from('${authors}').select('id, ${books}(title)').single()`,
|
|
124
|
-
` // Assertion de type : si l'inférence casse (colonnes/embed FK), ceci ne compile plus.`,
|
|
125
|
-
` if (data) {`,
|
|
126
|
-
` const id: string = data.id`,
|
|
127
|
-
` const relatedTitles: string[] = data['${books}'].map((b) => b.title)`,
|
|
128
|
-
` void id`,
|
|
129
|
-
` void relatedTitles`,
|
|
130
|
-
` }`,
|
|
131
|
-
` return data`,
|
|
132
|
-
`}`,
|
|
133
|
-
``,
|
|
134
|
-
`void probe`,
|
|
135
|
-
``,
|
|
136
|
-
].join('\n'),
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
// -- 3. Compile le probe avec le pipeline tsc du package (mêmes options
|
|
140
|
-
// strictes que `npm run test:types`) — preuve que les types générés
|
|
141
|
-
// pilotent réellement l'inférence de createClient<Database>.
|
|
142
|
-
expect(() =>
|
|
143
|
-
execFileSync('npx', ['tsc', '--noEmit', '-p', 'tsconfig.test-d.json'], {
|
|
144
|
-
cwd: PACKAGE_DIR,
|
|
145
|
-
stdio: 'pipe',
|
|
146
|
-
}),
|
|
147
|
-
).not.toThrow()
|
|
148
|
-
|
|
149
|
-
// -- 4. Requête runtime réelle (BEST-EFFORT) : valide la forme runtime SI le
|
|
150
|
-
// backend est disponible. La PREUVE du livrable Phase 2 est la compilation
|
|
151
|
-
// typée ci-dessus (étape 3) ; la disponibilité runtime du backend est
|
|
152
|
-
// orthogonale. Le reload du cache PostgREST (pg_notify) renvoie des 503
|
|
153
|
-
// transitoires (« schema cache. Retrying. » / service indisponible) — on
|
|
154
|
-
// rejoue brièvement pour CHAQUE appel runtime (insert authors, insert books,
|
|
155
|
-
// select embed final), et si le backend reste en 503 on n'échoue PAS (skip
|
|
156
|
-
// gracieux avec avertissement), car ce n'est pas un défaut du typage.
|
|
157
|
-
const insertedAuthor = await retry503(() =>
|
|
158
|
-
client.db.from(authors).insert({ name: 'Ada Lovelace' }).select('id').single(),
|
|
159
|
-
)
|
|
160
|
-
if (insertedAuthor.error?.status === 503) {
|
|
161
|
-
// eslint-disable-next-line no-console
|
|
162
|
-
console.warn(
|
|
163
|
-
'[db-types-e2e] backend en 503 (reload/indisponible) après retries — ' +
|
|
164
|
-
'assertion runtime ignorée. La preuve de compilation typée (étape 3) a réussi.',
|
|
165
|
-
)
|
|
166
|
-
return
|
|
167
|
-
}
|
|
168
|
-
expect(insertedAuthor.error).toBeNull()
|
|
169
|
-
const authorId = (insertedAuthor.data as { id: string } | null)?.id
|
|
170
|
-
expect(authorId).toBeTruthy()
|
|
171
|
-
|
|
172
|
-
const insertedBook = await retry503(() =>
|
|
173
|
-
client.db.from(books).insert({ author_id: authorId, title: 'Notes on the Analytical Engine' }),
|
|
174
|
-
)
|
|
175
|
-
if (insertedBook.error?.status === 503) {
|
|
176
|
-
// eslint-disable-next-line no-console
|
|
177
|
-
console.warn(
|
|
178
|
-
'[db-types-e2e] backend en 503 (reload/indisponible) après retries sur insert books — ' +
|
|
179
|
-
'assertion runtime ignorée. La preuve de compilation typée (étape 3) a réussi.',
|
|
180
|
-
)
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
-
expect(insertedBook.error).toBeNull()
|
|
184
|
-
|
|
185
|
-
const { data: real, error: realError } = await retry503(() =>
|
|
186
|
-
client.db
|
|
187
|
-
.from(authors)
|
|
188
|
-
.select(`id, ${books}(title)`)
|
|
189
|
-
.eq('id', authorId!)
|
|
190
|
-
.single(),
|
|
191
|
-
)
|
|
192
|
-
if (realError?.status === 503) {
|
|
193
|
-
// eslint-disable-next-line no-console
|
|
194
|
-
console.warn(
|
|
195
|
-
'[db-types-e2e] backend en 503 (reload/indisponible) après retries sur select embed — ' +
|
|
196
|
-
'assertion runtime ignorée. La preuve de compilation typée (étape 3) a réussi.',
|
|
197
|
-
)
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
expect(realError).toBeNull()
|
|
202
|
-
expect(real).not.toBeNull()
|
|
203
|
-
const row = real as { id: string; [key: string]: unknown }
|
|
204
|
-
expect(row.id).toBe(authorId)
|
|
205
|
-
const relatedBooks = row[books] as Array<{ title: string }>
|
|
206
|
-
expect(Array.isArray(relatedBooks)).toBe(true)
|
|
207
|
-
expect(relatedBooks.length).toBe(1)
|
|
208
|
-
expect(relatedBooks[0]!.title).toBe('Notes on the Analytical Engine')
|
|
209
|
-
} finally {
|
|
210
|
-
if (probeDir) rmSync(probeDir, { recursive: true, force: true })
|
|
211
|
-
await client.db.sql(`DROP TABLE IF EXISTS ${books}`)
|
|
212
|
-
await client.db.sql(`DROP TABLE IF EXISTS ${authors}`)
|
|
213
|
-
}
|
|
214
|
-
},
|
|
215
|
-
120_000,
|
|
216
|
-
)
|
|
217
|
-
})
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, afterEach } from 'vitest'
|
|
2
|
-
import { createClient } from '../src/index'
|
|
3
|
-
|
|
4
|
-
afterEach(() => vi.useRealTimers())
|
|
5
|
-
|
|
6
|
-
describe('AuraClient.dispose()', () => {
|
|
7
|
-
it('est idempotent et arrête le realtime', () => {
|
|
8
|
-
const c = createClient('http://localhost:8080', 'p', 'k')
|
|
9
|
-
const spy = vi.spyOn(c.realtime, 'disconnect')
|
|
10
|
-
c.dispose()
|
|
11
|
-
c.dispose() // 2e appel sans erreur
|
|
12
|
-
expect(spy).toHaveBeenCalled()
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
it('après dispose, onAuthStateChange ne reçoit plus rien', async () => {
|
|
16
|
-
const c = createClient('http://localhost:8080', 'p', 'k')
|
|
17
|
-
const cb = vi.fn()
|
|
18
|
-
c.auth.onAuthStateChange(cb)
|
|
19
|
-
c.dispose()
|
|
20
|
-
await Promise.resolve() // flush le microtask INITIAL_SESSION
|
|
21
|
-
expect(cb).not.toHaveBeenCalled()
|
|
22
|
-
})
|
|
23
|
-
})
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// E2E réel LOT D — getClaims/getUser d'un token ARBITRAIRE du même projet
|
|
3
|
-
// (foreign-token), contre la stack live (gateway :8080).
|
|
4
|
-
//
|
|
5
|
-
// Prouve en conditions réelles le déblocage du fallback foreign-token :
|
|
6
|
-
// 1. Enregistre 2 users A et B du MÊME projet (anon key).
|
|
7
|
-
// 2. Depuis un client dont le STORE porte le token de A, appelle
|
|
8
|
-
// `getClaims(tokenB)` → le backend valide le token de B (présenté via
|
|
9
|
-
// l'override `authToken`, PAS celui du store A) et renvoie SON identité :
|
|
10
|
-
// `data.claims.sub === userB.id`. Preuve anti-misattribution : c'est bien
|
|
11
|
-
// le token étranger qui est validé, pas la session courante.
|
|
12
|
-
// 3. Un JWT forgé/invalide → `error` (le backend refuse) ; jamais de claims
|
|
13
|
-
// fabriquées.
|
|
14
|
-
//
|
|
15
|
-
// Le JWKS de la stack locale est vide (HS256) → `getClaims` passe par le chemin
|
|
16
|
-
// FALLBACK `getUser(token)` (pas la vérif locale JWKS/WebCrypto). L'isolation
|
|
17
|
-
// projet est native côté gateway (un token d'un autre projet → 403).
|
|
18
|
-
//
|
|
19
|
-
// Gaté par RUN_E2E + credentials (URL/PROJECT/ANON) : `describe.runIf(RUN)`
|
|
20
|
-
// SKIPPE proprement la suite entière quand l'env manque (vérifié sans RUN_E2E
|
|
21
|
-
// via `pnpm --filter aurabase test`). Cleanup best-effort (deleteUser si une
|
|
22
|
-
// clé service_role est fournie via AURA_E2E_SERVICE_KEY).
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
|
|
25
|
-
import { describe, it, expect } from 'vitest'
|
|
26
|
-
import { createClient, createAdminClient } from '../src/index'
|
|
27
|
-
import type { AuraResponse } from '../src/index'
|
|
28
|
-
|
|
29
|
-
const URL = process.env.AURA_E2E_URL
|
|
30
|
-
const PROJECT = process.env.AURA_E2E_PROJECT
|
|
31
|
-
const ANON = process.env.AURA_E2E_ANON_KEY
|
|
32
|
-
const SERVICE = process.env.AURA_E2E_SERVICE_KEY
|
|
33
|
-
const RUN = Boolean(process.env.RUN_E2E && URL && PROJECT && ANON)
|
|
34
|
-
|
|
35
|
-
/** Statuts transitoires observés sur la stack locale (timeout gateway→service sous charge). */
|
|
36
|
-
const TRANSIENT_STATUS = new Set([408, 429, 502, 503, 504])
|
|
37
|
-
|
|
38
|
-
/** Rejoue une requête tant qu'elle renvoie un statut transitoire, borné. */
|
|
39
|
-
async function retryTransient<T>(fn: () => Promise<AuraResponse<T>>): Promise<AuraResponse<T>> {
|
|
40
|
-
let res = await fn()
|
|
41
|
-
for (let i = 0; i < 10 && res.error && TRANSIENT_STATUS.has(res.error.status); i++) {
|
|
42
|
-
await new Promise((r) => setTimeout(r, 1000))
|
|
43
|
-
res = await fn()
|
|
44
|
-
}
|
|
45
|
-
return res
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** b64url d'un objet JSON (sans padding). */
|
|
49
|
-
function b64url(obj: unknown): string {
|
|
50
|
-
return Buffer.from(JSON.stringify(obj)).toString('base64url')
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
describe.runIf(RUN)('foreign-token E2E réel — getClaims(tokenB) validé serveur (LOT D)', () => {
|
|
54
|
-
it(
|
|
55
|
-
'2 users même projet : client(store=A).getClaims(tokenB) → claims.sub===userB.id ; JWT forgé → error',
|
|
56
|
-
async () => {
|
|
57
|
-
const clientA = createClient(URL!, PROJECT!, ANON!)
|
|
58
|
-
const clientB = createClient(URL!, PROJECT!, ANON!)
|
|
59
|
-
const admin = SERVICE ? createAdminClient(URL!, PROJECT!, SERVICE) : null
|
|
60
|
-
const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
|
|
61
|
-
const emailA = `foreign-a-${suffix}@aurabase.dev`
|
|
62
|
-
const emailB = `foreign-b-${suffix}@aurabase.dev`
|
|
63
|
-
const password = 'EndUserPassword123!'
|
|
64
|
-
|
|
65
|
-
let userIdA: string | null = null
|
|
66
|
-
let userIdB: string | null = null
|
|
67
|
-
try {
|
|
68
|
-
// -- 1. register + login des 2 users (même projet, anon key) ----------
|
|
69
|
-
await retryTransient(() => clientA.auth.signUp({ email: emailA, password }))
|
|
70
|
-
const loginA = await retryTransient(() =>
|
|
71
|
-
clientA.auth.signInWithPassword({ email: emailA, password }),
|
|
72
|
-
)
|
|
73
|
-
expect(loginA.error).toBeNull()
|
|
74
|
-
const dataA = loginA.data as { access_token: string; user: { id: string } }
|
|
75
|
-
userIdA = dataA.user.id
|
|
76
|
-
|
|
77
|
-
await retryTransient(() => clientB.auth.signUp({ email: emailB, password }))
|
|
78
|
-
const loginB = await retryTransient(() =>
|
|
79
|
-
clientB.auth.signInWithPassword({ email: emailB, password }),
|
|
80
|
-
)
|
|
81
|
-
expect(loginB.error).toBeNull()
|
|
82
|
-
const dataB = loginB.data as { access_token: string; user: { id: string } }
|
|
83
|
-
userIdB = dataB.user.id
|
|
84
|
-
const tokenB = dataB.access_token
|
|
85
|
-
expect(userIdA).toBeTruthy()
|
|
86
|
-
expect(userIdB).toBeTruthy()
|
|
87
|
-
expect(userIdA).not.toBe(userIdB)
|
|
88
|
-
|
|
89
|
-
// Le store de clientA porte bien le token de A.
|
|
90
|
-
const { accessToken: storeA } = await clientA.auth.getSession()
|
|
91
|
-
expect(storeA).toBe(dataA.access_token)
|
|
92
|
-
|
|
93
|
-
// -- 2. getClaims(tokenB) depuis clientA (store=A) --------------------
|
|
94
|
-
// Le token ÉTRANGER de B est validé côté serveur (présenté via authToken),
|
|
95
|
-
// PAS le token du store A → claims.sub === userB.id (anti-misattribution).
|
|
96
|
-
const claimsB = await retryTransient(() => clientA.auth.getClaims(tokenB))
|
|
97
|
-
expect(claimsB.error).toBeNull()
|
|
98
|
-
expect(claimsB.data?.claims.sub).toBe(userIdB)
|
|
99
|
-
expect(claimsB.data?.claims.sub).not.toBe(userIdA)
|
|
100
|
-
|
|
101
|
-
// Le store de A n'a PAS été altéré par la validation du token étranger.
|
|
102
|
-
const { accessToken: storeStill } = await clientA.auth.getSession()
|
|
103
|
-
expect(storeStill).toBe(dataA.access_token)
|
|
104
|
-
|
|
105
|
-
// -- 3. JWT forgé/invalide → error (jamais de claims fabriquées) ------
|
|
106
|
-
const forged = `${b64url({ alg: 'HS256', typ: 'JWT' })}.${b64url({
|
|
107
|
-
sub: 'attacker',
|
|
108
|
-
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
109
|
-
})}.not-a-valid-signature`
|
|
110
|
-
const claimsForged = await clientA.auth.getClaims(forged)
|
|
111
|
-
expect(claimsForged.data).toBeNull()
|
|
112
|
-
expect(claimsForged.error).not.toBeNull()
|
|
113
|
-
} finally {
|
|
114
|
-
if (admin) {
|
|
115
|
-
for (const id of [userIdA, userIdB]) {
|
|
116
|
-
if (id) {
|
|
117
|
-
try {
|
|
118
|
-
await admin.authAdmin.deleteUser(id)
|
|
119
|
-
} catch {
|
|
120
|
-
// best-effort cleanup
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
120_000,
|
|
128
|
-
)
|
|
129
|
-
})
|