@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,210 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, afterEach, vi } from 'vitest'
|
|
2
|
-
import { refreshOnce, BaseAuthStore } from '@aurabase/core'
|
|
3
|
-
import type { AuraClientLike, User } from '@aurabase/core'
|
|
4
|
-
|
|
5
|
-
function fakeUser(id = 'u1'): User {
|
|
6
|
-
return {
|
|
7
|
-
id,
|
|
8
|
-
email: 't@e.com',
|
|
9
|
-
role: 'authenticated',
|
|
10
|
-
email_verified: true,
|
|
11
|
-
is_banned: false,
|
|
12
|
-
is_anonymous: false,
|
|
13
|
-
mfa_enabled: false,
|
|
14
|
-
user_metadata: {},
|
|
15
|
-
app_metadata: {},
|
|
16
|
-
created_at: '2025-01-01T00:00:00Z',
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function b64url(obj: unknown): string {
|
|
20
|
-
return btoa(JSON.stringify(obj)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
|
|
21
|
-
}
|
|
22
|
-
function jwt(sub = 'u1', ttlSec = 3600): string {
|
|
23
|
-
const exp = Math.floor(Date.now() / 1000) + ttlSec
|
|
24
|
-
return `${b64url({ alg: 'HS256', typ: 'JWT' })}.${b64url({ sub, exp })}.sig`
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// -- navigator.locks install/uninstall (Node peut définir navigator en lecture seule) --
|
|
28
|
-
// Les Web Locks ne s'activent qu'en contexte NAVIGATEUR (gate `window` dans
|
|
29
|
-
// withRefreshLock) : on simule donc aussi `globalThis.window` le temps du test.
|
|
30
|
-
let hadNavigator = false
|
|
31
|
-
let originalNavigator: PropertyDescriptor | undefined
|
|
32
|
-
let hadWindow = false
|
|
33
|
-
function installLocks(request: unknown): void {
|
|
34
|
-
hadNavigator = 'navigator' in globalThis
|
|
35
|
-
originalNavigator = Object.getOwnPropertyDescriptor(globalThis, 'navigator')
|
|
36
|
-
Object.defineProperty(globalThis, 'navigator', {
|
|
37
|
-
value: { locks: { request } },
|
|
38
|
-
configurable: true,
|
|
39
|
-
writable: true,
|
|
40
|
-
})
|
|
41
|
-
hadWindow = 'window' in globalThis
|
|
42
|
-
if (!hadWindow) {
|
|
43
|
-
;(globalThis as unknown as { window?: unknown }).window = globalThis
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function uninstallLocks(): void {
|
|
47
|
-
if (originalNavigator) {
|
|
48
|
-
Object.defineProperty(globalThis, 'navigator', originalNavigator)
|
|
49
|
-
} else if (hadNavigator) {
|
|
50
|
-
// rien
|
|
51
|
-
} else {
|
|
52
|
-
delete (globalThis as unknown as { navigator?: unknown }).navigator
|
|
53
|
-
}
|
|
54
|
-
originalNavigator = undefined
|
|
55
|
-
if (!hadWindow) {
|
|
56
|
-
delete (globalThis as unknown as { window?: unknown }).window
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
afterEach(() => {
|
|
61
|
-
uninstallLocks()
|
|
62
|
-
vi.restoreAllMocks()
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
function makeClient(
|
|
66
|
-
fetchFn: typeof globalThis.fetch,
|
|
67
|
-
store: BaseAuthStore,
|
|
68
|
-
projectId = 'proj-X',
|
|
69
|
-
): AuraClientLike {
|
|
70
|
-
return {
|
|
71
|
-
baseUrl: 'http://locks.local',
|
|
72
|
-
projectId,
|
|
73
|
-
apiKey: 'k',
|
|
74
|
-
fetchFn,
|
|
75
|
-
authStore: store,
|
|
76
|
-
auth: null as unknown as AuraClientLike['auth'],
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function refreshFetch(tokenPair: unknown): typeof globalThis.fetch {
|
|
81
|
-
return (async (url: string | URL | Request) => {
|
|
82
|
-
if (String(url).endsWith('/refresh')) {
|
|
83
|
-
return new Response(JSON.stringify({ data: tokenPair }), {
|
|
84
|
-
status: 200,
|
|
85
|
-
headers: { 'Content-Type': 'application/json' },
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
return new Response('{}', { status: 200 })
|
|
89
|
-
}) as unknown as typeof globalThis.fetch
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
describe('refreshOnce — verrou cross-onglets Web Locks (I2)', () => {
|
|
93
|
-
it('enveloppe le refresh dans navigator.locks.request avec le nom aura-refresh-<projectId>', async () => {
|
|
94
|
-
const tokenPair = {
|
|
95
|
-
access_token: jwt('u1'),
|
|
96
|
-
refresh_token: 'rt-new',
|
|
97
|
-
user: fakeUser(),
|
|
98
|
-
token_type: 'bearer',
|
|
99
|
-
expires_in: 3600,
|
|
100
|
-
}
|
|
101
|
-
const store = new BaseAuthStore()
|
|
102
|
-
store.save(jwt('u1'), 'rt-old', fakeUser())
|
|
103
|
-
|
|
104
|
-
// Mock du lock : exécute immédiatement le callback (comme un lock disponible).
|
|
105
|
-
const request = vi.fn(
|
|
106
|
-
async (_name: string, _opts: unknown, cb: () => Promise<unknown>) => cb(),
|
|
107
|
-
)
|
|
108
|
-
installLocks(request)
|
|
109
|
-
|
|
110
|
-
const client = makeClient(refreshFetch(tokenPair), store, 'proj-X')
|
|
111
|
-
const res = await refreshOnce(client)
|
|
112
|
-
|
|
113
|
-
expect(res.error).toBeNull()
|
|
114
|
-
expect(res.data?.access_token).toBe(tokenPair.access_token)
|
|
115
|
-
// Le lock a bien enveloppé le refresh, sous le nom project-scoped.
|
|
116
|
-
expect(request).toHaveBeenCalledTimes(1)
|
|
117
|
-
expect(request.mock.calls[0]![0]).toBe('aura-refresh-proj-X')
|
|
118
|
-
expect(typeof request.mock.calls[0]![2]).toBe('function')
|
|
119
|
-
// Le store porte les tokens rafraîchis.
|
|
120
|
-
expect(store.token).toBe(tokenPair.access_token)
|
|
121
|
-
expect(store.refreshToken).toBe('rt-new')
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('fallback sans navigator.locks : le refresh fonctionne (comportement actuel)', async () => {
|
|
125
|
-
// Pas de locks installés (navigator absent ou sans .locks).
|
|
126
|
-
uninstallLocks()
|
|
127
|
-
delete (globalThis as unknown as { navigator?: unknown }).navigator
|
|
128
|
-
|
|
129
|
-
const tokenPair = {
|
|
130
|
-
access_token: jwt('u2'),
|
|
131
|
-
refresh_token: 'rt-fb',
|
|
132
|
-
user: fakeUser('u2'),
|
|
133
|
-
token_type: 'bearer',
|
|
134
|
-
expires_in: 3600,
|
|
135
|
-
}
|
|
136
|
-
const store = new BaseAuthStore()
|
|
137
|
-
store.save(jwt('u2'), 'rt-old', fakeUser('u2'))
|
|
138
|
-
|
|
139
|
-
const client = makeClient(refreshFetch(tokenPair), store, 'proj-Y')
|
|
140
|
-
const res = await refreshOnce(client)
|
|
141
|
-
|
|
142
|
-
expect(res.error).toBeNull()
|
|
143
|
-
expect(res.data?.access_token).toBe(tokenPair.access_token)
|
|
144
|
-
expect(store.refreshToken).toBe('rt-fb')
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('TESTS-1.3 : navigator.locks présent MAIS window ABSENT (Node 21+ SSR) → lock NON utilisé', async () => {
|
|
148
|
-
// Non-régression du gate `window` de withRefreshLock : sous Node ≥21 `navigator.locks`
|
|
149
|
-
// EXISTE mais est PROCESS-GLOBAL — l'utiliser sérialiserait les refresh d'utilisateurs
|
|
150
|
-
// DIFFÉRENTS partageant le même projectId (bug). On installe navigator.locks SANS
|
|
151
|
-
// window et on prouve que request() n'est JAMAIS appelé (fallback single-flight).
|
|
152
|
-
hadNavigator = 'navigator' in globalThis
|
|
153
|
-
originalNavigator = Object.getOwnPropertyDescriptor(globalThis, 'navigator')
|
|
154
|
-
const request = vi.fn(
|
|
155
|
-
async (_name: string, _opts: unknown, cb: () => Promise<unknown>) => cb(),
|
|
156
|
-
)
|
|
157
|
-
Object.defineProperty(globalThis, 'navigator', {
|
|
158
|
-
value: { locks: { request } },
|
|
159
|
-
configurable: true,
|
|
160
|
-
writable: true,
|
|
161
|
-
})
|
|
162
|
-
// GARANTIT l'absence de window (contexte serveur Node).
|
|
163
|
-
hadWindow = 'window' in globalThis
|
|
164
|
-
delete (globalThis as unknown as { window?: unknown }).window
|
|
165
|
-
|
|
166
|
-
const tokenPair = {
|
|
167
|
-
access_token: jwt('u4'),
|
|
168
|
-
refresh_token: 'rt-nolock',
|
|
169
|
-
user: fakeUser('u4'),
|
|
170
|
-
token_type: 'bearer',
|
|
171
|
-
expires_in: 3600,
|
|
172
|
-
}
|
|
173
|
-
const store = new BaseAuthStore()
|
|
174
|
-
store.save(jwt('u4'), 'rt-old', fakeUser('u4'))
|
|
175
|
-
|
|
176
|
-
const client = makeClient(refreshFetch(tokenPair), store, 'proj-SSR')
|
|
177
|
-
const res = await refreshOnce(client)
|
|
178
|
-
|
|
179
|
-
// Le refresh aboutit, mais SANS passer par le lock process-global.
|
|
180
|
-
expect(res.error).toBeNull()
|
|
181
|
-
expect(res.data?.access_token).toBe(tokenPair.access_token)
|
|
182
|
-
expect(request).not.toHaveBeenCalled()
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
it('acquisition du lock qui rejette (AbortError) → fallback SANS lock, pas de blocage', async () => {
|
|
186
|
-
const tokenPair = {
|
|
187
|
-
access_token: jwt('u3'),
|
|
188
|
-
refresh_token: 'rt-ab',
|
|
189
|
-
user: fakeUser('u3'),
|
|
190
|
-
token_type: 'bearer',
|
|
191
|
-
expires_in: 3600,
|
|
192
|
-
}
|
|
193
|
-
const store = new BaseAuthStore()
|
|
194
|
-
store.save(jwt('u3'), 'rt-old', fakeUser('u3'))
|
|
195
|
-
|
|
196
|
-
// Le lock rejette (comme un abort d'attente) → withRefreshLock doit fallback.
|
|
197
|
-
const request = vi.fn(async () => {
|
|
198
|
-
throw new DOMException('aborted', 'AbortError')
|
|
199
|
-
})
|
|
200
|
-
installLocks(request)
|
|
201
|
-
|
|
202
|
-
const client = makeClient(refreshFetch(tokenPair), store, 'proj-Z')
|
|
203
|
-
const res = await refreshOnce(client)
|
|
204
|
-
|
|
205
|
-
expect(request).toHaveBeenCalledTimes(1)
|
|
206
|
-
// Malgré l'échec d'acquisition, le refresh a été exécuté (fallback).
|
|
207
|
-
expect(res.error).toBeNull()
|
|
208
|
-
expect(res.data?.access_token).toBe(tokenPair.access_token)
|
|
209
|
-
})
|
|
210
|
-
})
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { createClient } from '../src/index'
|
|
3
|
-
|
|
4
|
-
describe('option client retry → retryConfig', () => {
|
|
5
|
-
it('défaut : activé, maxRetries 3', () => {
|
|
6
|
-
const c = createClient('http://localhost:8080', 'p', 'aura_anon_x')
|
|
7
|
-
expect(c.retryConfig).toEqual({ enabled: true, maxRetries: 3 })
|
|
8
|
-
})
|
|
9
|
-
it('retry: false → désactivé', () => {
|
|
10
|
-
const c = createClient('http://localhost:8080', 'p', 'aura_anon_x', { retry: false })
|
|
11
|
-
expect(c.retryConfig).toEqual({ enabled: false, maxRetries: 0 })
|
|
12
|
-
})
|
|
13
|
-
it('retry: { maxRetries: 5 } → override partiel', () => {
|
|
14
|
-
const c = createClient('http://localhost:8080', 'p', 'aura_anon_x', { retry: { maxRetries: 5 } })
|
|
15
|
-
expect(c.retryConfig).toEqual({ enabled: true, maxRetries: 5 })
|
|
16
|
-
})
|
|
17
|
-
})
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// E2E réel de rpc() — Sous-chantier A (Task A3) + Sous-chantier B (Task B6),
|
|
3
|
-
// chantier DB `rpc()`.
|
|
4
|
-
//
|
|
5
|
-
// Sous-chantier A — preuve runtime que `aura.db.rpc(fn, args?, options?)`
|
|
6
|
-
// fonctionne contre une vraie fonction Postgres exposée par PostgREST, à
|
|
7
|
-
// travers le gateway :
|
|
8
|
-
// - fonction scalaire (POST, args en body) → data est la valeur scalaire ;
|
|
9
|
-
// - fonction SETOF filtrée post-rpc (`.eq(...).select(...).single()`) ;
|
|
10
|
-
// - même fonction SETOF appelée en mode GET (`{ get: true }`, args en query).
|
|
11
|
-
//
|
|
12
|
-
// Sous-chantier B — preuve que le backend introspecte réellement les
|
|
13
|
-
// fonctions Postgres et que le typage généré en consomme le résultat :
|
|
14
|
-
// - `admin.db.schema()` remonte `functions` (name/args/returns/is_set_returning)
|
|
15
|
-
// pour une fonction scalaire ET une fonction SETOF ;
|
|
16
|
-
// - `aura.db.rpc(fn, {}, { count: 'exact' })` remonte `meta.total` (le
|
|
17
|
-
// gateway propage `count` sur `/rpc` via le même mécanisme générique
|
|
18
|
-
// Content-Range → meta.total que pour les tables) ;
|
|
19
|
-
// - `aura.db.rpc(fn, {}, { head: true })` ne renvoie pas d'erreur/de corps
|
|
20
|
-
// (le 204 court-circuite la construction de `meta` côté gateway) ;
|
|
21
|
-
// - `generateDatabaseTypes` (`@aurabase/gen`) consomme `schema().functions`
|
|
22
|
-
// réel et émet un bloc `Functions` avec `Args`/`Returns`.
|
|
23
|
-
//
|
|
24
|
-
// Setup (service_role, via `admin.db.sql`) : CREATE OR REPLACE FUNCTION +
|
|
25
|
-
// GRANT EXECUTE TO PUBLIC + `pg_notify('pgrst', 'reload schema')`, calqué sur
|
|
26
|
-
// db-types-e2e.test.ts. Le reload de cache PostgREST étant asynchrone, chaque
|
|
27
|
-
// appel anon est enveloppé dans `retryReload` (rejoue sur les statuts
|
|
28
|
-
// transitoires {408,429,502,503,504} ∪ 404, borné), avec skip gracieux
|
|
29
|
-
// explicite si le reload persiste après retries — cf. readme-smoke-e2e.test.ts.
|
|
30
|
-
// Cleanup `DROP FUNCTION IF EXISTS` en `finally`.
|
|
31
|
-
//
|
|
32
|
-
// Gaté par RUN_E2E + credentials (identique aux autres E2E du package) :
|
|
33
|
-
// describe.runIf(RUN) SKIPPE proprement la suite entière (pas d'échec) quand
|
|
34
|
-
// les variables d'env manquent — vérifié sans RUN_E2E via
|
|
35
|
-
// `pnpm --filter aurabase test`.
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
|
|
38
|
-
import { describe, it, expect } from 'vitest'
|
|
39
|
-
import { createClient, createAdminClient } from '../src/index'
|
|
40
|
-
import type { AuraResponse } from '../src/index'
|
|
41
|
-
import { generateDatabaseTypes } from '../../gen/src/generate'
|
|
42
|
-
|
|
43
|
-
const URL = process.env.AURA_E2E_URL
|
|
44
|
-
const PROJECT = process.env.AURA_E2E_PROJECT
|
|
45
|
-
const ANON = process.env.AURA_E2E_ANON_KEY
|
|
46
|
-
const SERVICE = process.env.AURA_E2E_SERVICE_KEY
|
|
47
|
-
const RUN = Boolean(process.env.RUN_E2E && URL && PROJECT && ANON && SERVICE)
|
|
48
|
-
|
|
49
|
-
/** Statuts transitoires observés sur la stack locale (timeout gateway→service sous charge). */
|
|
50
|
-
const TRANSIENT_STATUS = new Set([408, 429, 502, 503, 504])
|
|
51
|
-
|
|
52
|
-
/** Rejoue une requête tant qu'elle renvoie un statut transitoire (jeu complet
|
|
53
|
-
* TRANSIENT_STATUS) OU 404 (cache PostgREST pas encore rechargé — fonction
|
|
54
|
-
* inconnue), borné. Cf. readme-smoke-e2e.test.ts / db-types-e2e.test.ts : le
|
|
55
|
-
* reload de cache (pg_notify) est ASYNCHRONE — juste après le CREATE/GRANT,
|
|
56
|
-
* PostgREST peut d'abord renvoyer un 404 puis un 503 avant d'être prêt. */
|
|
57
|
-
async function retryReload<T>(fn: () => Promise<AuraResponse<T>>): Promise<AuraResponse<T>> {
|
|
58
|
-
let res = await fn()
|
|
59
|
-
for (let i = 0; i < 20 && (TRANSIENT_STATUS.has(res.error?.status) || res.error?.status === 404); i++) {
|
|
60
|
-
await new Promise((r) => setTimeout(r, 1000))
|
|
61
|
-
res = await fn()
|
|
62
|
-
}
|
|
63
|
-
return res
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** true si la réponse reste en échec de reload/indisponibilité après retries
|
|
67
|
-
* (skip gracieux — NON un défaut de rpc(), un défaut d'infra transitoire). */
|
|
68
|
-
function isReloadStuck<T>(res: AuraResponse<T>): boolean {
|
|
69
|
-
return TRANSIENT_STATUS.has(res.error?.status) || res.error?.status === 404
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
describe.runIf(RUN)('rpc() E2E réel (stack live) — fonction scalaire + SETOF filtrée + get', () => {
|
|
73
|
-
it(
|
|
74
|
-
'rpc_sum (scalaire, POST) / rpc_things (SETOF, filtré + get:true)',
|
|
75
|
-
async () => {
|
|
76
|
-
const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
|
|
77
|
-
const aura = createClient(URL!, PROJECT!, ANON!)
|
|
78
|
-
const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
|
|
79
|
-
const fnSum = `rpc_sum_${suffix}`
|
|
80
|
-
const fnThings = `rpc_things_${suffix}`
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
// -- Setup (service_role) : fonctions + GRANT + reload cache PostgREST --
|
|
84
|
-
await admin.db.sql(
|
|
85
|
-
`CREATE OR REPLACE FUNCTION ${fnSum}(a int, b int) RETURNS int LANGUAGE sql IMMUTABLE AS $fn$ SELECT a + b $fn$`,
|
|
86
|
-
)
|
|
87
|
-
await admin.db.sql(
|
|
88
|
-
`CREATE OR REPLACE FUNCTION ${fnThings}() RETURNS TABLE(id int, name text) LANGUAGE sql STABLE AS $fn$ SELECT * FROM (VALUES (1,'alpha'), (2,'beta')) t(id,name) $fn$`,
|
|
89
|
-
)
|
|
90
|
-
await admin.db.sql(`GRANT EXECUTE ON FUNCTION ${fnSum}(int,int), ${fnThings}() TO PUBLIC`)
|
|
91
|
-
await admin.db.sql(`SELECT pg_notify('pgrst', 'reload schema')`)
|
|
92
|
-
// Laisse le reload asynchrone démarrer avant le premier appel — sans ce
|
|
93
|
-
// délai, le premier appel tombe quasi systématiquement en 404/503.
|
|
94
|
-
await new Promise((r) => setTimeout(r, 1500))
|
|
95
|
-
|
|
96
|
-
// -- 1. Fonction scalaire (POST, args en body) --------------------------
|
|
97
|
-
const scalar = await retryReload(() => aura.db.rpc(fnSum, { a: 2, b: 3 }))
|
|
98
|
-
if (isReloadStuck(scalar)) {
|
|
99
|
-
// eslint-disable-next-line no-console
|
|
100
|
-
console.warn(
|
|
101
|
-
'[rpc-e2e] SKIP rpc scalaire (infra: reload/503/404 persistant après retries) — ' +
|
|
102
|
-
'NON un succès : assertion runtime rpc ignorée, pas un défaut de rpc().',
|
|
103
|
-
)
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
expect(scalar.error).toBeNull()
|
|
107
|
-
expect(scalar.data).toBe(5)
|
|
108
|
-
|
|
109
|
-
// -- 2. Fonction SETOF filtrée post-rpc (.eq().select().single()) ------
|
|
110
|
-
const filtered = await retryReload(() =>
|
|
111
|
-
aura.db.rpc(fnThings, {}).eq('id', 2).select('id,name').single(),
|
|
112
|
-
)
|
|
113
|
-
if (isReloadStuck(filtered)) {
|
|
114
|
-
// eslint-disable-next-line no-console
|
|
115
|
-
console.warn(
|
|
116
|
-
'[rpc-e2e] SKIP rpc SETOF filtrée (infra: reload/503/404 persistant après retries) — ' +
|
|
117
|
-
'NON un succès : assertion runtime rpc ignorée, pas un défaut de rpc().',
|
|
118
|
-
)
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
expect(filtered.error).toBeNull()
|
|
122
|
-
expect(filtered.data).toEqual({ id: 2, name: 'beta' })
|
|
123
|
-
|
|
124
|
-
// -- 3. Même fonction SETOF, appelée en mode GET (get:true) -------------
|
|
125
|
-
const viaGet = await retryReload(() => aura.db.rpc(fnThings, undefined, { get: true }))
|
|
126
|
-
if (isReloadStuck(viaGet)) {
|
|
127
|
-
// eslint-disable-next-line no-console
|
|
128
|
-
console.warn(
|
|
129
|
-
'[rpc-e2e] SKIP rpc get:true (infra: reload/503/404 persistant après retries) — ' +
|
|
130
|
-
'NON un succès : assertion runtime rpc ignorée, pas un défaut de rpc().',
|
|
131
|
-
)
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
expect(viaGet.error).toBeNull()
|
|
135
|
-
expect(viaGet.data).toEqual([
|
|
136
|
-
{ id: 1, name: 'alpha' },
|
|
137
|
-
{ id: 2, name: 'beta' },
|
|
138
|
-
])
|
|
139
|
-
} finally {
|
|
140
|
-
await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnSum}(int,int)`)
|
|
141
|
-
await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnThings}()`)
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
120_000,
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
// -- Sous-chantier B (Task B6) : introspection backend + count/head + gen --
|
|
148
|
-
//
|
|
149
|
-
// Le backend est déployé (rebuild fait) : `GET /v1/db/{pid}/schema` remonte
|
|
150
|
-
// désormais `functions: [{name, args:[{name,data_type,mode}], returns,
|
|
151
|
-
// is_set_returning}]`, `DatabaseService.schema()` les propage, et le
|
|
152
|
-
// gateway propage déjà `count` sur `/rpc` (Content-Range → meta.total, même
|
|
153
|
-
// mécanisme générique que pour les tables — dérisqué en curl direct avant
|
|
154
|
-
// d'écrire ce test : `functions` contient bien `rpc_sum_*`/`rpc_things_*`
|
|
155
|
-
// avec la forme attendue, `count=exact` sur `rpc_things` donne
|
|
156
|
-
// `meta.total === 2`). Pour `head:true`, le gateway répond 204 No Content
|
|
157
|
-
// AVANT toute construction de `meta` (court-circuit générique, cf.
|
|
158
|
-
// `postgrest_proxy.rs::normalize_response`) — vérifié en curl direct : le
|
|
159
|
-
// total n'est PAS exposé en HEAD. On assert donc uniquement l'absence
|
|
160
|
-
// d'erreur/de corps, avec un hedge défensif si `meta.total` venait à
|
|
161
|
-
// apparaître un jour (comportement alors encore plus favorable).
|
|
162
|
-
it(
|
|
163
|
-
'schema() introspecte les fonctions réelles + count/head réels + gen émet Functions',
|
|
164
|
-
async () => {
|
|
165
|
-
const admin = createAdminClient(URL!, PROJECT!, SERVICE!)
|
|
166
|
-
const aura = createClient(URL!, PROJECT!, ANON!)
|
|
167
|
-
const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
|
|
168
|
-
const fnSum = `rpc_sum_b_${suffix}`
|
|
169
|
-
const fnThings = `rpc_things_b_${suffix}`
|
|
170
|
-
|
|
171
|
-
try {
|
|
172
|
-
// -- Setup (service_role) : fonctions + GRANT + reload cache PostgREST --
|
|
173
|
-
await admin.db.sql(
|
|
174
|
-
`CREATE OR REPLACE FUNCTION ${fnSum}(a int, b int) RETURNS int LANGUAGE sql IMMUTABLE AS $fn$ SELECT a + b $fn$`,
|
|
175
|
-
)
|
|
176
|
-
await admin.db.sql(
|
|
177
|
-
`CREATE OR REPLACE FUNCTION ${fnThings}() RETURNS TABLE(id int, name text) LANGUAGE sql STABLE AS $fn$ SELECT * FROM (VALUES (1,'alpha'), (2,'beta')) t(id,name) $fn$`,
|
|
178
|
-
)
|
|
179
|
-
await admin.db.sql(`GRANT EXECUTE ON FUNCTION ${fnSum}(int,int), ${fnThings}() TO PUBLIC`)
|
|
180
|
-
await admin.db.sql(`SELECT pg_notify('pgrst', 'reload schema')`)
|
|
181
|
-
await new Promise((r) => setTimeout(r, 1500))
|
|
182
|
-
|
|
183
|
-
// -- 1. schema() introspecte réellement les fonctions -------------------
|
|
184
|
-
const schemaRes = await retryReload(() => admin.db.schema())
|
|
185
|
-
if (isReloadStuck(schemaRes)) {
|
|
186
|
-
// eslint-disable-next-line no-console
|
|
187
|
-
console.warn(
|
|
188
|
-
'[rpc-e2e] SKIP schema() functions (infra: reload/503/404 persistant après retries) — ' +
|
|
189
|
-
'NON un succès : assertion schema() ignorée, pas un défaut de schema().',
|
|
190
|
-
)
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
expect(schemaRes.error).toBeNull()
|
|
194
|
-
const allFunctions = schemaRes.data?.functions ?? []
|
|
195
|
-
const sumFn = allFunctions.find((f) => f.name === fnSum)
|
|
196
|
-
const thingsFn = allFunctions.find((f) => f.name === fnThings)
|
|
197
|
-
expect(sumFn).toBeDefined()
|
|
198
|
-
expect(sumFn?.args).toEqual([
|
|
199
|
-
{ name: 'a', data_type: 'int4', mode: 'IN' },
|
|
200
|
-
{ name: 'b', data_type: 'int4', mode: 'IN' },
|
|
201
|
-
])
|
|
202
|
-
expect(sumFn?.returns).toBe('int4')
|
|
203
|
-
expect(sumFn?.is_set_returning).toBe(false)
|
|
204
|
-
expect(thingsFn).toBeDefined()
|
|
205
|
-
expect(thingsFn?.is_set_returning).toBe(true)
|
|
206
|
-
|
|
207
|
-
// -- 2. count réel : meta.total === 2 sur rpc_things ---------------------
|
|
208
|
-
const counted = await retryReload(() => aura.db.rpc(fnThings, {}, { count: 'exact' }))
|
|
209
|
-
if (isReloadStuck(counted)) {
|
|
210
|
-
// eslint-disable-next-line no-console
|
|
211
|
-
console.warn(
|
|
212
|
-
'[rpc-e2e] SKIP rpc count (infra: reload/503/404 persistant après retries) — ' +
|
|
213
|
-
'NON un succès : assertion count ignorée, pas un défaut de rpc().',
|
|
214
|
-
)
|
|
215
|
-
return
|
|
216
|
-
}
|
|
217
|
-
expect(counted.error).toBeNull()
|
|
218
|
-
expect(counted.meta?.total).toBe(2)
|
|
219
|
-
|
|
220
|
-
// -- 3. head réel : pas d'erreur, pas de corps (204) ---------------------
|
|
221
|
-
const headed = await retryReload(() => aura.db.rpc(fnThings, {}, { head: true }))
|
|
222
|
-
if (isReloadStuck(headed)) {
|
|
223
|
-
// eslint-disable-next-line no-console
|
|
224
|
-
console.warn(
|
|
225
|
-
'[rpc-e2e] SKIP rpc head (infra: reload/503/404 persistant après retries) — ' +
|
|
226
|
-
'NON un succès : assertion head ignorée, pas un défaut de rpc().',
|
|
227
|
-
)
|
|
228
|
-
return
|
|
229
|
-
}
|
|
230
|
-
expect(headed.error).toBeNull()
|
|
231
|
-
expect(headed.data).toBeNull()
|
|
232
|
-
// Hedge défensif : le 204 court-circuite `meta` côté gateway (vérifié en
|
|
233
|
-
// curl), donc `meta.total` est absent en pratique — si un jour exposé,
|
|
234
|
-
// il doit rester cohérent avec le compte réel.
|
|
235
|
-
if (headed.meta?.total !== undefined) {
|
|
236
|
-
expect(headed.meta.total).toBe(2)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// -- 4. gen consomme le schéma réel et émet le bloc Functions ------------
|
|
240
|
-
const typesOut = generateDatabaseTypes(schemaRes.data?.tables ?? [], {
|
|
241
|
-
functions: allFunctions,
|
|
242
|
-
})
|
|
243
|
-
expect(typesOut).toContain('Functions:')
|
|
244
|
-
expect(typesOut).toContain(`${fnSum}:`)
|
|
245
|
-
expect(typesOut).toContain('Args: { a: number; b: number }')
|
|
246
|
-
expect(typesOut).toContain(`${fnThings}:`)
|
|
247
|
-
} finally {
|
|
248
|
-
await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnSum}(int,int)`)
|
|
249
|
-
await admin.db.sql(`DROP FUNCTION IF EXISTS ${fnThings}()`)
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
|
-
120_000,
|
|
253
|
-
)
|
|
254
|
-
})
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// E2E réel — LOT E : getClaims via la vérification LOCALE RS256 (stack live)
|
|
3
|
-
//
|
|
4
|
-
// Contrairement à `auth-e2e.test.ts` (qui documente le déploiement HS256/JWKS
|
|
5
|
-
// vide → chemin FALLBACK getUser), ce test s'exécute contre la stack configurée
|
|
6
|
-
// en RS256 par l'override `docker/docker-compose.rs256.yml` (LOT E). Il prouve
|
|
7
|
-
// que `getClaims` emprunte enfin le chemin de vérification LOCALE (WebCrypto,
|
|
8
|
-
// JWKS non-vide) — cf. `packages/auth/src/jwks.ts` + `AuthService.getClaims`.
|
|
9
|
-
//
|
|
10
|
-
// Signaux discriminants du chemin LOCAL (vs fallback) :
|
|
11
|
-
// - `header.alg === 'RS256'` et `header.kid === 'dev1'` ;
|
|
12
|
-
// - `claims.exp` présent (le fallback dérive de getUser et OMET exp/iat) ;
|
|
13
|
-
// - ZÉRO appel réseau `/v1/auth/{pid}/user` (compteur sur un fetch injecté) ;
|
|
14
|
-
// - au moins un fetch `/.well-known/jwks.json`.
|
|
15
|
-
// Plus : une signature falsifiée → `signature_invalid` (JAMAIS de fallback qui
|
|
16
|
-
// masquerait une signature invalide).
|
|
17
|
-
//
|
|
18
|
-
// Gaté par RUN_E2E + AURA_E2E_URL/PROJECT/ANON_KEY (projet provisionné en amont
|
|
19
|
-
// par le driver LOT E). Squelette calqué sur auth-e2e.test.ts.
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
import { describe, it, expect } from 'vitest'
|
|
23
|
-
import { createClient } from '../src/index'
|
|
24
|
-
import type { AuraResponse } from '../src/index'
|
|
25
|
-
|
|
26
|
-
const URL = process.env.AURA_E2E_URL
|
|
27
|
-
const PROJECT = process.env.AURA_E2E_PROJECT
|
|
28
|
-
const ANON = process.env.AURA_E2E_ANON_KEY
|
|
29
|
-
const RUN = Boolean(process.env.RUN_E2E && URL && PROJECT && ANON)
|
|
30
|
-
|
|
31
|
-
const TRANSIENT_STATUS = new Set([408, 429, 502, 503, 504])
|
|
32
|
-
async function retryTransient<T>(fn: () => Promise<AuraResponse<T>>): Promise<AuraResponse<T>> {
|
|
33
|
-
let res = await fn()
|
|
34
|
-
for (let i = 0; i < 10 && res.error && TRANSIENT_STATUS.has(res.error.status); i++) {
|
|
35
|
-
await new Promise((r) => setTimeout(r, 1000))
|
|
36
|
-
res = await fn()
|
|
37
|
-
}
|
|
38
|
-
return res
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Décode le header d'un JWT (base64url natif Node). */
|
|
42
|
-
function jwtHeader(jwt: string): { alg?: string; kid?: string } {
|
|
43
|
-
return JSON.parse(Buffer.from(jwt.split('.')[0], 'base64url').toString('utf8'))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
describe.runIf(RUN)('LOT E — getClaims vérif LOCALE RS256 (stack live override)', () => {
|
|
47
|
-
it(
|
|
48
|
-
'login RS256 → getClaims chemin LOCAL (JWKS non-vide, 0 appel /user, exp présent) + signature falsifiée → signature_invalid',
|
|
49
|
-
async () => {
|
|
50
|
-
// Fetch instrumenté : compte les appels /user (fallback) et /jwks.json (local).
|
|
51
|
-
let userCalls = 0
|
|
52
|
-
let jwksCalls = 0
|
|
53
|
-
const countingFetch: typeof globalThis.fetch = (input, init) => {
|
|
54
|
-
const u =
|
|
55
|
-
typeof input === 'string' ? input : input instanceof URL ? input.href : (input as Request).url
|
|
56
|
-
if (u.includes('/.well-known/jwks.json')) jwksCalls++
|
|
57
|
-
if (/\/v1\/auth\/[^/]+\/user\b/.test(u)) userCalls++
|
|
58
|
-
return globalThis.fetch(input as Parameters<typeof globalThis.fetch>[0], init)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const client = createClient(URL!, PROJECT!, ANON!, { fetch: countingFetch })
|
|
62
|
-
const suffix = process.env.AURA_E2E_SUFFIX ?? Math.random().toString(36).slice(2, 8)
|
|
63
|
-
const email = `lote-rs256-${suffix}@aurabase.dev`
|
|
64
|
-
const password = 'EndUserPassword123!'
|
|
65
|
-
|
|
66
|
-
// -- 1. register + login → token RS256 --------------------------------
|
|
67
|
-
const signUpRes = await retryTransient(() => client.auth.signUp({ email, password }))
|
|
68
|
-
expect(signUpRes.error).toBeNull()
|
|
69
|
-
|
|
70
|
-
const loginRes = await retryTransient(() =>
|
|
71
|
-
client.auth.signInWithPassword({ email, password }),
|
|
72
|
-
)
|
|
73
|
-
expect(loginRes.error).toBeNull()
|
|
74
|
-
const loginData = loginRes.data as { access_token: string; user: { id: string } }
|
|
75
|
-
const token = loginData.access_token
|
|
76
|
-
const userId = loginData.user.id
|
|
77
|
-
expect(token).toBeTruthy()
|
|
78
|
-
expect(userId).toBeTruthy()
|
|
79
|
-
|
|
80
|
-
// -- 2. header du token : RS256 + kid=dev1 ----------------------------
|
|
81
|
-
const header = jwtHeader(token)
|
|
82
|
-
expect(header.alg).toBe('RS256')
|
|
83
|
-
expect(header.kid).toBe('dev1')
|
|
84
|
-
|
|
85
|
-
// -- 3. getClaims → chemin LOCAL (aucun /user) ------------------------
|
|
86
|
-
userCalls = 0
|
|
87
|
-
jwksCalls = 0
|
|
88
|
-
const claimsRes = await client.auth.getClaims()
|
|
89
|
-
expect(claimsRes.error).toBeNull()
|
|
90
|
-
expect(claimsRes.data?.header.alg).toBe('RS256')
|
|
91
|
-
expect(claimsRes.data?.header.kid).toBe('dev1')
|
|
92
|
-
expect(claimsRes.data?.claims.sub).toBe(userId)
|
|
93
|
-
// `exp` présent ⇒ claims issues du JWT décodé localement (le fallback getUser l'OMET).
|
|
94
|
-
expect(typeof claimsRes.data?.claims.exp).toBe('number')
|
|
95
|
-
// ZÉRO appel /user ⇒ pas de validation serveur : vérif purement locale.
|
|
96
|
-
expect(userCalls).toBe(0)
|
|
97
|
-
// Au moins un fetch JWKS (peut être 0 si un test précédent a chauffé le cache
|
|
98
|
-
// process-global ; on tolère >= 0 mais on exige surtout userCalls === 0).
|
|
99
|
-
expect(jwksCalls).toBeGreaterThanOrEqual(0)
|
|
100
|
-
|
|
101
|
-
// -- 4. signature falsifiée → signature_invalid (jamais de fallback) --
|
|
102
|
-
const parts = token.split('.')
|
|
103
|
-
const tampered = parts[2].endsWith('AAAA') ? parts[2].slice(0, -4) + 'BBBB' : parts[2].slice(0, -4) + 'AAAA'
|
|
104
|
-
const forged = `${parts[0]}.${parts[1]}.${tampered}`
|
|
105
|
-
userCalls = 0
|
|
106
|
-
const forgedRes = await client.auth.getClaims(forged)
|
|
107
|
-
expect(forgedRes.data).toBeNull()
|
|
108
|
-
expect(forgedRes.error?.code).toBe('signature_invalid')
|
|
109
|
-
// Une signature invalide ne doit JAMAIS déclencher le fallback getUser.
|
|
110
|
-
expect(userCalls).toBe(0)
|
|
111
|
-
},
|
|
112
|
-
120_000,
|
|
113
|
-
)
|
|
114
|
-
})
|