fix tests

This commit is contained in:
Ben Irvin 2023-08-23 14:18:57 +02:00
parent 6221af275d
commit dadc1f78e9
4 changed files with 104 additions and 37 deletions

View File

@ -29,7 +29,7 @@ export const getStrapiFactory =
>( >(
properties?: T properties?: T
) => ) =>
(additionalProperties?: T) => { (additionalProperties?: Partial<T>) => {
return { ...properties, ...additionalProperties } as Strapi.Strapi; return { ...properties, ...additionalProperties } as Strapi.Strapi;
}; };

View File

@ -24,6 +24,25 @@ const transaction = jest.fn(async (cb) => {
await cb({ trx, rollback }); await cb({ trx, rollback });
}); });
const strapiFactory = getStrapiFactory({
dirs: {
static: {
public: 'static/public/assets',
},
},
db: { transaction },
config: {
get(service) {
if (service === 'plugin.upload') {
return {
provider: 'local',
};
}
return {};
},
},
});
describe('Local Strapi Destination Provider - Get Assets Stream', () => { describe('Local Strapi Destination Provider - Get Assets Stream', () => {
test('Throws an error if the Strapi instance is not provided', async () => { test('Throws an error if the Strapi instance is not provided', async () => {
/* @ts-ignore: disable-next-line */ /* @ts-ignore: disable-next-line */
@ -35,17 +54,14 @@ describe('Local Strapi Destination Provider - Get Assets Stream', () => {
'Not able to stream Assets. Strapi instance not found' 'Not able to stream Assets. Strapi instance not found'
); );
}); });
test('Returns a stream', async () => {
test('Returns a stream when assets restore is true', async () => {
const provider = createLocalStrapiDestinationProvider({ const provider = createLocalStrapiDestinationProvider({
getStrapi: getStrapiFactory({ getStrapi: () => strapiFactory(),
dirs: {
static: {
public: 'static/public/assets',
},
},
db: { transaction },
}),
strategy: 'restore', strategy: 'restore',
restore: {
assets: true,
},
}); });
await provider.bootstrap(); await provider.bootstrap();
@ -54,6 +70,21 @@ describe('Local Strapi Destination Provider - Get Assets Stream', () => {
expect(stream instanceof Writable).toBeTruthy(); expect(stream instanceof Writable).toBeTruthy();
}); });
test('Throw an error if attempting to create stream while restore assets is false', async () => {
const provider = createLocalStrapiDestinationProvider({
getStrapi: () => strapiFactory(),
strategy: 'restore',
restore: {
assets: false,
},
});
await provider.bootstrap();
expect(async () => provider.createAssetsWriteStream()).rejects.toThrow(
'Attempting to transfer assets when they are not included'
);
});
test('Writes on the strapi assets path', async () => { test('Writes on the strapi assets path', async () => {
(fse.createWriteStream as jest.Mock).mockImplementationOnce(createWriteStreamMock); (fse.createWriteStream as jest.Mock).mockImplementationOnce(createWriteStreamMock);
const assetsDirectory = 'static/public/assets'; const assetsDirectory = 'static/public/assets';
@ -64,15 +95,18 @@ describe('Local Strapi Destination Provider - Get Assets Stream', () => {
stream: Readable.from(['test', 'test-2']), stream: Readable.from(['test', 'test-2']),
}; };
const provider = createLocalStrapiDestinationProvider({ const provider = createLocalStrapiDestinationProvider({
getStrapi: getStrapiFactory({ getStrapi: () =>
dirs: { strapiFactory({
static: { dirs: {
public: assetsDirectory, static: {
public: assetsDirectory,
},
}, },
}, }),
db: { transaction },
}),
strategy: 'restore', strategy: 'restore',
restore: {
assets: true,
},
}); });
await provider.bootstrap(); await provider.bootstrap();

View File

@ -19,7 +19,11 @@ jest.mock('../strategies/restore', () => {
const strapiCommonProperties = { const strapiCommonProperties = {
config: { config: {
get: jest.fn().mockReturnValue({ provider: 'aws-s3' }), get(service) {
if (service === 'plugin.upload') {
return { provider: 'local' };
}
},
}, },
dirs: { dirs: {
static: { static: {
@ -44,6 +48,11 @@ describe('Local Strapi Source Destination', () => {
...strapiCommonProperties, ...strapiCommonProperties,
}), }),
strategy: 'restore', strategy: 'restore',
restore: {
entities: {
exclude: [],
},
},
}); });
expect(provider.strapi).not.toBeDefined(); expect(provider.strapi).not.toBeDefined();
@ -56,6 +65,11 @@ describe('Local Strapi Source Destination', () => {
...strapiCommonProperties, ...strapiCommonProperties,
}), }),
strategy: 'restore', strategy: 'restore',
restore: {
entities: {
exclude: [],
},
},
}); });
await provider.bootstrap(); await provider.bootstrap();
@ -71,6 +85,11 @@ describe('Local Strapi Source Destination', () => {
...strapiCommonProperties, ...strapiCommonProperties,
}), }),
strategy: 'restore', strategy: 'restore',
restore: {
entities: {
exclude: [],
},
},
}); });
await restoreProvider.bootstrap(); await restoreProvider.bootstrap();
expect(restoreProvider.strapi).toBeDefined(); expect(restoreProvider.strapi).toBeDefined();
@ -89,7 +108,9 @@ describe('Local Strapi Source Destination', () => {
).rejects.toThrow(); ).rejects.toThrow();
}); });
test('Should delete all entities if it is a restore', async () => { test.todo('Should not delete entities that are not included');
test('Should delete all entities if it is a restore with only exclude property', async () => {
const entities = [ const entities = [
{ {
entity: { id: 1, title: 'My first foo' }, entity: { id: 1, title: 'My first foo' },
@ -161,6 +182,11 @@ describe('Local Strapi Source Destination', () => {
const provider = createLocalStrapiDestinationProvider({ const provider = createLocalStrapiDestinationProvider({
getStrapi: () => strapi, getStrapi: () => strapi,
strategy: 'restore', strategy: 'restore',
restore: {
entities: {
exclude: [],
},
},
}); });
const deleteAllSpy = jest.spyOn(restoreApi, 'deleteRecords'); const deleteAllSpy = jest.spyOn(restoreApi, 'deleteRecords');
await provider.bootstrap(); await provider.bootstrap();

View File

@ -13,7 +13,11 @@ import type {
import { restore } from './strategies'; import { restore } from './strategies';
import * as utils from '../../../utils'; import * as utils from '../../../utils';
import { ProviderTransferError, ProviderValidationError } from '../../../errors/providers'; import {
ProviderInitializationError,
ProviderTransferError,
ProviderValidationError,
} from '../../../errors/providers';
import { assertValidStrapi } from '../../../utils/providers'; import { assertValidStrapi } from '../../../utils/providers';
export const VALID_CONFLICT_STRATEGIES = ['restore']; export const VALID_CONFLICT_STRATEGIES = ['restore'];
@ -54,6 +58,9 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
async bootstrap(): Promise<void> { async bootstrap(): Promise<void> {
this.#validateOptions(); this.#validateOptions();
this.strapi = await this.options.getStrapi(); this.strapi = await this.options.getStrapi();
if (!this.strapi) {
throw new ProviderInitializationError('Could not access local strapi');
}
this.transaction = utils.transaction.createTransaction(this.strapi); this.transaction = utils.transaction.createTransaction(this.strapi);
} }
@ -99,7 +106,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
} }
if ( if (
strapi.config.get('plugin.upload').provider !== 'local' && this.strapi?.config.get('plugin.upload').provider !== 'local' &&
this.#areAssetsIncluded() && this.#areAssetsIncluded() &&
!this.#isContentTypeIncluded('plugin::upload.file') !this.#isContentTypeIncluded('plugin::upload.file')
) { ) {
@ -126,7 +133,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
return; return;
} }
const stream: Readable = strapi.db const stream: Readable = this.strapi.db
// Create a query builder instance (default type is 'select') // Create a query builder instance (default type is 'select')
.queryBuilder('plugin::upload.file') .queryBuilder('plugin::upload.file')
// Fetch all columns // Fetch all columns
@ -138,10 +145,10 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
// TODO use bulk delete when exists in providers // TODO use bulk delete when exists in providers
for await (const file of stream) { for await (const file of stream) {
await strapi.plugin('upload').provider.delete(file); await this.strapi.plugin('upload').provider.delete(file);
if (file.formats) { if (file.formats) {
for (const fileFormat of Object.values(file.formats)) { for (const fileFormat of Object.values(file.formats)) {
await strapi.plugin('upload').provider.delete(fileFormat); await this.strapi.plugin('upload').provider.delete(fileFormat);
} }
} }
} }
@ -171,7 +178,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
} }
getMetadata(): IMetadata { getMetadata(): IMetadata {
const strapiVersion = strapi.config.get('info.strapi'); const strapiVersion = this.strapi?.config.get('info.strapi');
const createdAt = new Date().toISOString(); const createdAt = new Date().toISOString();
return { return {
@ -185,8 +192,8 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
getSchemas() { getSchemas() {
assertValidStrapi(this.strapi, 'Not able to get Schemas'); assertValidStrapi(this.strapi, 'Not able to get Schemas');
const schemas = { const schemas = {
...this.strapi.contentTypes, ...this.strapi?.contentTypes,
...this.strapi.components, ...this.strapi?.components,
}; };
return utils.schema.mapSchemasValues(schemas); return utils.schema.mapSchemasValues(schemas);
@ -227,7 +234,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
return; return;
} }
if (strapi.config.get('plugin.upload').provider === 'local') { if (this.strapi.config.get('plugin.upload').provider === 'local') {
const assetsDirectory = path.join(this.strapi.dirs.static.public, 'uploads'); const assetsDirectory = path.join(this.strapi.dirs.static.public, 'uploads');
const backupDirectory = path.join( const backupDirectory = path.join(
this.strapi.dirs.static.public, this.strapi.dirs.static.public,
@ -267,7 +274,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
} }
// TODO: this should catch all thrown errors and bubble it up to engine so it can be reported as a non-fatal diagnostic message telling the user they may need to manually delete assets // TODO: this should catch all thrown errors and bubble it up to engine so it can be reported as a non-fatal diagnostic message telling the user they may need to manually delete assets
if (strapi.config.get('plugin.upload').provider === 'local') { if (this.strapi?.config.get('plugin.upload').provider === 'local') {
assertValidStrapi(this.strapi); assertValidStrapi(this.strapi);
const backupDirectory = path.join( const backupDirectory = path.join(
this.strapi.dirs.static.public, this.strapi.dirs.static.public,
@ -286,7 +293,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
} }
const removeAssetsBackup = this.#removeAssetsBackup.bind(this); const removeAssetsBackup = this.#removeAssetsBackup.bind(this);
const strapi = this.strapi; const thisStrapi = this.strapi;
const transaction = this.transaction; const transaction = this.transaction;
const backupDirectory = this.uploadsBackupDirectoryName; const backupDirectory = this.uploadsBackupDirectoryName;
@ -304,7 +311,7 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
// TODO: Remove this logic in V5 // TODO: Remove this logic in V5
if (!chunk.metadata) { if (!chunk.metadata) {
// If metadata does not exist is because it is an old backup file // If metadata does not exist is because it is an old backup file
const assetsDirectory = path.join(strapi.dirs.static.public, 'uploads'); const assetsDirectory = path.join(thisStrapi.dirs.static.public, 'uploads');
const entryPath = path.join(assetsDirectory, chunk.filename); const entryPath = path.join(assetsDirectory, chunk.filename);
const writableStream = fse.createWriteStream(entryPath); const writableStream = fse.createWriteStream(entryPath);
chunk.stream chunk.stream
@ -342,10 +349,10 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
buffer: chunk?.buffer, buffer: chunk?.buffer,
}; };
const provider = strapi.config.get('plugin.upload').provider; const provider = thisStrapi.config.get('plugin.upload').provider;
try { try {
await strapi.plugin('upload').provider.uploadStream(uploadData); await thisStrapi.plugin('upload').provider.uploadStream(uploadData);
// if we're not supposed to transfer the associated entities, stop here // if we're not supposed to transfer the associated entities, stop here
if (!restoreMediaEntitiesContent) { if (!restoreMediaEntitiesContent) {
@ -354,14 +361,14 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
// Files formats are stored within the parent file entity // Files formats are stored within the parent file entity
if (uploadData?.type) { if (uploadData?.type) {
const entry: IFile = await strapi.db.query('plugin::upload.file').findOne({ const entry: IFile = await thisStrapi.db.query('plugin::upload.file').findOne({
where: { hash: uploadData.mainHash }, where: { hash: uploadData.mainHash },
}); });
const specificFormat = entry?.formats?.[uploadData.type]; const specificFormat = entry?.formats?.[uploadData.type];
if (specificFormat) { if (specificFormat) {
specificFormat.url = uploadData.url; specificFormat.url = uploadData.url;
} }
await strapi.db.query('plugin::upload.file').update({ await thisStrapi.db.query('plugin::upload.file').update({
where: { hash: uploadData.mainHash }, where: { hash: uploadData.mainHash },
data: { data: {
formats: entry.formats, formats: entry.formats,
@ -370,11 +377,11 @@ class LocalStrapiDestinationProvider implements IDestinationProvider {
}); });
return callback(); return callback();
} }
const entry: IFile = await strapi.db.query('plugin::upload.file').findOne({ const entry: IFile = await thisStrapi.db.query('plugin::upload.file').findOne({
where: { hash: uploadData.hash }, where: { hash: uploadData.hash },
}); });
entry.url = uploadData.url; entry.url = uploadData.url;
await strapi.db.query('plugin::upload.file').update({ await thisStrapi.db.query('plugin::upload.file').update({
where: { hash: uploadData.hash }, where: { hash: uploadData.hash },
data: { data: {
url: entry.url, url: entry.url,