import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import type { Application } from '@spinnaker/core';
import { ConfirmationModalService, DeckRuntimeContext, ServerGroupWarningMessageService } from '@spinnaker/core';
import type { IAmazonServerGroupView } from '../../domain';
import { AWSProviderSettings } from '../../aws.settings';
import { AmazonServerGroupActionsComponent as AmazonServerGroupActions } from './AmazonServerGroupActions';
import { AmazonRollbackServerGroupModal } from './rollback';
describe(' rollback integration', () => {
const originalAdHocInfraWritesEnabled = AWSProviderSettings.adHocInfraWritesEnabled;
const buildServerGroup = (overrides: Partial = {}): IAmazonServerGroupView =>
({
account: 'test-account',
app: 'test-app',
capacity: { desired: 2, max: 2, min: 0 },
cloudProvider: 'aws',
cluster: 'test-app-main',
createdTime: 2,
instanceCounts: { total: 2 },
isDisabled: false,
name: 'test-app-main-v002',
moniker: { app: 'test-app', cluster: 'test-app-main' },
region: 'us-east-1',
...overrides,
} as IAmazonServerGroupView);
const buildApplication = (serverGroups: IAmazonServerGroupView[]): Application =>
({
attributes: {},
getDataSource: (key: string) => (key === 'serverGroups' ? { data: serverGroups } : undefined),
isManagementPaused: false,
name: 'test-app',
serverGroups: { refresh: vi.fn() },
} as any);
const renderActions = (
app: Application,
serverGroup: IAmazonServerGroupView,
stateService: any = { go: vi.fn(), includes: vi.fn() },
runtimeServices: any = {},
) => ({
runtimeServices,
...render(
,
),
});
const openActions = () => userEvent.click(screen.getByRole('button', { name: 'Server Group Actions' }));
beforeEach(() => {
AWSProviderSettings.adHocInfraWritesEnabled = true;
});
afterEach(() => {
AWSProviderSettings.adHocInfraWritesEnabled = originalAdHocInfraWritesEnabled;
});
it('renders standalone Rollback as a managed action and opens the modal with exact enriched state', async () => {
const selected = buildServerGroup({ isDisabled: true, name: 'test-app-main-v001' });
const rollbackSource = buildServerGroup();
const unrelated = buildServerGroup({ app: 'other-app', cluster: 'other-app-main', name: 'other-app-main-v001' });
const application = buildApplication([selected, rollbackSource, unrelated]);
const show = vi.spyOn(AmazonRollbackServerGroupModal, 'show').mockReturnValue(Promise.resolve({} as any));
const { runtimeServices } = renderActions(application, selected);
await openActions();
await userEvent.click(screen.getByText('Rollback'));
expect(show).toHaveBeenCalledExactlyOnceWith(
{
allServerGroups: [selected],
application,
previousServerGroup: selected,
serverGroup: rollbackSource,
},
runtimeServices,
);
});
it('does not render Rollback when a disabled server group has no enabled rollback source', async () => {
const selected = buildServerGroup({ isDisabled: true, name: 'test-app-main-v001' });
renderActions(buildApplication([selected]), selected);
await openActions();
expect(screen.queryByText('Rollback')).not.toBeInTheDocument();
});
it('opens rollback settings when orchestrated rollback is accepted from Enable', async () => {
const selected = buildServerGroup({ isDisabled: true, name: 'test-app-main-v001' });
const rollbackSource = buildServerGroup();
const application = buildApplication([selected, rollbackSource]);
const confirm = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(Promise.resolve() as any);
const show = vi.spyOn(AmazonRollbackServerGroupModal, 'show').mockReturnValue(Promise.resolve({} as any));
const { runtimeServices } = renderActions(application, selected);
await openActions();
await userEvent.click(screen.getByText('Enable'));
await settle();
expect(confirm).toHaveBeenCalledTimes(1);
expect(show).toHaveBeenCalledExactlyOnceWith(
{
allServerGroups: [selected],
application,
previousServerGroup: selected,
serverGroup: rollbackSource,
},
runtimeServices,
);
});
it('continues to the ordinary Enable confirmation when orchestrated rollback is declined', async () => {
const selected = buildServerGroup({ isDisabled: true, name: 'test-app-main-v001' });
const rollbackSource = buildServerGroup();
const application = buildApplication([selected, rollbackSource]);
const confirm = vi
.spyOn(ConfirmationModalService, 'confirm')
.mockImplementation((params: any) =>
params.header === 'Rolling back?' ? (Promise.reject({ source: 'footer' }) as any) : (Promise.resolve() as any),
);
const show = vi.spyOn(AmazonRollbackServerGroupModal, 'show').mockReturnValue(Promise.resolve({} as any));
renderActions(application, selected);
await openActions();
await userEvent.click(screen.getByText('Enable'));
await settle();
expect(show).not.toHaveBeenCalled();
expect(confirm).toHaveBeenCalledTimes(2);
expect(confirm.mock.lastCall[0]).toEqual(
expect.objectContaining({
header: `Really enable ${selected.name}?`,
submitMethod: expect.any(Function),
}),
);
});
it('does nothing when the orchestrated rollback prompt is dismissed', async () => {
const selected = buildServerGroup({ isDisabled: true, name: 'test-app-main-v001' });
const rollbackSource = buildServerGroup();
const application = buildApplication([selected, rollbackSource]);
let rejectConfirmation: (reason: any) => void;
const confirmation = new Promise((_, reject) => {
rejectConfirmation = reject;
});
const confirm = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(confirmation as any);
const show = vi.spyOn(AmazonRollbackServerGroupModal, 'show').mockReturnValue(Promise.resolve({} as any));
const writer = { enableServerGroup: vi.fn() };
const enable = writer.enableServerGroup;
const runtimeServices = { serverGroupWriter: writer } as any;
renderActions(application, selected, undefined, runtimeServices);
await openActions();
await userEvent.click(screen.getByText('Enable'));
await act(async () => {
rejectConfirmation({ source: 'header' });
await confirmation.catch(() => undefined);
});
expect(confirm).toHaveBeenCalledTimes(1);
expect(show).not.toHaveBeenCalled();
expect(enable).not.toHaveBeenCalled();
});
it('closes destroyed server group details through the injected state service', async () => {
const selected = buildServerGroup();
const stateService = { go: vi.fn(), includes: vi.fn().mockReturnValue(true) };
const confirm = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(undefined);
vi.spyOn(ServerGroupWarningMessageService, 'addDestroyWarningMessage').mockReturnValue(undefined);
renderActions(buildApplication([selected]), selected, stateService);
await openActions();
await userEvent.click(screen.getByText('Destroy'));
confirm.mock.lastCall[0].taskMonitorConfig.onTaskComplete();
expect(stateService.includes).toHaveBeenCalledWith('**.serverGroup', {
accountId: 'test-account',
name: 'test-app-main-v002',
region: 'us-east-1',
});
expect(stateService.go).toHaveBeenCalledWith('^');
});
});
const settle = () => act(() => new Promise((resolve) => setTimeout(resolve)));