Verwaiste Repo-Platzhalter blockieren keine Zuordnung mehr

Ein in Gitea geloeschtes Org-Repo liess sein unverknuepftes Projekt-Dokument
liegen. Der Unique-Index auf repoFullName ignoriert Gross-/Kleinschreibung,
darum scheiterte beim Zuweisen eines gleich benannten neuen Repos das
Umbenennen des Projekts; alle Pushes landeten im Platzhalter ohne Ticket,
also ohne Arbeitsblatt und ohne Push-Zeitmail (WEBklar/Schmacko-napf).

- repoMove: vor dem Umbenennen ein unverknuepftes fremdes Dokument mit dem
  Zielnamen entfernen (verknuepfte bleiben unangetastet)
- Webhook repository/deleted: unverknuepfte Platzhalter des geloeschten
  Repos entfernen, nur wenn Gitea das Repo wirklich nicht mehr kennt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-01 21:58:11 +00:00
1 parent a64a996291
commit 6718018211
5 files changed
+132 -7

No files matched your search

+21 -1
View File
@@ -37,7 +37,11 @@ before(async () => {
gitea = await startFakeGitea({ repos: [] })
aw = await startFakeAppwrite({
seed: {
websiteProjects: [],
websiteProjects: [
{ $id: 'waise', repoFullName: 'WEBklar/schmacko-napf', projectName: 'schmacko-napf', ticketId: '', customerId: '' },
{ $id: 'verknuepft', repoFullName: 'WEBklar/mit-ticket', projectName: 'mit-ticket', ticketId: 't-9' },
{ $id: 'lebt', repoFullName: 'WEBklar/lebt-noch', projectName: 'lebt-noch' },
],
employees: [{ $id: 'k', displayName: 'Kenso Grimm', shortcode: 'KNSO', email: 'kenso@webklar.com' }],
},
})
@@ -173,3 +177,19 @@ test('Job: Appwrite-Fehler bricht den Lauf ab, ohne zu werfen', async () => {
} finally { console.error = silent }
assert.ok(awUrl)
})
test('repository/deleted entfernt das unverknuepfte Platzhalter-Dokument, verknuepfte und lebende bleiben', async () => {
gitea.addRepo({ full_name: 'WEBklar/lebt-noch' })
const del = (full) => hook('repository', { ...created(full), action: 'deleted', sender: { login: 'ANDJ' } })
const r = await del('WEBklar/schmacko-napf')
assert.equal(r.status, 200)
assert.equal(r.body.removed, 1)
assert.equal(aw.get('websiteProjects', 'waise'), undefined)
assert.equal((await del('WEBklar/mit-ticket')).body.removed, 0)
assert.ok(aw.get('websiteProjects', 'verknuepft'))
assert.equal((await del('WEBklar/lebt-noch')).body.removed, 0)
assert.ok(aw.get('websiteProjects', 'lebt'))
})
+31
View File
@@ -45,6 +45,8 @@ before(async () => {
{ full_name: 'JUSN/verboten' },
{ full_name: 'JUSN/Case-Test' },
{ full_name: 'knso/rausgezogen' },
{ full_name: 'ANDJ/Schmacko-napf' },
{ full_name: 'JUSN/belegt' },
],
// C1: Hooks kommen (wie im echten Gitea) OHNE Top-Level-`url` an - `url` hier ist nur die
// Seed-Abkuerzung, gespeichert/ausgeliefert wird das Ziel ausschliesslich unter `config.url`.
@@ -85,6 +87,20 @@ before(async () => {
return { $id, repoFullName, projectName: repoFullName.split('/').pop() }
})
// Andrejs Fall (2026-10-01): WEBklar/Schmacko-napf wurde angelegt und wieder geloescht, sein
// Platzhalter-Dokument blieb liegen. Das neue, zugewiesene ANDJ/Schmacko-napf muss den Namen bekommen.
const linked = { ticketId: 't-1', customerId: 'c-1', kind: 'website' }
for (const [repoFullName, extra] of [
['ANDJ/Schmacko-napf', linked],
['WEBklar/Schmacko-napf', {}],
['JUSN/belegt', linked],
['WEBklar/belegt', { ticketId: 't-2' }],
]) {
const $id = randomUUID()
idByRepo.set(repoFullName, $id)
websiteProjects.push({ $id, repoFullName, projectName: repoFullName.split('/').pop(), ...extra })
}
aw = await startFakeAppwrite({ seed: { websiteProjects } })
originalCwd = process.cwd()
@@ -236,3 +252,18 @@ test('R10-Ein-Zeiler: Transfer meldet Erfolg, aber unveraenderten full_name -> k
false
)
})
test('verwaistes Platzhalter-Dokument mit dem Zielnamen wird entfernt, das zugewiesene Projekt uebernimmt den Namen', async () => {
const orphanId = idByRepo.get('WEBklar/Schmacko-napf')
const r = await moveRepoToOrg(doc('ANDJ/Schmacko-napf'))
assert.deepEqual(r, { moved: { from: 'ANDJ/Schmacko-napf', to: 'WEBklar/Schmacko-napf' }, warnings: [] })
assert.equal(aw.get('websiteProjects', orphanId), undefined)
const d = doc('ANDJ/Schmacko-napf')
assert.equal(d.repoFullName, 'WEBklar/Schmacko-napf')
assert.equal(d.ticketId, 't-1')
})
test('verknuepftes fremdes Dokument mit dem Zielnamen bleibt unangetastet', async () => {
await moveRepoToOrg(doc('JUSN/belegt'))
assert.equal(doc('WEBklar/belegt').ticketId, 't-2')
})