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_ACCOUNT_ID_SECRET,
DEFAULT_ZOOM_CLIENT_ID_SECRET,
DEFAULT_ZOOM_INTEGRATION_NAME,
DEFAULT_ZOOM_S2S_CLIENT_SECRET,
ZOOM_ACCOUNT_ID_SETUP_URL,
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 secret overrides select the server-to-server lane', () => {
const auth = resolveZoomAuth({
accountIdSecret: 'zoomAccountId-work',
integrationName: 'zoom-work',
})
assert.equal(auth.mode, 's2s')
assert.equal(auth.accountIdSecret, 'zoomAccountId-work')
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' })
assert.equal(auth.mode, 's2s')
assert.equal(auth.accountIdSecret, 'zoomAccountId-work')
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' })
assert.equal(auth.accountIdSecret, DEFAULT_ZOOM_ACCOUNT_ID_SECRET)
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')
})
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_ACCOUNT_ID_SETUP_URL, /^https:\/\/kody\.codes\/account\/secrets\/new\?/)
assert.match(ZOOM_ACCOUNT_ID_SETUP_URL, /name=zoomAccountId/)
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.match(readme, /https:\/\/kody\.codes\/account\/secrets\/new\?name=zoomAccountId/)
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)
})