Abo-API: Nachweis-ID nach Appwrite-Regeln, Webhook bei dauerhaften Fehlern, Tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Fable 5.1 committed 2026-09-28 10:19:05 +00:00
1 parent 7df0b72019
commit 887e1e54a0
10 files changed
+409 -107

No files matched your search

+32 -1
View File
@@ -3,11 +3,26 @@ import http from 'node:http'
/**
* Appwrite-Nachbau für Tests: die REST-Pfade, die server/services/appwriteAdmin.js benutzt, im
* Speicher. Mit schema = { collection: ['attr', ...] } lehnt er unbekannte Attribute ab wie
* Appwrite ("Invalid document structure: Unknown attribute").
* Appwrite ("Invalid document structure: Unknown attribute"). Dokument-IDs prüft er wie Appwrite
* (höchstens 36 Zeichen aus a-z, A-Z, 0-9, Punkt, Bindestrich, Unterstrich, kein Sonderzeichen am
* Anfang). failNext({ collection?, method?, status, message? }) lässt die nächste passende Anfrage
* mit diesem Status scheitern (Fehlerpfade wie "Appwrite nicht erreichbar" oder "Collection fehlt").
*/
export async function startAppwriteMock({ schema = {} } = {}) {
const collections = new Map()
let sequence = 0
let pendingFailure = null
// Wie Appwrite (Validator UID): 'unique()' ist erlaubt, sonst [A-Za-z0-9][A-Za-z0-9._-]{0,35}
const validDocumentId = (id) => id === 'unique()' || (typeof id === 'string' && /^[A-Za-z0-9][A-Za-z0-9._-]{0,35}$/.test(id))
const invalidDocumentId = [
400,
{
message: 'Invalid `documentId` param: UID must contain at most 36 chars. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char',
code: 400,
type: 'general_argument_invalid',
},
]
const docs = (collection) => {
if (!collections.has(collection)) collections.set(collection, new Map())
@@ -83,9 +98,21 @@ export async function startAppwriteMock({ schema = {} } = {}) {
const store = docs(collection)
const notFound = [404, { message: 'Document with the requested ID could not be found.', code: 404, type: 'document_not_found' }]
// Eingeplanter Fehler (failNext): genau eine passende Anfrage scheitert
if (
pendingFailure &&
(!pendingFailure.collection || pendingFailure.collection === collection) &&
(!pendingFailure.method || pendingFailure.method === req.method)
) {
const { status, message } = pendingFailure
pendingFailure = null
return [status, { message: message || `Nachbau: eingeplanter Fehler ${status}`, code: status, type: 'general_server_error' }]
}
if (!id && req.method === 'GET') return list(collection, url.searchParams.getAll('queries[]'))
if (!id && req.method === 'POST') {
const { documentId, data = {} } = await readBody(req)
if (!validDocumentId(documentId)) return invalidDocumentId
if (store.has(documentId)) {
return [409, { message: 'Document with the requested ID already exists.', code: 409, type: 'document_already_exists' }]
}
@@ -128,6 +155,10 @@ export async function startAppwriteMock({ schema = {} } = {}) {
url: `http://127.0.0.1:${server.address().port}/v1`,
docs: (collection) => new Map([...docs(collection)].map(([id, doc]) => [id, publicDoc(doc)])),
seed,
/** Naechste passende Dokument-Anfrage scheitert mit status (einmalig). */
failNext: ({ collection = null, method = null, status = 500, message = '' }) => {
pendingFailure = { collection, method, status, message }
},
close: () => new Promise((resolve) => server.close(resolve)),
}
}