diff --git a/packages/core/database/lib/tests/transactions.test.api.js b/packages/core/database/lib/tests/transactions.test.api.js new file mode 100644 index 0000000000..c318d6b760 --- /dev/null +++ b/packages/core/database/lib/tests/transactions.test.api.js @@ -0,0 +1,334 @@ +'use strict'; + +const { createStrapiInstance } = require('../../../../../test/helpers/strapi'); + +let strapi; + +describe('transactions', () => { + beforeAll(async () => { + strapi = await createStrapiInstance(); + }); + + afterAll(async () => { + await strapi.destroy(); + }); + + describe('using a transaction method', () => { + test('commits successfully', async () => { + let original; + await strapi.db.transaction(async () => { + original = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'wrong key', + }) + .where({ id: 1 }) + .execute(); + + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: original[0].key, + }) + .where({ id: 1 }) + .execute(); + }); + + const end = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(end[0].key).toEqual(original[0].key); + }); + + test('rollback successfully', async () => { + let original; + try { + await strapi.db.transaction(async () => { + original = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + // this is valid + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'wrong key', + }) + .where({ id: 1 }) + .execute(); + + // this throws + await strapi.db + .queryBuilder('invalid_uid') + .update({ + key: original[0].key, + invalid_key: 'error', + }) + .where({ id: 1 }) + .execute(); + }); + + expect('this should not be reached').toBe(false); + } catch (e) { + // do nothing + } + + const end = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(end[0].key).toEqual(original[0].key); + }); + + test('nested rollback -> rollback works', async () => { + let original; + try { + await strapi.db.transaction(async () => { + original = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + // this is valid + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'changed key', + }) + .where({ id: 1 }) + .execute(); + + // here we'll make a nested transaction that throws and then confirm we still have "changed key" from above + try { + await strapi.db.transaction(async () => { + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'changed key - nested', + }) + .where({ id: 1 }) + .execute(); + + // this should throw and roll back + await strapi.db + .queryBuilder('invalid_uid') + .update({ + invalid_key: 'error', + }) + .where({ id: 1 }) + .execute(); + }); + } catch (e) { + // do nothing + } + + // should equal the result from above + const result = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(result[0].key).toEqual('changed key'); + + // this throws + await strapi.db + .queryBuilder('invalid_uid') + .update({ + key: original[0].key, + invalid_key: 'error', + }) + .where({ id: 1 }) + .execute(); + }); + + expect('this should not be reached').toBe(false); + } catch (e) { + // do nothing + } + + const end = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(end[0].key).toEqual(original[0].key); + }); + + test('nested commit -> rollback works', async () => { + let original; + try { + await strapi.db.transaction(async () => { + original = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + // this is valid + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'changed key', + }) + .where({ id: 1 }) + .execute(); + + // here we'll make a nested transaction that works, and then later we'll rollback the outer transaction + try { + await strapi.db.transaction(async () => { + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'changed key - nested', + }) + .where({ id: 1 }) + .execute(); + }); + } catch (e) { + // do nothing + } + + // should equal the result from above + const result = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(result[0].key).toEqual('changed key - nested'); + + // this throws + await strapi.db + .queryBuilder('invalid_uid') + .update({ + key: original[0].key, + invalid_key: 'error', + }) + .where({ id: 1 }) + .execute(); + }); + + expect('this should not be reached').toBe(false); + } catch (e) { + // do nothing + } + + const end = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(end[0].key).toEqual(original[0].key); + }); + }); + + describe('using a transaction object', () => { + test('commits successfully', async () => { + const trx = await strapi.db.transaction(); + + try { + const original = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .transacting(trx.get()) + .execute(); + + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'wrong key', + }) + .where({ id: 1 }) + .transacting(trx.get()) + .execute(); + + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: original[0].key, + }) + .where({ id: 1 }) + .transacting(trx.get()) + .execute(); + + await trx.commit(); + } catch (e) { + await trx.rollback(); + console.log(e.message); + expect('this should not be reached').toBe(false); + } + + const end = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(end[0].key).toEqual('strapi_content_types_schema'); + }); + + test('rollback successfully', async () => { + const trx = await strapi.db.transaction(); + + try { + await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .transacting(trx.get()) + .execute(); + + await strapi.db + .queryBuilder('strapi::core-store') + .update({ + key: 'wrong key', + }) + .where({ id: 1 }) + .transacting(trx.get()) + .execute(); + + // this query should throw because it has errors + await strapi.db + .queryBuilder('invalid_uid') + .update({ + key: 123, + key_not_here: 'this should error', + }) + .where({ id: 'this should error' }) + .transacting(trx.get()) + .execute(); + + await trx.commit(); + expect('this should not be reached').toBe(false); + } catch (e) { + await trx.rollback(); + } + + const end = await strapi.db + .queryBuilder('strapi::core-store') + .select(['*']) + .where({ id: 1 }) + .execute(); + + expect(end[0].key).toEqual('strapi_content_types_schema'); + }); + }); +});