UNPKG

apialize

Version:

Turn a database model into a production ready REST(ish) CRUD API in a few lines.

120 lines (102 loc) 3.92 kB
const express = require('express'); const bodyParser = require('body-parser'); const request = require('supertest'); const { Sequelize, DataTypes } = require('sequelize'); const { create, list } = require('../src'); async function build({ createOptions = {}, listModelOptions = {} } = {}) { const sequelize = new Sequelize('sqlite::memory:', { logging: false }); const Item = sequelize.define( 'Item', { id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, external_id: { type: DataTypes.STRING(64), allowNull: false, unique: true, }, name: { type: DataTypes.STRING(100), allowNull: false }, desc: { type: DataTypes.STRING(255), allowNull: true }, }, { tableName: 'batch_items', timestamps: false } ); await sequelize.sync({ force: true }); const app = express(); app.use(bodyParser.json()); app.use('/items', create(Item, createOptions)); app.use('/items', list(Item, { metaShowFilters: true }, listModelOptions)); return { sequelize, Item, app }; } describe('create operation: batch array body', () => { let sequelize; afterEach(async () => { if (sequelize) { await sequelize.close(); sequelize = null; } }); test('creates multiple records atomically and returns array of created objects', async () => { const { sequelize: s, app } = await build({ createOptions: { allow_bulk_create: true }, }); sequelize = s; const payload = [ { external_id: 'b1', name: 'Batch One', desc: 'x' }, { external_id: 'b2', name: 'Batch Two', desc: 'y' }, ]; const res = await request(app).post('/items').send(payload); expect(res.status).toBe(201); expect(Array.isArray(res.body)).toBe(true); expect(res.body.length).toBe(2); // Ensure ids are present on returned objects for (const row of res.body) { expect(row).toHaveProperty('id'); expect(row).toHaveProperty('external_id'); } // Verify persisted via list const listRes = await request(app).get('/items'); expect(listRes.status).toBe(200); expect(listRes.body.meta.count).toBe(2); }); test('fails entire batch when one insert violates a constraint (rollback)', async () => { const { sequelize: s, app } = await build({ createOptions: { allow_bulk_create: true }, }); sequelize = s; const badBatch = [ { external_id: 'dup', name: 'Ok' }, { external_id: 'dup', name: 'Duplicate should fail' }, ]; const res = await request(app).post('/items').send(badBatch); // Expect an error; status may be 500 depending on default error handler expect(res.status).toBeGreaterThanOrEqual(400); // Ensure rollback: no rows persisted const listRes = await request(app).get('/items'); expect(listRes.status).toBe(200); expect(listRes.body.meta.count).toBe(0); }); test('respects id_mapping in batch response by mirroring mapped value into id', async () => { const { sequelize: s, app } = await build({ createOptions: { id_mapping: 'external_id', allow_bulk_create: true }, }); sequelize = s; const payload = [ { external_id: 'm1', name: 'Mapped One' }, { external_id: 'm2', name: 'Mapped Two' }, ]; const res = await request(app).post('/items').send(payload); expect(res.status).toBe(201); expect(Array.isArray(res.body)).toBe(true); expect(res.body.map((r) => r.id)).toEqual(['m1', 'm2']); }); test('returns 400 when allow_bulk_create is false (default) and body is array', async () => { const { sequelize: s, app } = await build(); sequelize = s; const payload = [ { external_id: 'x1', name: 'Nope 1' }, { external_id: 'x2', name: 'Nope 2' }, ]; const res = await request(app).post('/items').send(payload); expect(res.status).toBe(400); expect(res.body.success).toBe(false); }); });