import assert from 'node:assert/strict'
import test from 'node:test'
import {
DEFAULT_ACCOUNT_SECRET,
DEFAULT_SERVER_SECRET,
mutationPreview,
parseAction,
requireConfirm,
resolveAccountSecretName,
resolveServerSecretName,
sanitizePostmarkPayload,
secretSetupUrl,
} from './core.ts'
import { deleteDomain } from './domains.ts'
import { sendBatch, sendEmail, sendTemplateEmail } from './emails.ts'
import { deleteServer } from './servers.ts'
test('resolveServerSecretName defaults and suffixes accounts', () => {
assert.equal(resolveServerSecretName(), DEFAULT_SERVER_SECRET)
assert.equal(resolveServerSecretName({ account: 'default' }), DEFAULT_SERVER_SECRET)
assert.equal(resolveServerSecretName({ account: 'work' }), 'postmark-work')
assert.equal(resolveServerSecretName({ secretName: 'postmark-live' }), 'postmark-live')
})
test('resolveAccountSecretName defaults and suffixes accounts', () => {
assert.equal(resolveAccountSecretName(), DEFAULT_ACCOUNT_SECRET)
assert.equal(resolveAccountSecretName({ account: 'work' }), 'postmark-account-work')
assert.equal(
resolveAccountSecretName({ accountSecretName: 'postmark-account-live' }),
'postmark-account-live',
)
})
test('secret names reject Kent-style aliases and invalid labels', () => {
assert.throws(() => resolveServerSecretName({ secretName: 'kentPostmark' }), /postmark or postmark-/)
assert.throws(
() => resolveAccountSecretName({ accountSecretName: 'POSTMARK_ACCOUNT_TOKEN' }),
/postmark-account/,
)
assert.throws(() => resolveServerSecretName({ account: 'youtube-brand!' }), /short label/)
})
test('setup URLs stay prefilled on kody.codes', () => {
assert.match(secretSetupUrl(), /https:\/\/kody\.codes\/account\/secrets\/new\?/)
assert.match(secretSetupUrl(), /name=postmark/)
assert.match(secretSetupUrl(), /allowedHosts=api\.postmarkapp\.com/)
assert.match(secretSetupUrl('postmark-work', 'server'), /name=postmark-work/)
assert.match(secretSetupUrl(DEFAULT_ACCOUNT_SECRET, 'account'), /name=postmark-account/)
assert.match(secretSetupUrl(DEFAULT_ACCOUNT_SECRET, 'account'), /Account/)
})
test('requireConfirm blocks live mutations', () => {
assert.throws(() => requireConfirm({}, 'send email'), /confirm: true/)
requireConfirm({ confirm: true }, 'send email')
})
test('mutationPreview dryRun skips confirm', () => {
const preview = mutationPreview(
{ dryRun: true },
{
action: 'send email',
method: 'POST',
path: '/email',
body: { To: 'person@example.com' },
requireConfirm: true,
tokenKind: 'server',
},
)
assert.deepEqual(preview, {
dryRun: true,
action: 'send email',
method: 'POST',
path: '/email',
body: { To: 'person@example.com' },
tokenKind: 'server',
})
})
test('sanitizePostmarkPayload strips ApiTokens', () => {
const sanitized = sanitizePostmarkPayload({
ID: 12,
Name: 'Transactional',
ApiTokens: ['should-never-leak'],
Servers: [{ ID: 1, ApiTokens: ['also-secret'], apiTokens: ['camel'] }],
}) as {
ID: number
Name: string
ApiTokens?: unknown
Servers: Array<{ ID: number; ApiTokens?: unknown; apiTokens?: unknown }>
}
assert.equal(sanitized.ID, 12)
assert.equal(sanitized.Name, 'Transactional')
assert.equal(sanitized.ApiTokens, undefined)
assert.equal(sanitized.Servers[0]?.ApiTokens, undefined)
assert.equal(sanitized.Servers[0]?.apiTokens, undefined)
assert.equal(sanitized.Servers[0]?.ID, 1)
})
test('parseAction defaults and rejects unknown actions', () => {
assert.equal(parseAction(undefined, ['smoke-test', 'get-server'] as const, 'smoke-test', 'Postmark'), 'smoke-test')
assert.throws(() => parseAction('explode', ['smoke-test'] as const, 'smoke-test', 'Postmark'), /Unknown Postmark/)
})
test('sendEmail dryRun does not require confirm or a network call', async () => {
const preview = await sendEmail({
from: 'Name <hello@example.com>',
to: 'person@example.com',
subject: 'Hello',
textBody: 'Hi',
dryRun: true,
})
assert.deepEqual(preview, {
dryRun: true,
action: 'send email',
method: 'POST',
path: '/email',
tokenKind: 'server',
body: {
From: 'Name <hello@example.com>',
To: 'person@example.com',
Subject: 'Hello',
HtmlBody: undefined,
TextBody: 'Hi',
Cc: undefined,
Bcc: undefined,
ReplyTo: undefined,
Tag: undefined,
MessageStream: undefined,
TrackOpens: undefined,
TrackLinks: undefined,
Metadata: undefined,
Headers: undefined,
Attachments: undefined,
},
})
})
test('sendEmail live path requires from, to, body, and confirm', async () => {
await assert.rejects(
() => sendEmail({ to: 'person@example.com', subject: 'Hi', textBody: 'x' } as never),
/from is required/,
)
await assert.rejects(
() =>
sendEmail({
from: 'Name <hello@example.com>',
to: 'person@example.com',
subject: 'Hi',
textBody: 'x',
}),
/confirm: true/,
)
})
test('sendBatch and sendTemplateEmail dryRun stay local', async () => {
const batch = await sendBatch({
dryRun: true,
emails: [
{
from: 'Name <hello@example.com>',
to: 'one@example.com',
subject: 'One',
textBody: '1',
},
],
})
assert.equal((batch as { dryRun: boolean }).dryRun, true)
assert.equal((batch as { path: string }).path, '/email/batch')
const templated = await sendTemplateEmail({
from: 'Name <hello@example.com>',
to: 'person@example.com',
templateAlias: 'welcome',
templateModel: { name: 'Ada' },
dryRun: true,
})
assert.equal((templated as { dryRun: boolean }).dryRun, true)
assert.equal((templated as { path: string }).path, '/email/withTemplate')
})
test('delete helpers require confirm unless dryRun', async () => {
await assert.rejects(() => deleteServer({ serverId: 9 }), /confirm: true/)
await assert.rejects(() => deleteDomain({ domainId: 3 }), /confirm: true/)
const serverPreview = await deleteServer({ serverId: 9, dryRun: true })
assert.equal((serverPreview as { dryRun: boolean }).dryRun, true)
const domainPreview = await deleteDomain({ domainId: 3, dryRun: true })
assert.equal((domainPreview as { dryRun: boolean }).dryRun, true)
})