Shop C3: Produkte normalisieren, Käufer-Sicht und Speichern (shopProducts)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
129f8de278
commit
bea9f3cfba
2 files changed
+885
No files matched your search
@@ -0,0 +1,398 @@
|
||||
import { test, after } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { startAppwriteMock } from './support/appwriteMock.js'
|
||||
import { useTestEnv } from './support/testEnv.js'
|
||||
import { schemaKeys } from './support/schema.js'
|
||||
import { SHOP_DATETIMES } from '../server/services/shop/schema.js'
|
||||
|
||||
// Artikel des Shops: reines Normalisieren, Kaeufer-Sicht und Speichern gegen den Appwrite-Nachbau.
|
||||
const aw = await startAppwriteMock({ schema: { shopProducts: schemaKeys('shopProducts') }, datetimes: SHOP_DATETIMES })
|
||||
after(() => aw.close())
|
||||
useTestEnv({ appwriteUrl: aw.url, stripeUrl: 'http://127.0.0.1:1' })
|
||||
|
||||
const { config } = await import('../server/config.js')
|
||||
const P = await import('../server/services/shop/produkte.js')
|
||||
const { normalisiereProdukt, produktOeffentlich, listeProdukte, holeProdukt, speichereProdukt, loescheProdukt, sortiere, bildIdsInGebrauch } = P
|
||||
|
||||
const klein = { kleinunternehmer: true }
|
||||
const regel = { kleinunternehmer: false }
|
||||
const basis = (over = {}) => ({ name: 'Lavendelseife', preisCent: 450, ...over })
|
||||
const ok = (eingabe, einstellungen = regel) => {
|
||||
const r = normalisiereProdukt(eingabe, { einstellungen })
|
||||
assert.equal(r.fehler, undefined, JSON.stringify(r.fehler))
|
||||
return r.produkt
|
||||
}
|
||||
const fehler = (eingabe, einstellungen = regel) => {
|
||||
const r = normalisiereProdukt(eingabe, { einstellungen })
|
||||
assert.equal(r.produkt, undefined)
|
||||
return r.fehler
|
||||
}
|
||||
|
||||
test('Minimaler Artikel bekommt die Standardwerte', () => {
|
||||
const p = ok(basis())
|
||||
assert.deepEqual(p, {
|
||||
name: 'Lavendelseife',
|
||||
kategorie: '',
|
||||
kurz: '',
|
||||
beschreibung: '',
|
||||
preisCent: 450,
|
||||
steuersatz: 19,
|
||||
grundpreis: null,
|
||||
bestand: null,
|
||||
aktiv: true,
|
||||
varianten: null,
|
||||
bilder: [],
|
||||
lieferart: 'beides',
|
||||
widerruf: 'gesetzlich',
|
||||
sicherheit: { hersteller: 'selbst', euVerantwortlicher: null, hinweise: '' },
|
||||
lebensmittel: { aktiv: false, pflichtangaben: '' },
|
||||
})
|
||||
})
|
||||
|
||||
test('Name, Preis und Laengen werden geprueft', () => {
|
||||
assert.ok(fehler({ preisCent: 100 }).name)
|
||||
assert.ok(fehler(basis({ name: ' ' })).name)
|
||||
assert.ok(fehler(basis({ name: 'x'.repeat(121) })).name)
|
||||
assert.ok(fehler(basis({ kategorie: 'x'.repeat(41) })).kategorie)
|
||||
assert.ok(fehler(basis({ kurz: 'x'.repeat(301) })).kurz)
|
||||
assert.ok(fehler(basis({ beschreibung: 'x'.repeat(5001) })).beschreibung)
|
||||
for (const preis of [0, -5, 10_000_001, 4.5, '4,50', null, undefined]) assert.ok(fehler(basis({ preisCent: preis })).preisCent, String(preis))
|
||||
assert.equal(ok(basis({ preisCent: 10_000_000 })).preisCent, 10_000_000)
|
||||
assert.ok(fehler('kaputt').produkt)
|
||||
assert.ok(fehler(null).produkt)
|
||||
assert.ok(fehler([]).produkt)
|
||||
})
|
||||
|
||||
test('Texte: getrimmt, Steuerzeichen entfernt, Umbrueche nur in Langtexten', () => {
|
||||
const p = ok(basis({ name: ' Seife\u0000 mit\nUmbruch ', kurz: 'a\nb', beschreibung: 'Zeile 1\r\nZeile\u0007 2', sicherheit: { hinweise: 'Nicht\nessen\u0000' } }))
|
||||
assert.equal(p.name, 'Seife mit Umbruch')
|
||||
assert.equal(p.kurz, 'a b')
|
||||
assert.equal(p.beschreibung, 'Zeile 1\nZeile 2')
|
||||
assert.equal(p.sicherheit.hinweise, 'Nicht\nessen')
|
||||
})
|
||||
|
||||
test('Steuersatz: 0, 7, 19; Kleinunternehmer erzwingt 0', () => {
|
||||
assert.equal(ok(basis({ steuersatz: 7 })).steuersatz, 7)
|
||||
assert.equal(ok(basis({ steuersatz: 0 })).steuersatz, 0)
|
||||
assert.equal(ok(basis({ steuersatz: 19 })).steuersatz, 19)
|
||||
assert.ok(fehler(basis({ steuersatz: 16 })).steuersatz)
|
||||
assert.ok(fehler(basis({ steuersatz: 'viel' })).steuersatz)
|
||||
assert.equal(ok(basis({ steuersatz: 19 }), klein).steuersatz, 0)
|
||||
assert.equal(ok(basis({ steuersatz: 7 }), klein).steuersatz, 0)
|
||||
assert.equal(ok(basis(), klein).steuersatz, 0)
|
||||
})
|
||||
|
||||
test('Bestand: ganze Zahl ab 0 oder unbegrenzt', () => {
|
||||
assert.equal(ok(basis({ bestand: 0 })).bestand, 0)
|
||||
assert.equal(ok(basis({ bestand: 12 })).bestand, 12)
|
||||
assert.equal(ok(basis({ bestand: '7' })).bestand, 7)
|
||||
assert.equal(ok(basis({ bestand: null })).bestand, null)
|
||||
assert.equal(ok(basis({ bestand: '' })).bestand, null)
|
||||
for (const b of [-1, 1.5, 'viele']) assert.ok(fehler(basis({ bestand: b })).bestand, String(b))
|
||||
})
|
||||
|
||||
test('Grundpreis: Menge und Einheit', () => {
|
||||
assert.deepEqual(ok(basis({ grundpreis: { menge: 100, einheit: 'g' } })).grundpreis, { menge: 100, einheit: 'g' })
|
||||
assert.deepEqual(ok(basis({ grundpreis: { menge: '0,75', einheit: 'l' } })).grundpreis, { menge: 0.75, einheit: 'l' })
|
||||
assert.equal(ok(basis({ grundpreis: null })).grundpreis, null)
|
||||
assert.equal(ok(basis({ grundpreis: { menge: '', einheit: '' } })).grundpreis, null)
|
||||
assert.ok(fehler(basis({ grundpreis: { menge: 0, einheit: 'g' } })).grundpreis?.menge ?? fehler(basis({ grundpreis: { menge: 0, einheit: 'g' } }))['grundpreis.menge'])
|
||||
assert.ok(fehler(basis({ grundpreis: { menge: 100, einheit: 'stueck' } }))['grundpreis.einheit'])
|
||||
assert.ok(fehler(basis({ grundpreis: { menge: 'viel', einheit: 'g' } }))['grundpreis.menge'])
|
||||
assert.ok(fehler(basis({ grundpreis: 'x' })).grundpreis)
|
||||
})
|
||||
|
||||
test('aktiv, Lieferart, Widerruf', () => {
|
||||
assert.equal(ok(basis({ aktiv: false })).aktiv, false)
|
||||
assert.ok(fehler(basis({ aktiv: 'ja' })).aktiv)
|
||||
assert.equal(ok(basis({ lieferart: 'abholung' })).lieferart, 'abholung')
|
||||
assert.ok(fehler(basis({ lieferart: 'drohne' })).lieferart)
|
||||
for (const w of ['gesetzlich', 'verderblich', 'individuell', 'hygiene']) assert.equal(ok(basis({ widerruf: w })).widerruf, w)
|
||||
assert.ok(fehler(basis({ widerruf: 'nie' })).widerruf)
|
||||
})
|
||||
|
||||
test('Varianten: Preis und Bestand nur an den Werten, IDs vom Server', () => {
|
||||
const p = ok(
|
||||
basis({
|
||||
preisCent: 999,
|
||||
bestand: 5,
|
||||
varianten: {
|
||||
name: 'Duft',
|
||||
werte: [
|
||||
{ name: 'Lavendel', preisCent: 350, bestand: 3 },
|
||||
{ id: 'abcd1234', name: 'Rose', preisCent: 450 },
|
||||
{ id: 'ZU-KURZ', name: 'Minze', preisCent: 400, bestand: '0' },
|
||||
{ id: 'abcd1234', name: 'Zitrone', preisCent: 400 },
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
assert.equal(p.preisCent, null)
|
||||
assert.equal(p.bestand, null)
|
||||
assert.equal(p.varianten.name, 'Duft')
|
||||
const ids = p.varianten.werte.map((w) => w.id)
|
||||
assert.equal(ids[1], 'abcd1234')
|
||||
for (const id of ids) assert.match(id, /^[a-z0-9]{8}$/)
|
||||
assert.equal(new Set(ids).size, 4)
|
||||
assert.deepEqual(p.varianten.werte.map((w) => w.bestand), [3, null, 0, null])
|
||||
assert.deepEqual(p.varianten.werte.map((w) => w.preisCent), [350, 450, 400, 400])
|
||||
})
|
||||
|
||||
test('Varianten: Fehler je Wert', () => {
|
||||
const f = fehler(basis({ varianten: { name: '', werte: [{ name: 'A', preisCent: 0 }, { name: '', preisCent: 100 }, { name: 'a', preisCent: 100 }] } }))
|
||||
assert.ok(f['varianten.name'])
|
||||
assert.ok(f['varianten.werte.0.preisCent'])
|
||||
assert.ok(f['varianten.werte.1.name'])
|
||||
assert.ok(f['varianten.werte.2.name'])
|
||||
assert.ok(fehler(basis({ varianten: { name: 'Größe', werte: [] } }))['varianten.werte'])
|
||||
const zuViele = Array.from({ length: config.shop.maxVarianten + 1 }, (_, i) => ({ name: `W${i}`, preisCent: 100 }))
|
||||
assert.ok(fehler(basis({ varianten: { name: 'Größe', werte: zuViele } }))['varianten.werte'])
|
||||
const genau = zuViele.slice(0, config.shop.maxVarianten)
|
||||
assert.equal(ok(basis({ varianten: { name: 'Größe', werte: genau } })).varianten.werte.length, config.shop.maxVarianten)
|
||||
// leere Variante = keine Varianten, dann zaehlt der Artikelpreis
|
||||
assert.equal(ok(basis({ varianten: { name: '', werte: [] } })).varianten, null)
|
||||
assert.equal(ok(basis({ varianten: null })).varianten, null)
|
||||
})
|
||||
|
||||
test('Bilder: Bild-IDs, hoechstens maxBilder, eindeutig', () => {
|
||||
assert.deepEqual(ok(basis({ bilder: ['abc-123', 'def'] })).bilder, ['abc-123', 'def'])
|
||||
assert.ok(fehler(basis({ bilder: ['a', 'b', 'c', 'd', 'e'] })).bilder)
|
||||
assert.equal(ok(basis({ bilder: ['a', 'b', 'c', 'd'] })).bilder.length, config.shop.maxBilder)
|
||||
assert.ok(fehler(basis({ bilder: ['a', 'a'] })).bilder)
|
||||
assert.ok(fehler(basis({ bilder: ['../x'] })).bilder)
|
||||
assert.ok(fehler(basis({ bilder: ['A'] })).bilder)
|
||||
assert.ok(fehler(basis({ bilder: [''] })).bilder)
|
||||
assert.ok(fehler(basis({ bilder: [1] })).bilder)
|
||||
assert.ok(fehler(basis({ bilder: 'abc' })).bilder)
|
||||
})
|
||||
|
||||
test('Lebensmittel braucht Pflichtangaben', () => {
|
||||
const f = fehler(basis({ lebensmittel: { aktiv: true, pflichtangaben: ' ' } }))
|
||||
assert.ok(f['lebensmittel.pflichtangaben'])
|
||||
assert.deepEqual(ok(basis({ lebensmittel: { aktiv: true, pflichtangaben: 'Zutaten: Mehl. Allergene: Gluten.' } })).lebensmittel, { aktiv: true, pflichtangaben: 'Zutaten: Mehl. Allergene: Gluten.' })
|
||||
assert.ok(fehler(basis({ lebensmittel: { aktiv: true, pflichtangaben: 'x'.repeat(4001) } }))['lebensmittel.pflichtangaben'])
|
||||
assert.equal(ok(basis({ lebensmittel: { aktiv: false } })).lebensmittel.aktiv, false)
|
||||
})
|
||||
|
||||
test('Produktsicherheit: Hersteller selbst oder vollstaendig, EU-Verantwortlicher optional aber vollstaendig', () => {
|
||||
const herst = { name: 'Seifen GmbH', anschrift: 'Weg 1, 12345 Ort', kontakt: 'info@seifen.example' }
|
||||
assert.equal(ok(basis({ sicherheit: { hersteller: 'selbst' } })).sicherheit.hersteller, 'selbst')
|
||||
assert.deepEqual(ok(basis({ sicherheit: { hersteller: herst } })).sicherheit.hersteller, herst)
|
||||
const f = fehler(basis({ sicherheit: { hersteller: { name: 'X' } } }))
|
||||
assert.ok(f['sicherheit.hersteller.anschrift'])
|
||||
assert.ok(f['sicherheit.hersteller.kontakt'])
|
||||
assert.ok(fehler(basis({ sicherheit: { hersteller: 'jemand' } }))['sicherheit.hersteller'])
|
||||
assert.ok(fehler(basis({ sicherheit: { hersteller: 42 } }))['sicherheit.hersteller'])
|
||||
assert.deepEqual(ok(basis({ sicherheit: { hersteller: herst, euVerantwortlicher: herst } })).sicherheit.euVerantwortlicher, herst)
|
||||
assert.equal(ok(basis({ sicherheit: { hersteller: herst, euVerantwortlicher: { name: '', anschrift: '', kontakt: '' } } })).sicherheit.euVerantwortlicher, null)
|
||||
assert.ok(fehler(basis({ sicherheit: { hersteller: herst, euVerantwortlicher: { name: 'EU' } } }))['sicherheit.euVerantwortlicher.anschrift'])
|
||||
assert.ok(fehler(basis({ sicherheit: { hinweise: 'x'.repeat(2001) } }))['sicherheit.hinweise'])
|
||||
})
|
||||
|
||||
test('Normalisieren teilt keine Referenzen und ignoriert unbekannte Felder und __proto__', () => {
|
||||
const eingabe = JSON.parse('{"name":"A","preisCent":100,"bilder":["a"],"__proto__":{"preisCent":1},"boese":1}')
|
||||
const p = ok(eingabe)
|
||||
p.bilder.push('x')
|
||||
assert.deepEqual(eingabe.bilder, ['a'])
|
||||
assert.equal(p.boese, undefined)
|
||||
assert.equal(p.preisCent, 100)
|
||||
})
|
||||
|
||||
// ---- Kaeufer-Sicht -------------------------------------------------------------------------------------------------
|
||||
|
||||
const doc = (produkt, id = 'p1') => ({ $id: id, produkt: JSON.stringify(produkt) })
|
||||
const oeff = (eingabe, extra = {}, einstellungen = regel) => {
|
||||
const d = doc(ok(eingabe, einstellungen), extra.id)
|
||||
return produktOeffentlich(d, { einstellungen, siteKey: 'abc123', ...extra })
|
||||
}
|
||||
|
||||
test('produktOeffentlich: Einzelartikel', () => {
|
||||
const o = oeff(basis({ kurz: 'Handgemacht', grundpreis: { menge: 100, einheit: 'g' }, bilder: ['bild-1', 'bild2'], steuersatz: 7, bestand: 3, kategorie: 'Seife' }))
|
||||
assert.equal(o.id, 'p1')
|
||||
assert.equal(o.name, 'Lavendelseife')
|
||||
assert.equal(o.kategorie, 'Seife')
|
||||
assert.equal(o.kurz, 'Handgemacht')
|
||||
assert.equal(o.preisText, '4,50\u00a0€')
|
||||
assert.equal(o.preisAbText, undefined)
|
||||
assert.equal(o.grundpreisText, '(45,00\u00a0€ / 1 kg)')
|
||||
assert.equal(o.steuersatz, 7)
|
||||
assert.equal(o.varianten, null)
|
||||
assert.deepEqual(o.bilder, [
|
||||
{ klein: '/api/public/shop/v1/abc123/bild/bild-1-s.webp', gross: '/api/public/shop/v1/abc123/bild/bild-1-l.webp' },
|
||||
{ klein: '/api/public/shop/v1/abc123/bild/bild2-s.webp', gross: '/api/public/shop/v1/abc123/bild/bild2-l.webp' },
|
||||
])
|
||||
assert.equal(o.lieferart, 'beides')
|
||||
assert.equal(o.lieferHinweis, '')
|
||||
assert.equal(o.widerruf, 'gesetzlich')
|
||||
assert.equal(o.widerrufText, '')
|
||||
assert.deepEqual(o.sicherheit, { hersteller: null, euVerantwortlicher: null, hinweise: '' })
|
||||
assert.equal(o.lebensmittel, null)
|
||||
assert.equal(o.verfuegbar, true)
|
||||
assert.equal(o.ausverkauft, false)
|
||||
assert.equal('bestand' in o, false)
|
||||
assert.equal('bestandText' in o, false)
|
||||
assert.equal('preisCent' in o, false)
|
||||
})
|
||||
|
||||
test('produktOeffentlich: Lieferart, Widerrufstexte, Hersteller, Lebensmittel', () => {
|
||||
assert.equal(oeff(basis({ lieferart: 'abholung' })).lieferHinweis, 'Nur Abholung')
|
||||
assert.equal(oeff(basis({ widerruf: 'verderblich' })).widerrufText, 'Vom Widerruf ausgeschlossen: schnell verderbliche Ware')
|
||||
assert.match(oeff(basis({ widerruf: 'individuell' })).widerrufText, /nach Kundenwunsch angefertigt/)
|
||||
assert.match(oeff(basis({ widerruf: 'hygiene' })).widerrufText, /versiegelte Ware aus Hygienegründen, wenn die Versiegelung entfernt wurde/)
|
||||
const herst = { name: 'Seifen GmbH', anschrift: 'Weg 1', kontakt: 'a@b.de' }
|
||||
const o = oeff(basis({ sicherheit: { hersteller: herst, hinweise: 'Nicht essen' }, lebensmittel: { aktiv: true, pflichtangaben: 'Zutaten: Salz' } }))
|
||||
assert.deepEqual(o.sicherheit.hersteller, herst)
|
||||
assert.equal(o.sicherheit.hinweise, 'Nicht essen')
|
||||
assert.deepEqual(o.lebensmittel, { pflichtangaben: 'Zutaten: Salz' })
|
||||
})
|
||||
|
||||
test('produktOeffentlich: Verfuegbarkeit mit Bestand und Reservierung', () => {
|
||||
const eingabe = basis({ bestand: 2 })
|
||||
assert.equal(oeff(eingabe).verfuegbar, true)
|
||||
assert.equal(oeff(eingabe, { reserviert: new Map([['p1', 1]]) }).verfuegbar, true)
|
||||
const aus = oeff(eingabe, { reserviert: new Map([['p1', 2]]) })
|
||||
assert.equal(aus.verfuegbar, false)
|
||||
assert.equal(aus.ausverkauft, true)
|
||||
assert.equal(oeff(basis({ bestand: 0 })).ausverkauft, true)
|
||||
assert.equal(oeff(basis({ bestand: null }), { reserviert: new Map([['p1', 99]]) }).verfuegbar, true)
|
||||
assert.equal(oeff(eingabe, { reserviert: new Map([['p1', 5]]) }).verfuegbar, false)
|
||||
assert.equal(oeff(eingabe, { reserviert: undefined }).verfuegbar, true)
|
||||
})
|
||||
|
||||
test('produktOeffentlich: Varianten mit "ab"-Preis und Verfuegbarkeit je Wert', () => {
|
||||
const eingabe = basis({
|
||||
varianten: {
|
||||
name: 'Duft',
|
||||
werte: [
|
||||
{ id: 'aaaaaaaa', name: 'Lavendel', preisCent: 350, bestand: 1 },
|
||||
{ id: 'bbbbbbbb', name: 'Rose', preisCent: 450 },
|
||||
],
|
||||
},
|
||||
})
|
||||
const o = oeff(eingabe, { reserviert: new Map([['p1:aaaaaaaa', 1], ['p1', 50]]) })
|
||||
assert.equal(o.preisText, '3,50\u00a0€')
|
||||
assert.equal(o.preisAbText, 'ab 3,50\u00a0€')
|
||||
assert.equal(o.varianten.name, 'Duft')
|
||||
assert.deepEqual(o.varianten.werte.map((w) => [w.id, w.name, w.preisText, w.verfuegbar]), [
|
||||
['aaaaaaaa', 'Lavendel', '3,50\u00a0€', false],
|
||||
['bbbbbbbb', 'Rose', '4,50\u00a0€', true],
|
||||
])
|
||||
assert.equal(o.verfuegbar, true)
|
||||
const alle = oeff(eingabe, { reserviert: new Map([['p1:aaaaaaaa', 1]]) })
|
||||
assert.equal(alle.verfuegbar, true)
|
||||
const keine = oeff(basis({ varianten: { name: 'Duft', werte: [{ id: 'aaaaaaaa', name: 'L', preisCent: 350, bestand: 0 }] } }))
|
||||
assert.equal(keine.ausverkauft, true)
|
||||
// gleiche Preise: kein "ab"
|
||||
const gleich = oeff(basis({ varianten: { name: 'Farbe', werte: [{ name: 'Rot', preisCent: 500 }, { name: 'Blau', preisCent: 500 }] }, grundpreis: { menge: 50, einheit: 'g' } }))
|
||||
assert.equal(gleich.preisText, '5,00\u00a0€')
|
||||
assert.equal(gleich.preisAbText, undefined)
|
||||
assert.equal(gleich.grundpreisText, '(100,00\u00a0€ / 1 kg)')
|
||||
})
|
||||
|
||||
test('produktOeffentlich: kaputter Datensatz liefert null, Fremdtext bleibt Text', () => {
|
||||
assert.equal(produktOeffentlich({ $id: 'x', produkt: '{kaputt' }, { siteKey: 's' }), null)
|
||||
assert.equal(produktOeffentlich({ $id: 'x', produkt: '"text"' }, { siteKey: 's' }), null)
|
||||
assert.equal(produktOeffentlich({ $id: 'x' }, { siteKey: 's' }), null)
|
||||
const o = oeff(basis({ name: '<img src=x onerror=alert(1)>' }))
|
||||
assert.equal(o.name, '<img src=x onerror=alert(1)>')
|
||||
})
|
||||
|
||||
// ---- Speichern -----------------------------------------------------------------------------------------------------
|
||||
|
||||
const SHOP = { shopId: 'shop-a', customerId: 'cust-a' }
|
||||
const fremd = { shopId: 'shop-b', customerId: 'cust-b' }
|
||||
|
||||
test('speichern: anlegen sortiert ans Ende, Update behaelt sort, JSON in produkt', async () => {
|
||||
const a = await speichereProdukt(SHOP, null, ok(basis({ name: 'A' })))
|
||||
const b = await speichereProdukt(SHOP, null, ok(basis({ name: 'B' })))
|
||||
const c = await speichereProdukt(SHOP, null, ok(basis({ name: 'C', aktiv: false })))
|
||||
assert.deepEqual([a.sort, b.sort, c.sort], [0, 1, 2])
|
||||
assert.equal(a.shopId, 'shop-a')
|
||||
assert.equal(a.customerId, 'cust-a')
|
||||
assert.equal(c.aktiv, false)
|
||||
assert.equal(JSON.parse(a.produkt).name, 'A')
|
||||
const neu = await speichereProdukt(SHOP, b.$id, ok(basis({ name: 'B2', preisCent: 999 })))
|
||||
assert.equal(neu.sort, 1)
|
||||
assert.equal(neu.name, 'B2')
|
||||
assert.equal(JSON.parse(neu.produkt).preisCent, 999)
|
||||
const liste = await listeProdukte('shop-a')
|
||||
assert.deepEqual(liste.map((d) => d.name), ['A', 'B2', 'C'])
|
||||
})
|
||||
|
||||
test('Shops sind getrennt: Liste, Holen, Aendern, Loeschen, Sortieren', async () => {
|
||||
const x = await speichereProdukt(fremd, null, ok(basis({ name: 'Fremd' })))
|
||||
assert.equal(x.sort, 0)
|
||||
assert.deepEqual((await listeProdukte('shop-b')).map((d) => d.name), ['Fremd'])
|
||||
assert.equal(await holeProdukt('shop-a', x.$id), null)
|
||||
assert.equal((await holeProdukt('shop-b', x.$id)).name, 'Fremd')
|
||||
assert.equal(await holeProdukt('shop-a', 'gibt-es-nicht'), null)
|
||||
assert.equal(await holeProdukt('shop-a', '../etc'), null)
|
||||
await assert.rejects(speichereProdukt(SHOP, x.$id, ok(basis())), (e) => e.status === 404)
|
||||
await assert.rejects(loescheProdukt('shop-a', x.$id), (e) => e.status === 404)
|
||||
await assert.rejects(sortiere('shop-a', [x.$id]), (e) => e.status === 400)
|
||||
assert.equal((await holeProdukt('shop-b', x.$id)).name, 'Fremd')
|
||||
})
|
||||
|
||||
test('speichern: unvollstaendiger oder zu grosser Artikel => 400', async () => {
|
||||
await assert.rejects(speichereProdukt(SHOP, null, {}), (e) => e.status === 400 && /unvollständig/.test(e.message))
|
||||
await assert.rejects(speichereProdukt(SHOP, null, null), (e) => e.status === 400)
|
||||
const riesig = ok(basis({ sicherheit: { hinweise: 'ä'.repeat(2000) }, beschreibung: 'ä'.repeat(5000), lebensmittel: { aktiv: true, pflichtangaben: 'ä'.repeat(4000) }, varianten: { name: 'V', werte: Array.from({ length: 30 }, (_, i) => ({ name: `Wert-${i}-${'x'.repeat(30)}`, preisCent: 100 })) } }))
|
||||
riesig.kurz = 'ü'.repeat(20000)
|
||||
await assert.rejects(speichereProdukt(SHOP, null, riesig), (e) => e.status === 400)
|
||||
})
|
||||
|
||||
test('sortieren: genau die Artikel des Shops, sonst 400', async () => {
|
||||
const liste = await listeProdukte('shop-a')
|
||||
const ids = liste.map((d) => d.$id)
|
||||
const neu = [ids[2], ids[0], ids[1]]
|
||||
const r = await sortiere('shop-a', neu)
|
||||
assert.deepEqual(r.map((d) => [d.$id, d.sort]), neu.map((id, i) => [id, i]))
|
||||
assert.deepEqual((await listeProdukte('shop-a')).map((d) => d.$id), neu)
|
||||
await assert.rejects(sortiere('shop-a', ids.slice(0, 2)), (e) => e.status === 400)
|
||||
await assert.rejects(sortiere('shop-a', [...ids, 'noch-einer']), (e) => e.status === 400)
|
||||
await assert.rejects(sortiere('shop-a', [ids[0], ids[0], ids[1]]), (e) => e.status === 400)
|
||||
await assert.rejects(sortiere('shop-a', 'abc'), (e) => e.status === 400)
|
||||
await assert.rejects(sortiere('shop-a', [1, 2, 3]), (e) => e.status === 400)
|
||||
assert.deepEqual((await listeProdukte('shop-a')).map((d) => d.$id), neu)
|
||||
})
|
||||
|
||||
test('loeschen und neu anlegen: sort = max + 1', async () => {
|
||||
const liste = await listeProdukte('shop-a')
|
||||
assert.equal(await loescheProdukt('shop-a', liste[0].$id), true)
|
||||
assert.equal(await holeProdukt('shop-a', liste[0].$id), null)
|
||||
await assert.rejects(loescheProdukt('shop-a', liste[0].$id), (e) => e.status === 404)
|
||||
const neu = await speichereProdukt(SHOP, null, ok(basis({ name: 'Spaeter' })))
|
||||
assert.equal(neu.sort, 3)
|
||||
})
|
||||
|
||||
test('Bild-IDs in Gebrauch', async () => {
|
||||
await speichereProdukt(SHOP, null, ok(basis({ name: 'Mit Bildern', bilder: ['bild-x', 'bild-y'] })))
|
||||
await speichereProdukt(fremd, null, ok(basis({ name: 'Fremd2', bilder: ['bild-fremd'] })))
|
||||
const ids = await bildIdsInGebrauch('shop-a')
|
||||
assert.ok(ids instanceof Set)
|
||||
assert.deepEqual([...ids].sort(), ['bild-x', 'bild-y'])
|
||||
assert.equal((await bildIdsInGebrauch('shop-leer')).size, 0)
|
||||
})
|
||||
|
||||
test('Obergrenze maxArtikel => 409, Update bleibt moeglich', async () => {
|
||||
const alt = config.shop.maxArtikel
|
||||
const vorhanden = (await listeProdukte('shop-a')).length
|
||||
config.shop.maxArtikel = vorhanden
|
||||
try {
|
||||
await assert.rejects(speichereProdukt(SHOP, null, ok(basis({ name: 'Zu viel' }))), (e) => e.status === 409 && /Höchstzahl/.test(e.message))
|
||||
const erster = (await listeProdukte('shop-a'))[0]
|
||||
await speichereProdukt(SHOP, erster.$id, ok(basis({ name: 'Noch erlaubt' })))
|
||||
// anderer Shop hat eigenes Limit
|
||||
config.shop.maxArtikel = (await listeProdukte('shop-b')).length + 1
|
||||
await speichereProdukt(fremd, null, ok(basis({ name: 'Passt gerade' })))
|
||||
await assert.rejects(speichereProdukt(fremd, null, ok(basis({ name: 'Zu viel' }))), (e) => e.status === 409)
|
||||
} finally {
|
||||
config.shop.maxArtikel = alt
|
||||
}
|
||||
})
|
||||
|
||||
test('parallele Anlage vergibt verschiedene sort-Werte', async () => {
|
||||
const shop = { shopId: 'shop-par', customerId: 'c' }
|
||||
const docs = await Promise.all(Array.from({ length: 5 }, (_, i) => speichereProdukt(shop, null, ok(basis({ name: `P${i}` })))))
|
||||
assert.deepEqual(docs.map((d) => d.sort).sort(), [0, 1, 2, 3, 4])
|
||||
})
|
||||
Reference in new issue
Block a user