Files

144 lines
5.1 KiB
JavaScript

import { test, before, after } from 'node:test'
import assert from 'node:assert/strict'
import { createHmac } from 'node:crypto'
import fs from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import express from 'express'
import { startFakeGitea } from './helpers/fakeGitea.js'
import { startFakeAppwrite } from './helpers/fakeAppwrite.js'
import { mailSink } from './support/mailSink.js'
const WEBHOOK_TOKEN = 's3cret'
const REPO = 'WEBklar/amin-friseur'
let gitea
let aw
let tmp
let originalCwd
let server
let baseUrl
let setMailTransportForTests
function pushPayload({ sha, pusher }) {
return {
ref: 'refs/heads/main',
repository: { full_name: REPO, name: 'amin-friseur', html_url: `${gitea.baseUrl}/${REPO}` },
commits: [{ id: sha, message: `Commit ${sha}`, author: { name: pusher }, url: `${gitea.baseUrl}/${REPO}/commit/${sha}` }],
pusher: { username: pusher },
after: sha,
}
}
async function webhook(payload) {
const bodyText = JSON.stringify(payload)
const signature = createHmac('sha256', WEBHOOK_TOKEN).update(bodyText).digest('hex')
const res = await fetch(`${baseUrl}/webhook/gitea`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Gitea-Signature': signature },
body: bodyText,
})
return { status: res.status, body: await res.json().catch(() => null) }
}
/** Die Mail geht ohne await auf die Webhook-Antwort raus - kurz warten, bis sie da ist. */
async function waitFor(check, ms = 1000) {
const end = Date.now() + ms
while (Date.now() < end) {
if (check()) return true
await new Promise((r) => setTimeout(r, 10))
}
return check()
}
before(async () => {
gitea = await startFakeGitea({ repos: [{ full_name: REPO }] })
aw = await startFakeAppwrite({
seed: {
websiteProjects: [{ $id: 'p1', repoFullName: REPO, projectName: 'amin-friseur', customerId: 'c1', ticketId: 't1' }],
workorders: [{ $id: 't1', woid: '100118', customerName: 'Amin Friseur', topic: 'Website', status: 'Open' }],
customers: [{ $id: 'c1', name: 'Amin Friseur' }],
employees: [{ $id: 'k', displayName: 'Kenso Grimm', shortcode: 'KNSO', email: 'kenso@webklar.com' }],
},
})
originalCwd = process.cwd()
tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'push-zeitmail-'))
process.chdir(tmp)
Object.assign(process.env, {
GITEA_BASE_URL: gitea.baseUrl,
GITEA_API_TOKEN: 'test-token',
GITEA_WEBHOOK_TOKEN: WEBHOOK_TOKEN,
APPWRITE_ENDPOINT: aw.endpoint,
APPWRITE_API_KEY: 'test-key',
PREVIEW_DEPLOY_ROOT: '',
TRAEFIK_DYNAMIC_DIR: '',
PUSH_TIME_SECRET: 'w'.repeat(40),
})
const [{ default: giteaWebhookRoutes }, mailer] = await Promise.all([
import('../server/routes/webhook/gitea.js'),
import('../server/services/mailer.js'),
])
setMailTransportForTests = mailer.setMailTransportForTests
const app = express()
app.use(express.json({ limit: '2mb', verify: (req, _res, buf) => { req.rawBody = buf } }))
app.use('/webhook', giteaWebhookRoutes)
server = await new Promise((resolve) => {
const s = app.listen(0, '127.0.0.1', () => resolve(s))
})
baseUrl = `http://127.0.0.1:${server.address().port}`
})
after(async () => {
setMailTransportForTests?.(null)
if (server) await new Promise((resolve) => server.close(resolve))
if (originalCwd) process.chdir(originalCwd)
await gitea?.close()
await aw?.close()
if (tmp) await fs.rm(tmp, { recursive: true, force: true })
})
test('Push von knso → eine Zeitmail, Antwort ohne Dokumente', async () => {
const sink = mailSink()
setMailTransportForTests(sink)
const r = await webhook(pushPayload({ sha: 'aaaa1111', pusher: 'knso' }))
assert.equal(r.status, 200)
assert.equal(r.body.gitWorksheet.created, true)
assert.equal(r.body.gitWorksheet.ticket, undefined)
assert.equal(r.body.gitWorksheet.worksheet, undefined)
assert.ok(r.body.gitWorksheet.worksheetId)
assert.ok(await waitFor(() => sink.sent.length === 1))
assert.deepEqual(sink.sent[0].to.map((t) => t.address), ['kenso@webklar.com'])
assert.match(sink.sent[0].subject, /^\[Zeit\] #100118 Amin Friseur/)
assert.match(sink.sent[0].html, new RegExp(`/zeit/[^"?]+\\?min=60`))
})
test('derselbe Push erneut → keine zweite Mail', async () => {
const sink = mailSink()
setMailTransportForTests(sink)
const r = await webhook(pushPayload({ sha: 'aaaa1111', pusher: 'knso' }))
assert.equal(r.status, 200)
assert.equal(r.body.gitWorksheet.reason, 'duplicate_commit')
await new Promise((res) => setTimeout(res, 100))
assert.equal(sink.sent.length, 0)
})
test('System-Konto → keine Mail', async () => {
const sink = mailSink()
setMailTransportForTests(sink)
const r = await webhook(pushPayload({ sha: 'bbbb2222', pusher: 'webklar-system' }))
assert.equal(r.status, 200)
await new Promise((res) => setTimeout(res, 100))
assert.equal(sink.sent.length, 0)
})
test('Mailfehler ändert die Webhook-Antwort nicht', async () => {
setMailTransportForTests({ sendMail: async () => { throw new Error('SMTP kaputt') } })
const r = await webhook(pushPayload({ sha: 'cccc3333', pusher: 'knso' }))
assert.equal(r.status, 200)
assert.equal(r.body.gitWorksheet.created, true)
})