Zusatzspeicher: Plus sofort anteilig, Minus zum Monatsende, Aufgaben für konsoleH
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
887e1e54a0
commit
379cc26dcd
11 files changed
+1531
-27
No files matched your search
@@ -48,6 +48,12 @@ test('Appwrite-Nachbau: failNext liefert einmal den gewuenschten Fehler', async
|
||||
aw.failNext({ collection: 'hourCredits', method: 'POST', status: 404 })
|
||||
assert.equal((await getDocument('subscriptions', 'sub_fail')).$id, 'sub_fail')
|
||||
await assert.rejects(createDocument('hourCredits', { customerId: 'c9' }, 'hc_1'), (e) => e.status === 404)
|
||||
// after: so viele passende Anfragen gehen vorher noch durch (Task 8: erst die zweite PATCH scheitert).
|
||||
// adminFetch bildet jeden 5xx-Status auf status 500 ab.
|
||||
aw.failNext({ collection: 'subscriptions', method: 'GET', status: 503, after: 1 })
|
||||
assert.equal((await getDocument('subscriptions', 'sub_fail')).$id, 'sub_fail')
|
||||
await assert.rejects(getDocument('subscriptions', 'sub_fail'), (e) => e.status === 500 && /503/.test(e.message))
|
||||
assert.equal((await getDocument('subscriptions', 'sub_fail')).$id, 'sub_fail')
|
||||
})
|
||||
|
||||
test('Stripe-Nachbau: getStripe spricht mit 127.0.0.1', async () => {
|
||||
@@ -64,3 +70,94 @@ test('useTestEnv lehnt fremde Hosts ab', () => {
|
||||
assert.throws(() => useTestEnv({ appwriteUrl: 'https://ticket.webklar.com/v1', stripeUrl: st.url }))
|
||||
assert.equal(new URL(process.env.APPWRITE_ENDPOINT).hostname, '127.0.0.1')
|
||||
})
|
||||
|
||||
// F17 (Task 8): POST /v1/subscriptions/:id mit Positionen (anlegen per Preis, aendern per ID), Metadaten
|
||||
// (leerer Wert loescht wie bei Stripe) und einmaligem Zusatz nextSubscriptionUpdate fuer die Antwort
|
||||
test('Stripe-Nachbau: Abo-Update mit Positionen, Metadaten, einmaliger Zusatz', async () => {
|
||||
const stripe = getStripe()
|
||||
st.prices.set('price_a', { id: 'price_a', object: 'price', active: true, lookup_key: 'lookup_a', unit_amount: 100 })
|
||||
st.subscriptions.set('sub_u', {
|
||||
id: 'sub_u',
|
||||
object: 'subscription',
|
||||
status: 'active',
|
||||
metadata: { keep: '1', drop: 'x' },
|
||||
items: { object: 'list', data: [{ id: 'si_1', object: 'subscription_item', quantity: 1, price: { id: 'price_0', object: 'price' } }] },
|
||||
})
|
||||
const added = await stripe.subscriptions.update('sub_u', {
|
||||
items: [{ price: 'price_a', quantity: 2 }],
|
||||
metadata: { drop: '', neu: 'y' },
|
||||
proration_behavior: 'none',
|
||||
})
|
||||
assert.equal(added.items.data.length, 2)
|
||||
const item = added.items.data[1]
|
||||
assert.equal(item.quantity, 2)
|
||||
assert.equal(item.price.id, 'price_a')
|
||||
assert.equal(item.price.lookup_key, 'lookup_a')
|
||||
assert.match(item.id, /^si_/)
|
||||
assert.deepEqual(added.metadata, { keep: '1', neu: 'y' })
|
||||
assert.equal(added.status, 'active')
|
||||
assert.equal(st.calls.at(-1).body['items[0][price]'], 'price_a')
|
||||
|
||||
const changed = await stripe.subscriptions.update('sub_u', { items: [{ id: item.id, quantity: 0 }] })
|
||||
assert.equal(changed.items.data[1].quantity, 0)
|
||||
assert.equal((await stripe.subscriptions.retrieve('sub_u')).items.data[1].quantity, 0, 'gespeichert')
|
||||
|
||||
// Wie Stripe: derselbe Preis nicht zweimal, unbekannter Preis oder Posten 400/404, unbekanntes Abo 404
|
||||
await assert.rejects(stripe.subscriptions.update('sub_u', { items: [{ price: 'price_a', quantity: 1 }] }), (e) => e.statusCode === 400)
|
||||
await assert.rejects(stripe.subscriptions.update('sub_u', { items: [{ price: 'price_gibt_es_nicht', quantity: 1 }] }), (e) => e.statusCode === 400)
|
||||
await assert.rejects(stripe.subscriptions.update('sub_u', { items: [{ id: 'si_gibt_es_nicht', quantity: 1 }] }), (e) => e.statusCode === 400)
|
||||
await assert.rejects(stripe.subscriptions.update('sub_gibt_es_nicht', { metadata: { a: 'b' } }), (e) => e.statusCode === 404)
|
||||
|
||||
// Kuendigungsfelder wie cancelAtPeriodEnd/cancelSubscriptionAt sie setzen
|
||||
const cancelled = await stripe.subscriptions.update('sub_u', { cancel_at_period_end: true, metadata: { cancelSource: 'test' } })
|
||||
assert.equal(cancelled.cancel_at_period_end, true)
|
||||
assert.equal(cancelled.metadata.cancelSource, 'test')
|
||||
const dated = await stripe.subscriptions.update('sub_u', { cancel_at: 1800000000, proration_behavior: 'none' })
|
||||
assert.equal(dated.cancel_at, 1800000000)
|
||||
|
||||
// nextSubscriptionUpdate: einmalig in die naechste Antwort gemischt, nicht gespeichert
|
||||
st.nextSubscriptionUpdate = { pending_update: { expires_at: 1 }, latest_invoice: { status: 'open', hosted_invoice_url: 'https://pay.example/inv' } }
|
||||
const pending = await stripe.subscriptions.update('sub_u', { metadata: { a: 'b' } })
|
||||
assert.deepEqual(pending.pending_update, { expires_at: 1 })
|
||||
assert.equal(pending.latest_invoice.hosted_invoice_url, 'https://pay.example/inv')
|
||||
assert.equal(pending.metadata.a, 'b')
|
||||
assert.equal((await stripe.subscriptions.retrieve('sub_u')).pending_update, undefined)
|
||||
assert.equal((await stripe.subscriptions.update('sub_u', { metadata: { a: 'c' } })).pending_update, undefined)
|
||||
assert.equal(st.nextSubscriptionUpdate, null)
|
||||
})
|
||||
|
||||
// F17: GET /v1/invoices/:id bettet wie Stripe hoechstens 10 Zeilen ein, GET /v1/invoices/:id/lines blaettert
|
||||
test('Stripe-Nachbau: Rechnung bettet höchstens 10 Zeilen ein, /lines blättert', async () => {
|
||||
const stripe = getStripe()
|
||||
st.invoices.set('in_p', { id: 'in_p', object: 'invoice', lines: { object: 'list', data: Array.from({ length: 12 }, (_, i) => ({ amount: i })) } })
|
||||
const invoice = await stripe.invoices.retrieve('in_p')
|
||||
assert.equal(invoice.lines.data.length, 10)
|
||||
assert.equal(invoice.lines.has_more, true)
|
||||
assert.ok(invoice.lines.data.every((l) => l.id && l.object === 'line_item'))
|
||||
const page = await stripe.invoices.listLineItems('in_p', { limit: 5 })
|
||||
assert.equal(page.data.length, 5)
|
||||
assert.equal(page.has_more, true)
|
||||
const all = await stripe.invoices.listLineItems('in_p', { limit: 5 }).autoPagingToArray({ limit: 100 })
|
||||
assert.deepEqual(all.map((l) => l.amount), [...Array(12).keys()])
|
||||
const small = await stripe.invoices.retrieve('in_p') // unveraendert seedbar: die Quelle behaelt alle Zeilen
|
||||
assert.equal(small.lines.data.length, 10)
|
||||
st.invoices.set('in_q', { id: 'in_q', object: 'invoice', lines: { object: 'list', data: [{ amount: 1 }] } })
|
||||
assert.equal((await stripe.invoices.retrieve('in_q')).lines.has_more, false)
|
||||
await assert.rejects(stripe.invoices.listLineItems('in_nix'), (e) => e.statusCode === 404)
|
||||
})
|
||||
|
||||
// F17: POST /v1/invoices/create_preview liefert die vorab hinterlegte Vorschau (Objekt oder Funktion des Aufrufs)
|
||||
test('Stripe-Nachbau: Rechnungsvorschau aus dem Seed', async () => {
|
||||
const stripe = getStripe()
|
||||
const params = { subscription: 'sub_u', subscription_details: { items: [{ id: 'si_1', quantity: 2 }], proration_behavior: 'create_prorations', proration_date: 1 } }
|
||||
const empty = await stripe.invoices.createPreview(params)
|
||||
assert.deepEqual(empty.lines.data, [])
|
||||
st.invoicePreview = { object: 'invoice', lines: { object: 'list', data: [{ amount: 42 }], has_more: false } }
|
||||
assert.equal((await stripe.invoices.createPreview(params)).lines.data[0].amount, 42)
|
||||
assert.equal(st.calls.at(-1).body['subscription_details[items][0][quantity]'], '2')
|
||||
assert.equal(st.calls.at(-1).body['subscription_details[proration_behavior]'], 'create_prorations')
|
||||
st.invoicePreview = (body) => ({ object: 'invoice', lines: { data: [{ amount: Number(body.subscription_details.items[0].quantity) * 100 }] } })
|
||||
assert.equal((await stripe.invoices.createPreview(params)).lines.data[0].amount, 200)
|
||||
await assert.rejects(stripe.invoices.createPreview({ subscription: 'sub_nix' }), (e) => e.statusCode === 404)
|
||||
st.invoicePreview = null
|
||||
})
|
||||
Reference in new issue
Block a user