Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ jobs:
- name: Run tests for Contentstack Import Plugin
working-directory: ./packages/contentstack-import
run: npm run test:unit

- name: Run tests for Contentstack Export Plugin
working-directory: ./packages/contentstack-export
run: npm run test:unit

- name: Run tests for Audit plugin
working-directory: ./packages/contentstack-audit
Expand Down
12 changes: 10 additions & 2 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
fileignoreconfig:
- filename: package-lock.json
checksum: ca12061eb32da8cb2d0e3be8e10e89b3f23b2351df8d397e811b34040c9d79b5
checksum: 3d9b941e6ae97e135e6054a218dc0322743015b78d06c88349e27384c9414bc1
- filename: pnpm-lock.yaml
checksum: 45e2fb78b203e512a8a15eb508b82a9bfcbbfaddc461c02edb194a127b5168d9
checksum: e2c471579cf21de9678f16049684925e47f3d8bd67ff711fc52fc5f409bedd2b
- filename: packages/contentstack-import-setup/test/unit/backup-handler.test.ts
checksum: 0582d62b88834554cf12951c8690a73ef3ddbb78b82d2804d994cf4148e1ef93
- filename: packages/contentstack-import-setup/test/config.json
Expand Down Expand Up @@ -151,8 +151,16 @@ fileignoreconfig:
checksum: 457912f0f1ad3cadabbdf19cff6c325164e76063f12b968a00af37ec15a875e9
- filename: packages/contentstack-export/test/unit/export/modules/global-fields.test.ts
checksum: 64d204d0ff6232d161275b1df5b2ea5612b53c72d9ba2c22bd13564229353c4d
- filename: packages/contentstack-export/test/unit/utils/common-helper.test.ts
checksum: 276e850e4caddc89372f09f4eee5832cc4ab5b513da2a662a821f5feb8561349
- filename: packages/contentstack-export/test/unit/utils/file-helper.test.ts
checksum: a16f5833515ececd93c582b35d19b8a5df4880f22126fba18f110692c679025b
- filename: packages/contentstack-export/test/unit/utils/export-config-handler.test.ts
checksum: ba02c3d580e02fc4ecd5e6a0fc59e6c7d56d7de735339aa00e2c2241ffe22176
- filename: packages/contentstack-import/test/unit/import/modules/webhooks.test.ts
checksum: 9f6dc9fb12f0d30600dac28846c7a9972e1dafe7c7bf5385ea677100a1d8fbd1
- filename: packages/contentstack-export/test/unit/utils/interactive.test.ts
checksum: b619744ebba28dbafe3a0e65781a61a6823ccaa3eb84e2b380a323c105324c1a
- filename: packages/contentstack-import/test/unit/import/modules/index.test.ts
checksum: aab773ccbe05b990a4b934396ee2fcd2a780e7d886d080740cfddd8a4d4f73f7
- filename: packages/contentstack-import/test/unit/import/modules/personalize.test.ts
Expand Down
369 changes: 230 additions & 139 deletions package-lock.json

Large diffs are not rendered by default.

255 changes: 255 additions & 0 deletions packages/contentstack-export/test/unit/utils/common-helper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
import { expect } from 'chai';
import sinon from 'sinon';
import { validateConfig, formatError, executeTask, writeExportMetaFile } from '../../../src/utils/common-helper';
import { ExternalConfig, ExportConfig } from '../../../src/types';

describe('Common Helper Utils', () => {
afterEach(() => {
sinon.restore();
});

describe('validateConfig', () => {
it('should throw error when host and cdn are missing', () => {
const config: ExternalConfig = {} as any;

expect(() => validateConfig(config)).to.throw('Host/CDN end point is missing from config');
});

it('should validate correctly with all credentials provided', () => {
const config: ExternalConfig = {
host: 'https://api.contentstack.io',
cdn: 'https://cdn.contentstack.io',
email: 'test@example.com',
password: 'password',
management_token: 'token',
access_token: 'token'
} as any;

expect(() => validateConfig(config)).to.not.throw();
});

it('should validate email and password with access_token', () => {
const config: ExternalConfig = {
host: 'https://api.contentstack.io',
cdn: 'https://cdn.contentstack.io',
email: 'test@example.com',
password: 'password',
access_token: 'token',
source_stack: 'stack-key'
} as any;

// Should not throw with access token
expect(() => validateConfig(config)).to.not.throw();
});

it('should throw error when authentication credentials are missing', () => {
const config: ExternalConfig = {
host: 'https://api.contentstack.io',
cdn: 'https://cdn.contentstack.io',
email: '',
password: '',
source_stack: 'stack-key'
} as any;

// This will throw when no valid credentials provided
try {
validateConfig(config);
// If it doesn't throw, check if email/password path throws
const config2 = { ...config, email: 'test', password: '' };
validateConfig(config2);
expect.fail('Should have thrown an error');
} catch (error: any) {
expect(error.message).to.exist;
}
});

it('should validate preserveStackVersion requires email and password', () => {
const config: ExternalConfig = {
host: 'https://api.contentstack.io',
cdn: 'https://cdn.contentstack.io',
preserveStackVersion: true
} as any;

expect(() => validateConfig(config)).to.throw('Kindly provide Email and password for stack details');
});

it('should validate with management token', () => {
const config: ExternalConfig = {
host: 'https://api.contentstack.io',
cdn: 'https://cdn.contentstack.io',
management_token: 'token',
source_stack: 'stack-key'
} as any;

expect(() => validateConfig(config)).to.not.throw();
});
});

describe('formatError', () => {
it('should format string error correctly', () => {
const errorStr = 'Simple error message';
const result = formatError(errorStr);
expect(result).to.equal(errorStr);
});

it('should parse and format JSON error string', () => {
const errorJson = JSON.stringify({ errorMessage: 'Test error' });
const result = formatError(errorJson);
expect(result).to.equal('Test error');
});

it('should format error message from Error object', () => {
const error = { message: 'Error occurred' };
const result = formatError(error);
expect(result).to.equal('Error occurred');
});

it('should include error details when available', () => {
const error = {
errorMessage: 'Main error',
errors: {
authorization: 'Invalid token',
api_key: 'Invalid key'
}
};
const result = formatError(error);
expect(result).to.include('Main error');
expect(result).to.include('Management Token Invalid token');
expect(result).to.include('Stack API key Invalid key');
});

it('should map entity names correctly', () => {
const error = {
errors: {
authorization: 'fail',
api_key: 'fail',
uid: 'fail',
access_token: 'fail'
}
};
const result = formatError(error);
expect(result).to.include('Management Token');
expect(result).to.include('Stack API key');
expect(result).to.include('Content Type');
expect(result).to.include('Delivery Token');
});

it('should handle null or undefined error gracefully', () => {
// formatError doesn't handle null gracefully, so we expect it to throw
expect(() => formatError(null as any)).to.throw();
});
});

describe('executeTask', () => {
it('should execute tasks with concurrency limit', async () => {
const tasks = [1, 2, 3, 4, 5];
const handler = async (task: unknown) => (task as number) * 2;

const results = await executeTask(tasks, handler, { concurrency: 2 });

expect(results).to.deep.equal([2, 4, 6, 8, 10]);
});

it('should handle empty tasks array', async () => {
const tasks: any[] = [];
const handler = async (): Promise<void> => { return; };

const results = await executeTask(tasks, handler, { concurrency: 1 });

expect(results).to.be.an('array');
expect(results.length).to.equal(0);
});

it('should throw error when handler is not a function', () => {
const tasks = [1, 2, 3];
const handler = 'not a function' as any;

expect(() => executeTask(tasks, handler, { concurrency: 1 })).to.throw('Invalid handler');
});

it('should execute tasks sequentially when concurrency is 1', async () => {
const order: number[] = [];
const tasks = [1, 2, 3];
const handler = async (task: unknown) => {
order.push(task as number);
return task;
};

await executeTask(tasks, handler, { concurrency: 1 });

expect(order).to.deep.equal([1, 2, 3]);
});

it('should handle task errors gracefully', async () => {
const tasks = [1, 2, 3];
const handler = async (task: unknown) => {
if ((task as number) === 2) throw new Error('Task failed');
return task;
};

try {
await executeTask(tasks, handler, { concurrency: 1 });
expect.fail('Should have thrown an error');
} catch (error) {
expect(error).to.exist;
}
});
});

describe('writeExportMetaFile', () => {
it('should write export meta file with correct data', () => {
const exportConfig: ExportConfig = {
contentVersion: 1,
exportDir: '/test/export'
} as ExportConfig;

// Stub FsUtility constructor to avoid fs operations
const FsUtility = require('@contentstack/cli-utilities').FsUtility;
const originalWriteFile = FsUtility.prototype.writeFile;
const writeFileStub = sinon.stub().resolves();
FsUtility.prototype.writeFile = writeFileStub;

writeExportMetaFile(exportConfig);

// Verify that writeFile was called with correct data
expect(writeFileStub.called).to.be.true;
const filePath = writeFileStub.firstCall.args[0];
const metaData = writeFileStub.firstCall.args[1];

expect(filePath).to.include('export-info.json');
expect(metaData.contentVersion).to.equal(1);
expect(metaData.logsPath).to.exist;

// Restore original
FsUtility.prototype.writeFile = originalWriteFile;
});

it('should accept custom meta file path', () => {
const exportConfig: ExportConfig = {
contentVersion: 2,
exportDir: '/test/export'
} as ExportConfig;

// Stub FsUtility constructor to avoid fs operations
const FsUtility = require('@contentstack/cli-utilities').FsUtility;
const originalWriteFile = FsUtility.prototype.writeFile;
const writeFileStub = sinon.stub().resolves();
FsUtility.prototype.writeFile = writeFileStub;

writeExportMetaFile(exportConfig, '/custom/path');

// Verify that writeFile was called with custom path and correct data
expect(writeFileStub.called).to.be.true;
const filePath = writeFileStub.firstCall.args[0];
const metaData = writeFileStub.firstCall.args[1];

expect(filePath).to.include('/custom/path');
expect(filePath).to.include('export-info.json');
expect(metaData.contentVersion).to.equal(2);

// Restore original
FsUtility.prototype.writeFile = originalWriteFile;
});
});
});

Loading
Loading