import { test, before, after } from 'node:test' import assert from 'node:assert/strict' import fs from 'node:fs/promises' import os from 'node:os' import path from 'node:path' import { randomUUID } from 'node:crypto' import { startFakeGitea } from './helpers/fakeGitea.js' import { startFakeAppwrite } from './helpers/fakeAppwrite.js' let gitea let aw let tmp let originalCwd let previewWorkDir let moveRepoToOrg let isOrgRepo let isInternalRepo let repoLockKey let currentRepoFullName // Schluessel: urspruenglich gesetzter repoFullName -> Appwrite-$id. Wird beim Seeden befuellt // (siehe Controller-Ruling R2) - eine Live-Suche ueber repoFullName wuerde nach einem Umzug // nichts mehr finden, weil sich das Feld dann geaendert hat. const idByRepo = new Map() /** Liefert das gespeicherte Dokument des Repos, ueber den zur Seed-Zeit gebauten Id-Index. */ function doc(name) { return aw.get('websiteProjects', idByRepo.get(name)) } before(async () => { gitea = await startFakeGitea({ repos: [ { full_name: 'JUSN/D-ner-Nation' }, { full_name: 'knso/emmas-schatzkiste' }, { full_name: 'ANDJ/Steuerungstechnik' }, { full_name: 'knso/hytail-ark' }, { full_name: 'WEBklar/schon-da' }, { full_name: 'JUSN/schon-da' }, { full_name: 'WEBklar/von-hand' }, { full_name: 'ANDJ/unberuehrt' }, ], hooks: { 'knso/emmas-schatzkiste': [{ id: 110, url: 'https://project.webklar.com/webhook/gitea' }], 'knso/hytail-ark': [{ id: 7, url: 'https://webklar.com/webhook/hytail-ark/deploy' }], }, // knso/von-hand wurde von Hand bereits nach WEBklar/von-hand verschoben (kein Transfer-Aufruf noetig). redirects: { 'knso/von-hand': 'WEBklar/von-hand', }, }) const repoNames = [ 'JUSN/D-ner-Nation', 'knso/emmas-schatzkiste', 'ANDJ/Steuerungstechnik', 'knso/hytail-ark', 'WEBklar/schon-da', 'JUSN/schon-da', 'knso/von-hand', // Appwrite kennt noch den alten Namen (siehe Brief) 'ANDJ/unberuehrt', ] const websiteProjects = repoNames.map((repoFullName) => { const $id = randomUUID() idByRepo.set(repoFullName, $id) return { $id, repoFullName, projectName: repoFullName.split('/').pop() } }) aw = await startFakeAppwrite({ seed: { websiteProjects } }) originalCwd = process.cwd() tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'repo-move-')) process.chdir(tmp) Object.assign(process.env, { GITEA_BASE_URL: gitea.baseUrl, GITEA_API_TOKEN: 'test-token', APPWRITE_ENDPOINT: aw.endpoint, APPWRITE_API_KEY: 'test-key', PREVIEW_DEPLOY_ROOT: '', TRAEFIK_DYNAMIC_DIR: '', }) // config.js liest die Umgebung beim Import ;({ previewWorkDir } = await import('../server/services/previewDeploy.js')) ;({ moveRepoToOrg, isOrgRepo, isInternalRepo, repoLockKey, currentRepoFullName } = await import( '../server/services/repoMove.js' )) }) after(async () => { process.chdir(originalCwd) await gitea.close() await aw.close() await fs.rm(tmp, { recursive: true, force: true }) }) test('verschiebt Repo aus eigenem Konto', async () => { await fs.mkdir(previewWorkDir('JUSN/D-ner-Nation'), { recursive: true }) const r = await moveRepoToOrg(doc('JUSN/D-ner-Nation')) assert.deepEqual(r, { moved: { from: 'JUSN/D-ner-Nation', to: 'WEBklar/D-ner-Nation' }, warnings: [] }) assert.deepEqual(gitea.calls.find((c) => c.path.endsWith('/transfer')).body, { new_owner: 'WEBklar' }) const d = aw.get('websiteProjects', doc('JUSN/D-ner-Nation').$id) assert.equal(d.repoFullName, 'WEBklar/D-ner-Nation') assert.equal(d.giteaRepoUrl, `${gitea.baseUrl}/WEBklar/D-ner-Nation`) assert.equal(d.giteaRepoName, 'D-ner-Nation') await assert.rejects(fs.access(previewWorkDir('JUSN/D-ner-Nation'))) }) test('löscht den alten Portal-Webhook', async () => { await moveRepoToOrg(doc('knso/emmas-schatzkiste')) assert.deepEqual(gitea.hooksOf('WEBklar/emmas-schatzkiste'), []) }) test('schon in WEBklar: nichts passiert', async () => { assert.deepEqual(await moveRepoToOrg(doc('WEBklar/schon-da')), { moved: null, warnings: [] }) }) test('internes Repo per Liste und per fremdem Webhook', async () => { for (const name of ['ANDJ/Steuerungstechnik', 'knso/hytail-ark']) { assert.deepEqual(await moveRepoToOrg(doc(name)), { moved: null, warnings: ['Internes Repo, bleibt wo es ist.'] }) } assert.equal(gitea.calls.filter((c) => c.path.endsWith('/transfer') && /steuerung|hytail/i.test(c.path)).length, 0) }) test('Name existiert in WEBklar', async () => { const r = await moveRepoToOrg(doc('JUSN/schon-da')) assert.deepEqual(r, { moved: null, warnings: ['In WEBklar gibt es schon ein Repo namens schon-da. Bitte in Gitea umbenennen und dann ‚Pushes nachtragen‘ klicken.'] }) }) test('Weiterleitung korrigiert Projektdaten', async () => { const r = await moveRepoToOrg(doc('knso/von-hand')) assert.deepEqual(r.moved, { from: 'knso/von-hand', to: 'WEBklar/von-hand' }) assert.equal(gitea.calls.filter((c) => c.path.endsWith('/transfer') && c.path.includes('von-hand')).length, 0) assert.equal(aw.get('websiteProjects', doc('knso/von-hand').$id).repoFullName, 'WEBklar/von-hand') }) test('Erkennung ohne Groß-/Kleinschreibung', async () => { assert.equal(isOrgRepo('webklar/x'), true) assert.equal(await isInternalRepo('andj/steuerungstechnik'), true) }) test('repoLockKey', () => { assert.equal(repoLockKey('JUSN/D-ner-Nation'), 'repo:jusn/d-ner-nation') }) test('currentRepoFullName', async () => { assert.equal(await currentRepoFullName('WEBklar/D-ner-Nation'), 'WEBklar/D-ner-Nation') // Eintrag vorhanden assert.equal(await currentRepoFullName('JUSN/D-ner-Nation'), 'WEBklar/D-ner-Nation') // kein Eintrag, Weiterleitung }) test('Gitea nicht erreichbar', async () => { gitea.setDown(true) const r = await moveRepoToOrg(doc('ANDJ/unberuehrt')) gitea.setDown(false) assert.equal(r.moved, null) assert.match(r.warnings[0], /^Gitea ist gerade nicht erreichbar \(.+\)\. Bitte später ‚Pushes nachtragen‘ klicken\.$/) })