import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { dirname, join } from 'node:path' import test from 'node:test' import { fileURLToPath } from 'node:url' import { DEFAULT_ZOOM_CLIENT_ID_SECRET, DEFAULT_ZOOM_INTEGRATION_NAME, DEFAULT_ZOOM_S2S_CLIENT_SECRET, ZOOM_CLIENT_ID_SETUP_URL, ZOOM_OAUTH_CONNECT_URL, ZOOM_S2S_CLIENT_SECRET_SETUP_URL, buildOauthConnectUrl, isConfirmed, isDryRun, resolveAuthMode, resolveIntegrationName, resolveZoomAuth, } from '../src/auth.ts' import { normalizeMeetingLocator, normalizeUserLocator, normalizeWebinarLocator, parseZoomResourceUrl, } from '../src/locators.ts' import { buildMeetingBody, buildWebinarBody } from '../src/shape.ts' const root = join(dirname(fileURLToPath(import.meta.url)), '..') test('default auth is OAuth integration zoom', () => { const auth = resolveZoomAuth() assert.equal(auth.mode, 'oauth') assert.equal(auth.integrationName, DEFAULT_ZOOM_INTEGRATION_NAME) assert.equal(auth.apiHost, 'api.zoom.us') assert.equal(auth.apiBaseUrl, 'https://api.zoom.us/v2') }) test('multi-account OAuth uses zoom-* integration names', () => { assert.equal(resolveZoomAuth({ integrationName: 'zoom-work' }).integrationName, 'zoom-work') assert.equal(resolveIntegrationName({ account: 'work' }), 'zoom-work') assert.equal(resolveIntegrationName({ account: 'zoom-bot' }), 'zoom-bot') assert.equal(resolveIntegrationName({ account: 'zoom' }), 'zoom') }) test('S2S accountId selects the server-to-server lane', () => { const auth = resolveZoomAuth({ accountId: 'exampleAccountId_123', integrationName: 'zoom-work', }) assert.equal(auth.mode, 's2s') assert.equal(auth.accountId, 'exampleAccountId_123') assert.equal(auth.clientIdSecret, DEFAULT_ZOOM_CLIENT_ID_SECRET) assert.equal(auth.clientSecretSecret, DEFAULT_ZOOM_S2S_CLIENT_SECRET) assert.equal(auth.integrationName, null) }) test('account suffix applies to S2S secret names', () => { const auth = resolveZoomAuth({ auth: 's2s', account: 'work', accountId: 'exampleWorkAccount' }) assert.equal(auth.mode, 's2s') assert.equal(auth.accountId, 'exampleWorkAccount') assert.equal(auth.clientIdSecret, 'zoomClientId-work') assert.equal(auth.clientSecretSecret, 'zoomS2sClientSecret-work') }) test('explicit auth s2s uses documented default secret names', () => { const auth = resolveZoomAuth({ auth: 's2s', accountId: 'exampleAccountId' }) assert.equal(auth.accountId, 'exampleAccountId') assert.equal(auth.clientIdSecret, DEFAULT_ZOOM_CLIENT_ID_SECRET) assert.equal(auth.clientSecretSecret, DEFAULT_ZOOM_S2S_CLIENT_SECRET) assert.equal(resolveAuthMode({}), 'oauth') assert.equal(resolveAuthMode({ auth: 's2s' }), 's2s') assert.equal(resolveAuthMode({ accountId: 'exampleAccountId' }), 's2s') }) test('S2S requires a plain accountId (never a secret placeholder)', () => { assert.throws(() => resolveZoomAuth({ auth: 's2s' }), /accountId/) assert.throws( () => resolveZoomAuth({ auth: 's2s', accountId: '{{secret:zoomAccountId}}' }), /accountId must be the Zoom account ID/, ) }) test('core never puts a secret placeholder outside a header', () => { const core = readFileSync(join(root, 'src/core.ts'), 'utf8') assert.doesNotMatch(core, /\{\{secret:/) assert.match(core, /secretHeaders\.basic\(\{/) }) test('connect and secret URLs are prefilled and generic', () => { assert.match(ZOOM_OAUTH_CONNECT_URL, /^https:\/\/kody\.codes\/connect\/oauth\?/) assert.match(ZOOM_OAUTH_CONNECT_URL, /provider=zoom/) assert.match(ZOOM_OAUTH_CONNECT_URL, /authorizeUrl=https%3A%2F%2Fzoom.us%2Foauth%2Fauthorize/) assert.match(ZOOM_OAUTH_CONNECT_URL, /tokenUrl=https%3A%2F%2Fzoom.us%2Foauth%2Ftoken/) assert.match(ZOOM_OAUTH_CONNECT_URL, /allowedHosts=api.zoom.us%2Czoom.us/) assert.match(ZOOM_OAUTH_CONNECT_URL, /apiBaseUrl=https%3A%2F%2Fapi.zoom.us%2Fv2/) assert.match(ZOOM_CLIENT_ID_SETUP_URL, /^https:\/\/kody\.codes\/account\/secrets\/new\?/) assert.match(ZOOM_CLIENT_ID_SETUP_URL, /name=zoomClientId/) assert.match(ZOOM_S2S_CLIENT_SECRET_SETUP_URL, /name=zoomS2sClientSecret/) assert.match(ZOOM_S2S_CLIENT_SECRET_SETUP_URL, /allowedHosts=api.zoom.us%2Czoom.us/) const work = buildOauthConnectUrl({ provider: 'zoom-work' }) assert.match(work, /provider=zoom-work/) }) test('user locator defaults to me', () => { assert.deepEqual(normalizeUserLocator({}), { userId: 'me' }) assert.deepEqual(normalizeUserLocator({ email: 'host@example.com' }), { userId: 'host%40example.com', }) }) test('meeting locators accept id and zoom.us join URLs', () => { assert.deepEqual(normalizeMeetingLocator({ meetingId: '11111111111' }), { meetingId: '11111111111', }) assert.deepEqual( normalizeMeetingLocator({ meetingUrl: 'https://example.zoom.us/j/11111111111?pwd=example', }), { meetingId: '11111111111' }, ) assert.deepEqual( normalizeMeetingLocator({ meetingUrl: 'https://zoom.us/j/11111111111?occurrence_id=1', }), { meetingId: '11111111111', occurrenceId: '1' }, ) }) test('webinar locators parse zoom.us/w URLs', () => { assert.deepEqual( normalizeWebinarLocator({ webinarUrl: 'https://zoom.us/w/22222222222' }), { webinarId: '22222222222' }, ) assert.equal(parseZoomResourceUrl('https://zoom.us/j/11111111111')?.id, '11111111111') }) test('rejects non-Zoom hosts', () => { assert.throws( () => normalizeMeetingLocator({ meetingUrl: 'https://meet.example.com/j/11111111111' }), /zoom\.us/, ) }) test('meeting and webinar bodies stay generic and accept camelCase aliases', () => { assert.deepEqual( buildMeetingBody({ topic: 'Example meeting', type: 2, startTime: '2030-01-15T17:00:00', duration: 30, timezone: 'UTC', }), { topic: 'Example meeting', type: 2, start_time: '2030-01-15T17:00:00', duration: 30, timezone: 'UTC', }, ) assert.equal(buildWebinarBody({ topic: 'Example webinar', type: 5 }).topic, 'Example webinar') }) test('mutation flags require an explicit dryRun or confirm', () => { assert.equal(isDryRun({ dryRun: true }), true) assert.equal(isConfirmed({ confirm: true }), true) assert.equal(isDryRun({}), false) assert.equal(isConfirmed({}), false) }) test('source and docs stay generic', () => { const files = [ 'README.md', 'package.json', 'src/index.ts', 'src/auth.ts', 'src/locators.ts', 'src/meetings/create.ts', 'src/meetings/delete.ts', 'src/scheduled-upcoming-meetings.ts', ] for (const file of files) { const text = readFileSync(join(root, file), 'utf8') assert.doesNotMatch(text, /kentcdodds/i) assert.doesNotMatch(text, /epicweb/i) assert.doesNotMatch(text, /\/community\/[0-9a-f-]{36}/) } const readme = readFileSync(join(root, 'README.md'), 'utf8') assert.match(readme, /^## Intent$/m) assert.match(readme, /https:\/\/kody\.codes\/@kody\/zoom/) assert.match(readme, /https:\/\/kody\.codes\/connect\/oauth\?provider=zoom/) assert.doesNotMatch(readme, /name=zoomAccountId/) assert.match(readme, /accountId/) assert.match(readme, /https:\/\/kody\.codes\/account\/secrets\/new\?name=zoomClientId/) assert.match(readme, /https:\/\/kody\.codes\/account\/secrets\/new\?name=zoomS2sClientSecret/) assert.match(readme, /dryRun/) assert.match(readme, /confirm/) const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { name: string private?: boolean license: string kody: { id: string; jobs: { 'upcoming-meetings': { enabled: boolean } } } } assert.equal(manifest.name, '@kody/zoom') assert.equal(manifest.private, false) assert.equal(manifest.license, 'MIT') assert.equal(manifest.kody.id, 'zoom') assert.equal(manifest.kody.jobs['upcoming-meetings'].enabled, false) })