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 { extractListItems, extractPageInfo, mapArticle, mapContact, mapConversation, mapMe, } from './models.ts' import { ACCESS_TOKEN_SETUP_URL, DEFAULT_TOKEN_SECRET, INTERCOM_CALLBACK_URL, OAUTH_CONNECT_URL, accessTokenSetupUrl, inferOperationFromPath, intercomUrl, mutationPreview, normalizeIntercomPath, oauthConnectUrl, permissionForOperation, resolveApiBaseUrl, resolveIntegrationName, resolveSecretName, } from './setup.ts' const here = dirname(fileURLToPath(import.meta.url)) const root = join(here, '..') test('secret and integration names default and suffix accounts', () => { assert.equal(resolveIntegrationName(), 'intercom') assert.equal(resolveSecretName(), DEFAULT_TOKEN_SECRET) assert.equal(resolveIntegrationName({ account: 'default' }), 'intercom') assert.equal(resolveIntegrationName({ account: 'work' }), 'intercom-work') assert.equal(resolveSecretName({ account: 'work' }), 'intercomAccessToken-work') assert.equal(resolveIntegrationName({ integrationName: 'intercom-brand' }), 'intercom-brand') assert.equal(resolveSecretName({ secretName: 'intercomAccessToken-brand' }), 'intercomAccessToken-brand') }) test('setup URLs are prefilled for OAuth and access token', () => { assert.match(OAUTH_CONNECT_URL, /provider=intercom/) assert.match(OAUTH_CONNECT_URL, /authorizeUrl=https%3A%2F%2Fapp\.intercom\.com%2Foauth/) assert.match(OAUTH_CONNECT_URL, /tokenUrl=https%3A%2F%2Fapi\.intercom\.io%2Fauth%2Feagle%2Ftoken/) assert.match(OAUTH_CONNECT_URL, /apiBaseUrl=https%3A%2F%2Fapi\.intercom\.io/) assert.match(OAUTH_CONNECT_URL, /allowedHosts=api\.intercom\.io/) assert.equal(INTERCOM_CALLBACK_URL, 'https://kody.codes/connect/oauth') assert.match(ACCESS_TOKEN_SETUP_URL, /name=intercomAccessToken/) assert.match(ACCESS_TOKEN_SETUP_URL, /allowedHosts=api\.intercom\.io/) assert.match(ACCESS_TOKEN_SETUP_URL, /scope=user/) assert.match(oauthConnectUrl('intercom-work'), /provider=intercom-work/) assert.match(accessTokenSetupUrl('intercomAccessToken-work'), /name=intercomAccessToken-work/) assert.equal(resolveApiBaseUrl({ region: 'eu' }), 'https://api.eu.intercom.io') assert.match(oauthConnectUrl('intercom', { region: 'eu' }), /app\.eu\.intercom\.com/) assert.equal(resolveApiBaseUrl({ region: 'au' }), 'https://api.au.intercom.io') }) test('mutationPreview dryRun skips confirm', () => { const preview = mutationPreview( { dryRun: true }, { method: 'POST', path: '/contacts', url: 'https://api.intercom.io/contacts', body: { email: 'pat@example.com' }, }, ) assert.deepEqual(preview, { dryRun: true, method: 'POST', path: '/contacts', url: 'https://api.intercom.io/contacts', body: { email: 'pat@example.com' }, }) }) test('mutationPreview requires confirm for live writes', () => { assert.throws( () => mutationPreview( {}, { method: 'POST', path: '/articles', url: 'https://api.intercom.io/articles', body: { title: 'Reset your password' }, }, ), /confirm: true/, ) assert.equal( mutationPreview( { confirm: true }, { method: 'POST', path: '/articles', url: 'https://api.intercom.io/articles', body: { title: 'Reset your password' }, }, ), null, ) }) test('paths stay on Intercom API hosts and infer operations', () => { assert.equal(normalizeIntercomPath('/contacts'), '/contacts') assert.match(intercomUrl('/contacts', { per_page: 5 }), /^https:\/\/api\.intercom\.io\/contacts\?per_page=5$/) assert.match( intercomUrl('/contacts', { per_page: 5 }, { region: 'eu' }), /^https:\/\/api\.eu\.intercom\.io\/contacts\?per_page=5$/, ) assert.throws(() => normalizeIntercomPath('https://example.com/contacts'), /api\.intercom\.io/) assert.equal(inferOperationFromPath('GET', '/contacts'), 'contacts.read') assert.equal(inferOperationFromPath('POST', '/contacts/search'), 'contacts.read') assert.equal(inferOperationFromPath('POST', '/contacts'), 'contacts.write') assert.equal(inferOperationFromPath('POST', '/conversations/1/reply'), 'conversations.write') assert.equal(inferOperationFromPath('GET', '/articles/9'), 'articles.read') assert.equal(permissionForOperation('articles.write'), 'Read and Write Articles') }) test('summaries stay generic and require ids from the payload', () => { assert.equal(mapContact({ email: 'pat@example.com' }), null) assert.equal(mapContact({ id: 'contact-from-caller', email: 'pat@example.com' })?.email, 'pat@example.com') assert.equal(mapConversation({ title: 'Help' }), null) assert.equal(mapConversation({ id: 'conversation-from-caller', state: 'open' })?.state, 'open') assert.equal(mapArticle({ title: 'Reset' }), null) assert.equal(mapArticle({ id: 'article-from-caller', title: 'Reset' })?.title, 'Reset') assert.deepEqual(mapMe({ id: 'admin-from-caller', type: 'admin', app: { region: 'US' } }).app.region, 'US') assert.deepEqual( extractListItems({ type: 'list', data: [{ id: '1' }] }), [{ id: '1' }], ) assert.equal( extractPageInfo({ pages: { next: { starting_after: 'cursor-from-caller' }, page: 1, per_page: 20, total_pages: 2 }, }).startingAfter, 'cursor-from-caller', ) }) test('package is public MIT official intercom with disabled job', () => { const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) assert.equal(manifest.name, '@kody/intercom') assert.equal(manifest.license, 'MIT') assert.equal(manifest.private, false) assert.equal(manifest.kody.id, 'intercom') assert.equal(manifest.kody.jobs['workspace-health'].enabled, false) const readme = readFileSync(join(root, 'README.md'), 'utf8') assert.match(readme, /## Intent/) assert.match(readme, /https:\/\/kody\.codes\/@kody\/intercom/) assert.match(readme, /\/account\/secrets\/new\?name=intercomAccessToken/) assert.match(readme, /\/connect\/oauth\?provider=intercom/) assert.doesNotMatch(readme, /\/community\/[0-9a-f-]{36}/) assert.doesNotMatch(readme, /hello@kentcdodds\.com/) assert.doesNotMatch(readme, /betterWithKent/) assert.doesNotMatch(readme, /[0-9a-f]{24}/) const icon = readFileSync(join(root, 'community-icon.svg'), 'utf8') assert.match(icon, /Official Intercom logomark/) assert.match(icon, /#1A1A19/i) })