Subdomain-Vorschlag und Zuweisungs-Logik mit Tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-30 10:41:35 +00:00
1 parent c24b3d43c8
commit cc59e7978e
6 files changed
+174 -1

No files matched your search

+20
View File
@@ -0,0 +1,20 @@
import esbuild from 'esbuild'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const repoRoot = path.resolve(__dirname, '../..')
export async function loadModule(relPathFromRepoRoot) {
const entryPoint = path.join(repoRoot, relPathFromRepoRoot)
const result = await esbuild.build({
entryPoints: [entryPoint],
bundle: true,
format: 'esm',
write: false,
platform: 'neutral',
})
const code = result.outputFiles[0].text
const dataUrl = `data:text/javascript;base64,${Buffer.from(code).toString('base64')}`
return import(dataUrl)
}
+61
View File
@@ -0,0 +1,61 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { loadModule } from './load.mjs'
const { isOutsideOrg, needsPreviewButton, assignSummary, assignSequentially } = await loadModule(
'src/lib/projectAssign.js'
)
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
test('isOutsideOrg', () => {
assert.equal(isOutsideOrg('JUSN/D-ner-Nation'), true)
assert.equal(isOutsideOrg('webklar/x'), false)
assert.equal(isOutsideOrg(''), false)
})
test('needsPreviewButton', () => {
assert.equal(needsPreviewButton({ previewUrl: 'https://x', status: 'deployed' }), false)
assert.equal(needsPreviewButton({ previewUrl: '', status: 'synced' }), true)
assert.equal(needsPreviewButton({ previewUrl: 'https://x', status: 'pending' }), true)
})
test('assignSummary', () => {
assert.deepEqual(
assignSummary('Döner Nation', {
moved: { from: 'JUSN/D-ner-Nation', to: 'WEBklar/D-ner-Nation' },
backfilled: 1,
warnings: [],
}),
{ text: 'Döner Nation: nach WEBklar verschoben · 1 Push nachgetragen', warnings: [] }
)
assert.deepEqual(
assignSummary('X', { moved: null, backfilled: 0, warnings: ['Internes Repo, bleibt wo es ist.'] }),
{ text: 'X: keine neuen Pushes', warnings: ['Internes Repo, bleibt wo es ist.'] }
)
assert.equal(assignSummary('X', { moved: null, backfilled: 3, warnings: [] }).text, 'X: 3 Pushes nachgetragen')
assert.deepEqual(assignSummary('X', { error: 'boom' }), { text: 'X: Zuweisen fehlgeschlagen – boom', warnings: [] })
})
test('nacheinander und weiter nach Fehler', async () => {
const log = []
const fn = async (id) => {
log.push(id + ':start')
await sleep(10)
log.push(id + ':end')
if (id === 'b') throw new Error('boom')
return { backfilled: 0 }
}
const r = await assignSequentially(['a', 'b', 'c'], { ticketId: 't1' }, fn)
assert.deepEqual(log, ['a:start', 'a:end', 'b:start', 'b:end', 'c:start', 'c:end'])
assert.deepEqual(
r.map((x) => [x.projectId, x.ok, x.error]),
[
['a', true, undefined],
['b', false, 'boom'],
['c', true, undefined],
]
)
})
+25
View File
@@ -0,0 +1,25 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { loadModule } from './load.mjs'
const { suggestSubdomain } = await loadModule('src/lib/subdomain.js')
test('Kundenname vor Projektname', () => {
assert.equal(suggestSubdomain('Döner Nation', 'D-ner-Nation'), 'doener-nation')
})
test('Apostroph', () => {
assert.equal(suggestSubdomain("Emma's Schatzkiste", ''), 'emmas-schatzkiste')
})
test('Sonderzeichen', () => {
assert.equal(suggestSubdomain('', 'Café Müller & Söhne + Straße'), 'cafe-mueller-soehne-strasse')
})
test('leer', () => {
assert.equal(suggestSubdomain('', ''), '')
})
test('63 Zeichen ohne Bindestrich am Ende', () => {
assert.equal(suggestSubdomain('a'.repeat(62) + ' b', ''), 'a'.repeat(62))
})