Skip to content
Open
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
24 changes: 24 additions & 0 deletions src/adapters/file-upload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,30 @@ describe('FileUpload Adapter', () => {
expect(showSuggestionMock).not.toHaveBeenCalled();
});

it('should exit with code 1 when deployment status is CANCELLED for new project', async () => {
const fileUploadInstance = new FileUpload({
config: {
isExistingProject: false,
currentDeploymentStatus: DeploymentStatus.CANCELLED,
},
log: logMock,
exit: exitMock,
} as any);

try {
await fileUploadInstance.run();
} catch (error: any) {
expect(error.message).toBe('1');
}

expect(handleNewProjectMock).toHaveBeenCalled();
expect(prepareLaunchConfigMock).toHaveBeenCalled();
expect(showLogsMock).toHaveBeenCalled();
expect(exitMock).toHaveBeenCalledWith(1);
expect(showDeploymentUrlMock).not.toHaveBeenCalled();
expect(showSuggestionMock).not.toHaveBeenCalled();
});

it('should continue normally when deployment status is not FAILED', async () => {
const fileUploadInstance = new FileUpload({
config: {
Expand Down
5 changes: 4 additions & 1 deletion src/adapters/file-upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ export default class FileUpload extends BaseClass {

this.prepareLaunchConfig();
await this.showLogs();
if(this.config.currentDeploymentStatus === DeploymentStatus.FAILED) {
if (
this.config.currentDeploymentStatus === DeploymentStatus.FAILED ||
this.config.currentDeploymentStatus === DeploymentStatus.CANCELLED
) {
this.exit(1);
}
this.showDeploymentUrl();
Expand Down
24 changes: 24 additions & 0 deletions src/adapters/github.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,30 @@ describe('GitHub Adapter', () => {
expect(showSuggestionMock).not.toHaveBeenCalled();
});

it('should exit with code 1 when deployment status is CANCELLED for new project', async () => {
const githubInstance = new GitHub({
config: {
isExistingProject: false,
currentDeploymentStatus: DeploymentStatus.CANCELLED,
},
log: logMock,
exit: exitMock,
} as any);

try {
await githubInstance.run();
} catch (error: any) {
expect(error.message).toBe('1');
}

expect(handleNewProjectMock).toHaveBeenCalled();
expect(prepareLaunchConfigMock).toHaveBeenCalled();
expect(showLogsMock).toHaveBeenCalled();
expect(exitMock).toHaveBeenCalledWith(1);
expect(showDeploymentUrlMock).not.toHaveBeenCalled();
expect(showSuggestionMock).not.toHaveBeenCalled();
});

it('should continue normally when deployment status is not FAILED', async () => {
const githubInstance = new GitHub({
config: {
Expand Down
5 changes: 4 additions & 1 deletion src/adapters/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ export default class GitHub extends BaseClass {

this.prepareLaunchConfig();
await this.showLogs();
if (this.config.currentDeploymentStatus === DeploymentStatus.FAILED) {
if (
this.config.currentDeploymentStatus === DeploymentStatus.FAILED ||
this.config.currentDeploymentStatus === DeploymentStatus.CANCELLED
) {
this.exit(1);
}
this.showDeploymentUrl();
Expand Down
2 changes: 1 addition & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const config = {
launchHubUrls: '',
launchBaseUrl: '',
supportedAdapters: ['GitHub'],
deploymentStatus: ['LIVE', 'FAILED', 'SKIPPED', 'DEPLOYED'],
deploymentStatus: ['LIVE', 'FAILED', 'SKIPPED', 'DEPLOYED', 'CANCELLED'],
Comment thread
rohan-agrawal-cs marked this conversation as resolved.
pollingInterval: 1000,
variablePreparationTypeOptions: [
VariablePreparationTypeOptions.IMPORT_FROM_STACK,
Expand Down
80 changes: 80 additions & 0 deletions src/util/logs-polling-utilities.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import { EventEmitter } from 'events';
import { logPolling as cliUtilitiesJestMock } from '../test/mocks/cli-utilities';
import LogPolling from './logs-polling-utilities';
import defaultConfig from '../config';

type LogPollingCtor = typeof import('./logs-polling-utilities').default;

jest.mock('@contentstack/cli-utilities', () => cliUtilitiesJestMock);
jest.mock('timers/promises', () => ({ setTimeout: jest.fn().mockResolvedValue(undefined) }));

function makeWatchQuery() {
let subscriber: (result: any) => void = () => {};
return {
subscribe: jest.fn((cb: (result: any) => void) => {
subscriber = cb;
return { unsubscribe: jest.fn() };
}),
setVariables: jest.fn(),
stopPolling: jest.fn(),
emit: (result: any) => subscriber(result),
};
}

const CONFIG = {
deployment: 'd1',
Expand Down Expand Up @@ -115,3 +132,66 @@ describe('LogPolling Apollo deprecation regression', () => {
expect(watchQuery).toHaveBeenCalledTimes(1);
});
});

describe('cancelled deployment stops log polling', () => {
function buildInstance(deploymentStatus: string[]) {
const statusWatchQuery = makeWatchQuery();
const logsWatchQuery = makeWatchQuery();
const config = {
deployment: 'd1',
environment: 'e1',
pollingInterval: 1000,
deploymentStatus,
};
const instance = new LogPolling({
apolloManageClient: { watchQuery: jest.fn().mockReturnValue(statusWatchQuery) } as any,
apolloLogsClient: { watchQuery: jest.fn().mockReturnValue(logsWatchQuery) } as any,
config: config as any,
$event: new EventEmitter(),
});
return { instance, statusWatchQuery, logsWatchQuery, config };
}

it('stops status polling once the deployment status is CANCELLED', async () => {
const { instance, statusWatchQuery } = buildInstance(['LIVE', 'FAILED', 'SKIPPED', 'DEPLOYED', 'CANCELLED']);

await instance.deploymentLogs();
statusWatchQuery.emit({ data: { Deployment: { status: 'CANCELLED' } } });

expect(instance.deploymentStatus).toBe('CANCELLED');
expect(statusWatchQuery.stopPolling).toHaveBeenCalledTimes(1);
});

it('stops deployment-logs polling and emits DONE once status is CANCELLED', async () => {
Comment thread
rohan-agrawal-cs marked this conversation as resolved.
const { instance, statusWatchQuery, logsWatchQuery } = buildInstance([
'LIVE',
'FAILED',
'SKIPPED',
'DEPLOYED',
'CANCELLED',
]);
const events: string[] = [];
(instance as any).$event.on('deployment-logs', (e: any) => events.push(e.message));

await instance.deploymentLogs();
statusWatchQuery.emit({ data: { Deployment: { status: 'CANCELLED' } } });
await logsWatchQuery.emit({ data: { getLogs: [] } });

expect(logsWatchQuery.stopPolling).toHaveBeenCalledTimes(1);
expect(events).toContain('DONE');
});

it('regression guard: keeps polling forever if CANCELLED is missing from deploymentStatus', async () => {
const { instance, statusWatchQuery } = buildInstance(['LIVE', 'FAILED', 'SKIPPED', 'DEPLOYED']);

await instance.deploymentLogs();
statusWatchQuery.emit({ data: { Deployment: { status: 'CANCELLED' } } });

expect(instance.deploymentStatus).toBe('CANCELLED');
expect(statusWatchQuery.stopPolling).not.toHaveBeenCalled();
});

it('real app config (src/config) lists CANCELLED as a terminal deployment status', () => {
expect(defaultConfig.deploymentStatus).toContain('CANCELLED');
});
});
Loading