calendar-oauth-connector-integration.test.mjs
sha256:baa800aedf841dbf32081aa7b2befa288ac33dfc7175ac55014c55c4d8c742f9
docs: move durable-auth freeze/evidence to local developmen…
Human
10 days ago
| 1 | /** |
| 2 | * Tier 2 — INTEGRATION: full Google OAuth connector lifecycle (fake Google client). |
| 3 | */ |
| 4 | import { describe, it, beforeEach, afterEach } from 'node:test'; |
| 5 | import assert from 'node:assert/strict'; |
| 6 | import fs from 'node:fs'; |
| 7 | import path from 'node:path'; |
| 8 | import { fileURLToPath } from 'node:url'; |
| 9 | import { |
| 10 | handleBeginGoogleConnector, |
| 11 | handleGoogleConnectorCallback, |
| 12 | handleListGoogleConnectors, |
| 13 | handleSyncGoogleConnector, |
| 14 | handleRevokeGoogleConnector, |
| 15 | createFakeGoogleClient, |
| 16 | findPendingConnectorByState, |
| 17 | } from '../lib/calendar/google-oauth-connector.mjs'; |
| 18 | import { listSourceCalendars, queryStoredEvents } from '../lib/calendar/event-store.mjs'; |
| 19 | |
| 20 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
| 21 | const tmpRoot = path.join(__dirname, 'fixtures', 'tmp-calendar-oauth-integration'); |
| 22 | const vaultId = 'default'; |
| 23 | const RETURN_URL = 'https://app.scooling.local:5174/settings/calendar/connect/return'; |
| 24 | |
| 25 | const testEnv = { |
| 26 | GOOGLE_CALENDAR_OAUTH_CLIENT_ID: 'test-client-id', |
| 27 | GOOGLE_CALENDAR_OAUTH_CLIENT_SECRET: 'test-client-secret', |
| 28 | KNOWTATION_CALENDAR_OAUTH_SECRET: 'y'.repeat(40), |
| 29 | CALENDAR_OAUTH_REDIRECT_URI: 'https://hub.local/api/v1/calendar/connectors/callback', |
| 30 | SCOOLING_RETURN_URL_ALLOWLIST: RETURN_URL, |
| 31 | }; |
| 32 | |
| 33 | describe('calendar oauth integration lifecycle', () => { |
| 34 | let dataDir; |
| 35 | |
| 36 | beforeEach(() => { |
| 37 | fs.rmSync(tmpRoot, { recursive: true, force: true }); |
| 38 | dataDir = path.join(tmpRoot, 'data'); |
| 39 | fs.mkdirSync(dataDir, { recursive: true }); |
| 40 | }); |
| 41 | |
| 42 | afterEach(() => { |
| 43 | fs.rmSync(tmpRoot, { recursive: true, force: true }); |
| 44 | }); |
| 45 | |
| 46 | it('POST begin → callback → list → sync → DELETE', async () => { |
| 47 | const begin = handleBeginGoogleConnector({ |
| 48 | dataDir, |
| 49 | vaultId, |
| 50 | body: { provider: 'google', display_name: 'Work', return_url: RETURN_URL }, |
| 51 | env: testEnv, |
| 52 | now: Date.now(), |
| 53 | authorizedOverride: true, |
| 54 | }); |
| 55 | assert.equal(begin.ok, true); |
| 56 | assert.ok(begin.payload.connector_id.startsWith('conn_')); |
| 57 | assert.match(begin.payload.authorization_url, /accounts\.google\.com/); |
| 58 | |
| 59 | const located = findPendingConnectorByState(dataDir, new URL(begin.payload.authorization_url).searchParams.get('state')); |
| 60 | assert.ok(located); |
| 61 | |
| 62 | const googleClient = createFakeGoogleClient(); |
| 63 | const callback = await handleGoogleConnectorCallback({ |
| 64 | dataDir, |
| 65 | query: { |
| 66 | code: 'auth-code-123', |
| 67 | state: located.connector.oauth_pending.state, |
| 68 | }, |
| 69 | googleClient, |
| 70 | env: testEnv, |
| 71 | authorizedOverride: true, |
| 72 | }); |
| 73 | assert.equal(callback.ok, true); |
| 74 | assert.match(callback.redirect, /connect=ok/); |
| 75 | |
| 76 | const listed = handleListGoogleConnectors({ dataDir, vaultId, authorizedOverride: true }); |
| 77 | assert.equal(listed.ok, true); |
| 78 | assert.equal(listed.payload.connectors.length, 1); |
| 79 | assert.equal(listed.payload.connectors[0].status, 'connected'); |
| 80 | assert.equal(listed.payload.connectors[0].source_calendar_count, 1); |
| 81 | |
| 82 | const calendars = listSourceCalendars(dataDir, vaultId); |
| 83 | assert.equal(calendars.length, 1); |
| 84 | assert.equal(calendars[0].provider, 'google'); |
| 85 | |
| 86 | const sync = await handleSyncGoogleConnector({ |
| 87 | dataDir, |
| 88 | vaultId, |
| 89 | connectorId: begin.payload.connector_id, |
| 90 | googleClient, |
| 91 | env: testEnv, |
| 92 | now: Date.now() + 120_000, |
| 93 | authorizedOverride: true, |
| 94 | }); |
| 95 | assert.equal(sync.ok, true); |
| 96 | |
| 97 | const events = queryStoredEvents(dataDir, vaultId, { |
| 98 | fromIso: '2026-01-01T00:00:00.000Z', |
| 99 | toIso: '2027-01-01T00:00:00.000Z', |
| 100 | }); |
| 101 | assert.ok(events.length >= 1); |
| 102 | |
| 103 | const revoke = await handleRevokeGoogleConnector({ |
| 104 | dataDir, |
| 105 | vaultId, |
| 106 | connectorId: begin.payload.connector_id, |
| 107 | googleClient, |
| 108 | env: testEnv, |
| 109 | authorizedOverride: true, |
| 110 | }); |
| 111 | assert.equal(revoke.ok, true); |
| 112 | assert.equal(revoke.payload.revoked, true); |
| 113 | assert.ok(revoke.payload.events_deleted >= 1); |
| 114 | }); |
| 115 | }); |
File History
1 commit
sha256:baa800aedf841dbf32081aa7b2befa288ac33dfc7175ac55014c55c4d8c742f9
docs: move durable-auth freeze/evidence to local developmen…
Human
10 days ago