mirror of
https://github.com/strapi/strapi.git
synced 2026-05-03 16:22:30 +00:00
chore(tests): add setupDatabaseReset utility (#24786)
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
restart: always
|
||||
volumes:
|
||||
- pgdata_test:/var/lib/postgresql/data
|
||||
- pgdata_test:/var/lib/postgresql
|
||||
environment:
|
||||
POSTGRES_USER: strapi
|
||||
POSTGRES_PASSWORD: strapi
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Core, Modules } from '@strapi/types';
|
||||
import { omit } from 'lodash/fp';
|
||||
|
||||
import { createTestSetup, destroyTestSetup } from '../../../utils/builder-helper';
|
||||
import { testInTransaction } from '../../../utils/index';
|
||||
import { setupDatabaseReset } from '../../../utils/index';
|
||||
import resources from './resources/index';
|
||||
import { ARTICLE_UID, findArticleDb } from './utils';
|
||||
|
||||
@@ -25,8 +25,10 @@ describe('Document Service', () => {
|
||||
await destroyTestSetup(testUtils);
|
||||
});
|
||||
|
||||
setupDatabaseReset();
|
||||
|
||||
describe('Update', () => {
|
||||
testInTransaction('Can update a draft', async () => {
|
||||
it('Can update a draft', async () => {
|
||||
const articleDb = await findArticleDb({ title: 'Article1-Draft-EN' });
|
||||
|
||||
const data = {
|
||||
@@ -55,7 +57,7 @@ describe('Document Service', () => {
|
||||
});
|
||||
});
|
||||
|
||||
testInTransaction('Can update a draft article in dutch', async () => {
|
||||
it('Can update a draft article in dutch', async () => {
|
||||
const articleDb = await findArticleDb({ title: 'Article1-Draft-NL' });
|
||||
|
||||
const data = { title: 'updated document' };
|
||||
@@ -78,7 +80,7 @@ describe('Document Service', () => {
|
||||
expect(enLocale).toBeDefined();
|
||||
});
|
||||
|
||||
testInTransaction('Create a new locale for an existing document', async () => {
|
||||
it('Create a new locale for an existing document', async () => {
|
||||
const articleDb = await findArticleDb({ title: 'Article1-Draft-EN' });
|
||||
const newName = 'updated document';
|
||||
|
||||
@@ -106,7 +108,7 @@ describe('Document Service', () => {
|
||||
expect(enLocale).toBeDefined();
|
||||
});
|
||||
|
||||
testInTransaction('Can update a draft and publish it', async () => {
|
||||
it('Can update a draft and publish it', async () => {
|
||||
const articleDb = await findArticleDb({ title: 'Article1-Draft-EN' });
|
||||
|
||||
const article = await updateArticle({
|
||||
@@ -121,7 +123,7 @@ describe('Document Service', () => {
|
||||
});
|
||||
});
|
||||
|
||||
testInTransaction('Returns null if document to update does not exist', async () => {
|
||||
it('Returns null if document to update does not exist', async () => {
|
||||
const article = await updateArticle({
|
||||
documentId: 'does-not-exist',
|
||||
data: { title: 'updated document' },
|
||||
@@ -130,50 +132,47 @@ describe('Document Service', () => {
|
||||
expect(article).toBeNull();
|
||||
});
|
||||
|
||||
testInTransaction(
|
||||
'Preserves non-localized fields when updating localized content for new locale',
|
||||
async () => {
|
||||
// Covers issue https://github.com/strapi/strapi/issues/21594
|
||||
it('Preserves non-localized fields when updating localized content for new locale', async () => {
|
||||
// Covers issue https://github.com/strapi/strapi/issues/21594
|
||||
|
||||
const MIXED_CONTENT_UID = 'api::mixed-content.mixed-content';
|
||||
const MIXED_CONTENT_UID = 'api::mixed-content.mixed-content';
|
||||
|
||||
// Create a document with both localized and non-localized fields
|
||||
const originalDoc = await strapi.documents(MIXED_CONTENT_UID).create({
|
||||
data: {
|
||||
localizedText: 'Original Text',
|
||||
sharedText: 'Shared Content',
|
||||
},
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
const updatedDoc = await strapi.documents(MIXED_CONTENT_UID).update({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'es',
|
||||
data: {
|
||||
localizedText: 'Texto Español',
|
||||
},
|
||||
});
|
||||
|
||||
expect(updatedDoc).toMatchObject({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'es',
|
||||
localizedText: 'Texto Español',
|
||||
// Non-localized field should remain unchanged
|
||||
sharedText: 'Shared Content',
|
||||
});
|
||||
|
||||
const originalEnDoc = await strapi.documents(MIXED_CONTENT_UID).findOne({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
expect(originalEnDoc).toMatchObject({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'en',
|
||||
// Create a document with both localized and non-localized fields
|
||||
const originalDoc = await strapi.documents(MIXED_CONTENT_UID).create({
|
||||
data: {
|
||||
localizedText: 'Original Text',
|
||||
sharedText: 'Shared Content',
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
const updatedDoc = await strapi.documents(MIXED_CONTENT_UID).update({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'es',
|
||||
data: {
|
||||
localizedText: 'Texto Español',
|
||||
},
|
||||
});
|
||||
|
||||
expect(updatedDoc).toMatchObject({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'es',
|
||||
localizedText: 'Texto Español',
|
||||
// Non-localized field should remain unchanged
|
||||
sharedText: 'Shared Content',
|
||||
});
|
||||
|
||||
const originalEnDoc = await strapi.documents(MIXED_CONTENT_UID).findOne({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'en',
|
||||
});
|
||||
|
||||
expect(originalEnDoc).toMatchObject({
|
||||
documentId: originalDoc.documentId,
|
||||
locale: 'en',
|
||||
localizedText: 'Original Text',
|
||||
sharedText: 'Shared Content',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,89 @@
|
||||
// Note: any tests that would cause writes to the db should be wrapped with this method to prevent changes
|
||||
// Alternatively, we could truncate/insert the tables in afterEach which should be only marginally slower
|
||||
// TODO: move to utils
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* setupDatabaseReset
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
|
||||
// Store initial database state for reset
|
||||
let initialTestData = {};
|
||||
let isTestDataCaptured = false;
|
||||
let allTableNames = [];
|
||||
|
||||
/**
|
||||
* Capture the initial state of all database tables for later restoration
|
||||
*/
|
||||
async function captureInitialTestData() {
|
||||
if (isTestDataCaptured) return;
|
||||
|
||||
initialTestData = {};
|
||||
|
||||
// Use Strapi's built-in dialect system to get table names
|
||||
allTableNames = await strapi.db.dialect.schemaInspector.getTables();
|
||||
|
||||
for (const tableName of allTableNames) {
|
||||
try {
|
||||
const data = await strapi.db.connection(tableName).select('*');
|
||||
initialTestData[tableName] = data;
|
||||
} catch (error) {
|
||||
console.warn(`Could not capture data for table ${tableName}:`, error.message);
|
||||
initialTestData[tableName] = [];
|
||||
}
|
||||
}
|
||||
|
||||
isTestDataCaptured = true;
|
||||
}
|
||||
|
||||
async function resetTestDatabase() {
|
||||
// Use Strapi's built-in schema update mechanism to disable constraints (e.g. foreign key constraints)
|
||||
await strapi.db.dialect.startSchemaUpdate();
|
||||
|
||||
try {
|
||||
for (const tableName of allTableNames) {
|
||||
try {
|
||||
// Clear the table
|
||||
await strapi.db.connection(tableName).del();
|
||||
|
||||
// Restore initial data if any
|
||||
const initialData = initialTestData[tableName];
|
||||
if (initialData && initialData.length > 0) {
|
||||
await strapi.db.connection(tableName).insert(initialData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Could not reset table ${tableName}:`, error.message);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Always re-enable constraints
|
||||
await strapi.db.dialect.endSchemaUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup database reset for a test suite
|
||||
* Call this in your describe block to automatically reset after each test
|
||||
*
|
||||
* NOTE:
|
||||
* Only use sparingly where needed as the operation is slower than testInTransaction
|
||||
*/
|
||||
export function setupDatabaseReset() {
|
||||
let isDataCaptured = false;
|
||||
|
||||
beforeEach(async () => {
|
||||
if (!isDataCaptured) {
|
||||
await captureInitialTestData();
|
||||
isDataCaptured = true;
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (isDataCaptured) {
|
||||
await resetTestDatabase();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* testInTransaction
|
||||
* -----------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const wrapInTransaction = (test) => {
|
||||
return async (...args) => {
|
||||
await strapi.db.transaction(async ({ trx, rollback }) => {
|
||||
@@ -10,6 +93,13 @@ export const wrapInTransaction = (test) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets the database by leveragin a transaction rollback
|
||||
*
|
||||
* NOTE:
|
||||
* Alternatively, use setupDatabaseReset() which is slower but avoids errors thrown by asnyc operations
|
||||
* executed after the test's transaction context has closed.
|
||||
*/
|
||||
export const testInTransaction = (...args: Parameters<jest.It>) => {
|
||||
if (args.length > 1) {
|
||||
return it(
|
||||
|
||||
Reference in New Issue
Block a user