Skip to content

Commit 90a6f65

Browse files
committed
Fix: Added Unit Tests for Migration
1 parent 8e0a57c commit 90a6f65

7 files changed

Lines changed: 562 additions & 272 deletions

File tree

.talismanrc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,10 @@ fileignoreconfig:
5353
checksum: f5e40f93389f4fa62be90f0203ebd849c9c882d38afe3a8c35ae91970894a74e
5454
- filename: packages/contentstack-utilities/src/proxy-helper.ts
5555
checksum: 2a8379d7a34acb3c14093599fb4ba7307c94b0f280ea70d6d862ecb2448fe924
56-
version: ""
56+
- filename: packages/contentstack-migration/test/unit/validators/edit-content-type-validator.test.ts
57+
checksum: 0eeae791e22943cd871ffd9e29db84d30abddadd381969539e9acb122d2e03af
58+
- filename: packages/contentstack-migration/test/unit/commands/cm/stacks/migration.test.ts
59+
checksum: 9efc2552963731c0ddc8feeed42c97fbde98f2472407e61f0e98502e1599993c
60+
- filename: packages/contentstack-migration/test/unit/validators/create-content-type-validator.test.ts
61+
checksum: 2b82ec338f6e2723086ffd04d9d6c324ef0c33ee286a067903bc11f539cf8907
62+
version: ""

packages/contentstack-migration/test/unit/commands/cm/stacks/migration.test.ts

Lines changed: 129 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,14 @@ describe('Migration Command', () => {
6464
// Don't stub fs operations - use real temporary files/directories instead
6565
// This avoids issues with non-configurable properties in newer Node.js versions
6666

67-
// Stub utilities - use configHandler to control isAuthenticated behavior
68-
// Default: isAuthenticated returns true (OAUTH) - individual tests can override
67+
// Stub utilities - use configHandler and isAuthenticated
6968
const cliUtilities = require('@contentstack/cli-utilities');
7069
sandbox.stub(cliUtilities.configHandler, 'get').callsFake((key: string) => {
7170
if (key === 'authorisationType') {
7271
return 'OAUTH'; // Default: authenticated
7372
}
7473
return undefined;
7574
});
76-
7775
try {
7876
managementSDKClientStub = sandbox.stub(cliUtilities, 'managementSDKClient').resolves({
7977
stack: sandbox.stub().returns({}),
@@ -118,23 +116,25 @@ describe('Migration Command', () => {
118116
expect(MigrationCommand.flags).to.have.property('file-path');
119117
expect(MigrationCommand.flags).to.have.property('alias');
120118
});
119+
120+
it('should have usage string containing migration', () => {
121+
expect(MigrationCommand.usage).to.be.a('string');
122+
expect(MigrationCommand.usage).to.include('migration');
123+
});
124+
125+
it('should have aliases including cm:migration', () => {
126+
expect(MigrationCommand.aliases).to.be.an('array');
127+
expect(MigrationCommand.aliases).to.include('cm:migration');
128+
});
121129
});
122130

123131
describe('run() method', () => {
124132
it.skip('should exit when no authtoken and no alias', async () => {
125-
// Override configHandler.get to return undefined (not authenticated)
126133
const cliUtilities = require('@contentstack/cli-utilities');
127-
sandbox.stub(cliUtilities.configHandler, 'get').callsFake((key: string) => {
128-
if (key === 'authorisationType') {
129-
return undefined; // Not authenticated
130-
}
131-
return undefined;
132-
});
133-
134+
sandbox.stub(cliUtilities.configHandler, 'get').callsFake((key: string) => undefined);
134135
parseStub.resolves({
135136
flags: {
136137
'file-path': tempFile,
137-
// No alias provided
138138
},
139139
} as any);
140140

@@ -145,7 +145,6 @@ describe('Migration Command', () => {
145145
});
146146

147147
it.skip('should exit when file path is not provided', async () => {
148-
// Don't stub execSingleFile since we exit before reaching it
149148
parseStub.resolves({
150149
flags: {},
151150
} as any);
@@ -157,7 +156,6 @@ describe('Migration Command', () => {
157156
});
158157

159158
it.skip('should exit when file path does not exist', async () => {
160-
// Use a path that definitely doesn't exist
161159
parseStub.resolves({
162160
flags: {
163161
'file-path': '/nonexistent/path/that/does/not/exist.js',
@@ -430,6 +428,123 @@ describe('Migration Command', () => {
430428
fs.rmSync(migrationLogsPath, { recursive: true, force: true });
431429
}
432430
});
431+
432+
it('should not log migration-logs path when directory does not exist', async () => {
433+
sandbox.stub(command, 'execSingleFile').resolves();
434+
const cwdStub = sandbox.stub(process, 'cwd').returns(tempDir);
435+
parseStub.resolves({
436+
flags: {
437+
'file-path': tempFile,
438+
},
439+
} as any);
440+
441+
await command.run();
442+
443+
const migrationLogCall = logStub.getCalls().find(
444+
(c: any) => c.args[1] && String(c.args[1]).includes('migration-logs')
445+
);
446+
expect(migrationLogCall).to.be.undefined;
447+
});
448+
});
449+
450+
describe('getTasks() method', () => {
451+
let safePromiseStub: SinonStub;
452+
let waterfallStub: SinonStub;
453+
454+
beforeEach(() => {
455+
safePromiseStub = sandbox.stub(utilsModule, 'safePromise').callsFake(async (p: any) => {
456+
try {
457+
const result = await p;
458+
return [null, result];
459+
} catch (err) {
460+
return [err, null];
461+
}
462+
});
463+
const asyncModule = require('async');
464+
waterfallStub = sandbox.stub(asyncModule, 'waterfall').callsFake((tasks: any[], callback?: any) => {
465+
if (typeof callback === 'function') {
466+
callback(null, 'result');
467+
}
468+
return Promise.resolve('result');
469+
});
470+
});
471+
472+
it('should return array of task objects from requests', () => {
473+
const requests = [
474+
{ title: 'Task 1', failedTitle: 'F1', successTitle: 'S1', tasks: [async () => 'r1'] },
475+
{ title: 'Task 2', failedTitle: 'F2', successTitle: 'S2', tasks: [async () => 'r2'] },
476+
];
477+
const tasks = command.getTasks(requests);
478+
expect(tasks).to.be.an('array').with.lengthOf(2);
479+
expect(tasks[0].title).to.equal('Task 1');
480+
expect(tasks[0].task).to.be.a('function');
481+
expect(tasks[1].title).to.equal('Task 2');
482+
expect(tasks[1].task).to.be.a('function');
483+
});
484+
485+
it('should return empty array when requests is empty', () => {
486+
const tasks = command.getTasks([]);
487+
expect(tasks).to.be.an('array').with.lengthOf(0);
488+
});
489+
490+
it('should run task and set success title on success', async () => {
491+
const requests = [
492+
{ title: 'T', failedTitle: 'F', successTitle: 'S', tasks: [async () => 'ok'] },
493+
];
494+
const tasks = command.getTasks(requests);
495+
const mockCtx: any = {};
496+
const mockTask: any = { title: 'T' };
497+
await tasks[0].task(mockCtx, mockTask);
498+
expect(mockTask.title).to.equal('S');
499+
});
500+
501+
it('should set failedTitle and ctx.error and throw when waterfall fails', async () => {
502+
safePromiseStub.callsFake(async (p: any) => {
503+
await p;
504+
return [new Error('waterfall failed'), null];
505+
});
506+
const requests = [
507+
{ title: 'T', failedTitle: 'Failed', successTitle: 'S', tasks: [async () => 'ok'] },
508+
];
509+
const tasks = command.getTasks(requests);
510+
const mockCtx: any = {};
511+
const mockTask: any = { title: 'T' };
512+
try {
513+
await tasks[0].task(mockCtx, mockTask);
514+
expect.fail('task should have thrown');
515+
} catch (err: any) {
516+
expect(err.message).to.equal('waterfall failed');
517+
}
518+
expect(mockTask.title).to.equal('Failed');
519+
expect(mockCtx.error).to.be.true;
520+
});
521+
522+
it('should return result from task when successful', async () => {
523+
const requests = [
524+
{ title: 'T', failedTitle: 'F', successTitle: 'S', tasks: [async () => ({ id: '123' })] },
525+
];
526+
const tasks = command.getTasks(requests);
527+
const mockCtx: any = {};
528+
const mockTask: any = { title: 'T' };
529+
const result = await tasks[0].task(mockCtx, mockTask);
530+
expect(result).to.equal('result');
531+
});
532+
});
533+
534+
describe('handleErrors() method', () => {
535+
beforeEach(() => {
536+
getStub.returns([]);
537+
});
538+
539+
it('should run without throwing when actions array is empty', () => {
540+
expect(() => command.handleErrors()).to.not.throw();
541+
});
542+
543+
it('should run and invoke validation when actions exist', () => {
544+
getStub.returns([{ type: 'create', payload: {} }]);
545+
expect(() => command.handleErrors()).to.not.throw();
546+
});
547+
433548
});
434549

435550
describe.skip('execSingleFile() method', () => {
@@ -561,137 +676,4 @@ describe('Migration Command', () => {
561676
});
562677
});
563678

564-
describe.skip('getTasks() method', () => {
565-
let safePromiseStub: SinonStub;
566-
let waterfallStub: SinonStub;
567-
568-
beforeEach(() => {
569-
safePromiseStub = sandbox.stub(utilsModule, 'safePromise').resolves([null, 'result']);
570-
waterfallStub = sandbox.stub(require('async'), 'waterfall').callsFake((tasks: any[], callback: any) => {
571-
callback(null, 'result');
572-
});
573-
});
574-
575-
it('should create tasks from requests', () => {
576-
const requests = [
577-
{
578-
title: 'Task 1',
579-
failedTitle: 'Task 1 Failed',
580-
successTitle: 'Task 1 Success',
581-
tasks: [sandbox.stub().callsArg(1)],
582-
},
583-
{
584-
title: 'Task 2',
585-
failedTitle: 'Task 2 Failed',
586-
successTitle: 'Task 2 Success',
587-
tasks: [sandbox.stub().callsArg(1)],
588-
},
589-
];
590-
591-
const tasks = command.getTasks(requests);
592-
593-
expect(tasks).to.be.an('array');
594-
expect(tasks.length).to.equal(2);
595-
expect(tasks[0]).to.have.property('title', 'Task 1');
596-
expect(tasks[0]).to.have.property('task');
597-
expect(tasks[1]).to.have.property('title', 'Task 2');
598-
expect(tasks[1]).to.have.property('task');
599-
});
600-
601-
it('should create task function that handles success', async () => {
602-
const requests = [
603-
{
604-
title: 'Test Task',
605-
failedTitle: 'Test Failed',
606-
successTitle: 'Test Success',
607-
tasks: [sandbox.stub().callsArg(1)],
608-
},
609-
];
610-
611-
const tasks = command.getTasks(requests);
612-
const taskFn = tasks[0].task;
613-
const mockCtx: any = {};
614-
const mockTask: any = {
615-
title: 'Test Task',
616-
};
617-
618-
await taskFn(mockCtx, mockTask);
619-
620-
expect(mockTask.title).to.equal('Test Success');
621-
});
622-
623-
it('should create task function that handles errors', async () => {
624-
const testError = new Error('Test error');
625-
// Make safePromise return the error
626-
safePromiseStub.resolves([testError, null]);
627-
// waterfall will be called with tasks array, and safePromise wraps it
628-
// So when safePromise resolves with [error, null], the task function should handle it
629-
waterfallStub.restore();
630-
waterfallStub = sandbox.stub(require('async'), 'waterfall').callsFake((tasks: any[], callback: any) => {
631-
// Simulate waterfall calling callback with error
632-
if (callback) {
633-
callback(testError);
634-
}
635-
});
636-
637-
const requests = [
638-
{
639-
title: 'Test Task',
640-
failedTitle: 'Test Failed',
641-
successTitle: 'Test Success',
642-
tasks: [sandbox.stub().callsArgWith(0, testError)],
643-
},
644-
];
645-
646-
const tasks = command.getTasks(requests);
647-
const taskFn = tasks[0].task;
648-
const mockCtx: any = {};
649-
const mockTask: any = {
650-
title: 'Test Task',
651-
};
652-
653-
try {
654-
await taskFn(mockCtx, mockTask);
655-
// Should not reach here
656-
expect.fail('Should have thrown an error');
657-
} catch (err) {
658-
expect(err).to.equal(testError);
659-
expect(mockCtx.error).to.be.true;
660-
expect(mockTask.title).to.equal('Test Failed');
661-
}
662-
});
663-
});
664-
665-
describe.skip('handleErrors() method', () => {
666-
let actionListInstance: any;
667-
let ActionListConstructorStub: SinonStub;
668-
669-
beforeEach(() => {
670-
actionListInstance = {
671-
addValidators: sandbox.stub(),
672-
validate: sandbox.stub().returns([]),
673-
};
674-
const actionListModule = require('../../../../../src/actions/action-list');
675-
ActionListConstructorStub = sandbox.stub(actionListModule, 'default').returns(actionListInstance);
676-
getStub.returns([]);
677-
});
678-
679-
it('should validate actions and handle errors', () => {
680-
command.handleErrors();
681-
682-
expect(getMapInstanceStub.called).to.be.true;
683-
expect(getStub.called).to.be.true;
684-
expect(ActionListConstructorStub.called).to.be.true;
685-
expect(actionListInstance.addValidators.callCount).to.equal(4);
686-
expect(actionListInstance.validate.called).to.be.true;
687-
expect(errorHelperStub.called).to.be.true;
688-
});
689-
690-
it('should add all validators', () => {
691-
command.handleErrors();
692-
693-
const validatorCalls = actionListInstance.addValidators.getCalls();
694-
expect(validatorCalls.length).to.equal(4);
695-
});
696-
});
697679
});

0 commit comments

Comments
 (0)