portainer/app/react/common/stacks/ItemView/StackDuplicationForm/useDuplicateStackMutation.t...

278 lines
8.0 KiB
TypeScript

import { http, HttpResponse } from 'msw';
import { server } from '@/setup-tests/server';
import { StackType } from '../../types';
import { duplicateStack } from './useDuplicateStackMutation';
type StackRequestBody = {
name: string;
swarmID: string;
env?: Array<{ name: string; value: string }>;
};
describe('Swarm stack duplication', () => {
it('should call getSwarm with targetEnvironmentId for Swarm stacks', async () => {
const swarmId = 'swarm123';
let swarmRequestCalled = false;
server.use(
http.get('/api/endpoints/:id/docker/swarm', ({ params }) => {
swarmRequestCalled = true;
expect(params.id).toBe('2');
return HttpResponse.json({ ID: swarmId });
}),
http.post('/api/stacks/create/:type/:method', async () =>
HttpResponse.json({ Id: 123 })
)
);
await duplicateStack({
name: 'test-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerSwarm,
env: [{ name: 'VAR1', value: 'value1' }],
});
expect(swarmRequestCalled).toBe(true);
});
it('should call createSwarmStackFromFileContent with correct parameters', async () => {
const swarmId = 'swarm123';
let stackRequestBody: undefined | StackRequestBody;
server.use(
http.get('/api/endpoints/:id/docker/swarm', () =>
HttpResponse.json({ ID: swarmId })
),
http.post('/api/stacks/create/:type/:method', async ({ request }) => {
stackRequestBody = (await request.json()) as StackRequestBody;
return HttpResponse.json({ Id: 123 });
})
);
const fileContent = 'version: "3"\nservices:\n app:\n image: nginx';
const env = [{ name: 'VAR1', value: 'value1' }];
await duplicateStack({
name: 'test-stack',
fileContent,
targetEnvironmentId: 2,
type: StackType.DockerSwarm,
env,
});
expect(stackRequestBody).toBeDefined();
expect(stackRequestBody?.name).toBe('test-stack');
expect(stackRequestBody?.swarmID).toBe(swarmId);
});
it('should throw error if Swarm ID is missing', async () => {
server.use(
http.get('/api/endpoints/:id/docker/swarm', () =>
HttpResponse.json({ ID: '' })
)
);
await expect(
duplicateStack({
name: 'test-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerSwarm,
})
).rejects.toThrow('Swarm ID is required');
});
it('should pass swarmID from getSwarm response', async () => {
const swarmId = 'custom-swarm-id-456';
let stackRequestBody: StackRequestBody | undefined;
server.use(
http.get('/api/endpoints/:id/docker/swarm', () =>
HttpResponse.json({ ID: swarmId })
),
http.post('/api/stacks/create/:type/:method', async ({ request }) => {
stackRequestBody = (await request.json()) as StackRequestBody;
return HttpResponse.json({ Id: 123 });
})
);
await duplicateStack({
name: 'test-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerSwarm,
});
expect(stackRequestBody?.swarmID).toBe(swarmId);
});
it('should pass environmentId, name, stackFileContent, env', async () => {
const swarmId = 'swarm123';
let stackRequestBody: StackRequestBody | undefined;
server.use(
http.get('/api/endpoints/:id/docker/swarm', () =>
HttpResponse.json({ ID: swarmId })
),
http.post('/api/stacks/create/:type/:method', async ({ request }) => {
stackRequestBody = (await request.json()) as StackRequestBody;
return HttpResponse.json({ Id: 123 });
})
);
const fileContent = 'version: "3"\nservices:\n app:\n image: nginx';
const env = [
{ name: 'VAR1', value: 'value1' },
{ name: 'VAR2', value: 'value2' },
];
await duplicateStack({
name: 'my-swarm-stack',
fileContent,
targetEnvironmentId: 3,
type: StackType.DockerSwarm,
env,
});
expect(stackRequestBody).toBeDefined();
expect(stackRequestBody?.name).toBe('my-swarm-stack');
expect(stackRequestBody?.env).toEqual(env);
});
});
describe('Standalone stack duplication', () => {
it('should call createStandaloneStackFromFileContent for non-Swarm stacks', async () => {
let stackRequestBody: StackRequestBody | undefined;
server.use(
http.post('/api/stacks/create/:type/:method', async ({ request }) => {
stackRequestBody = (await request.json()) as StackRequestBody;
return HttpResponse.json({ Id: 123 });
})
);
await duplicateStack({
name: 'standalone-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerCompose,
});
expect(stackRequestBody).toBeDefined();
expect(stackRequestBody?.name).toBe('standalone-stack');
});
it('should pass environmentId, name, stackFileContent, env', async () => {
let stackRequestBody: StackRequestBody | undefined;
server.use(
http.post('/api/stacks/create/:type/:method', async ({ request }) => {
stackRequestBody = (await request.json()) as StackRequestBody;
return HttpResponse.json({ Id: 123 });
})
);
const fileContent = 'version: "3"\nservices:\n web:\n image: nginx';
const env = [{ name: 'PORT', value: '8080' }];
await duplicateStack({
name: 'compose-stack',
fileContent,
targetEnvironmentId: 5,
type: StackType.DockerCompose,
env,
});
expect(stackRequestBody).toBeDefined();
expect(stackRequestBody?.name).toBe('compose-stack');
expect(stackRequestBody?.env).toEqual(env);
});
it('should not call getSwarm for standalone stacks', async () => {
let swarmRequestCalled = false;
server.use(
http.get('/api/endpoints/:id/docker/swarm', () => {
swarmRequestCalled = true;
return HttpResponse.json({ ID: 'swarm123' });
}),
http.post('/api/stacks/create/:type/:method', () =>
HttpResponse.json({ Id: 123 })
)
);
await duplicateStack({
name: 'standalone-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerCompose,
});
expect(swarmRequestCalled).toBe(false);
});
});
describe('error handling', () => {
it('should propagate errors from getSwarm', async () => {
server.use(
http.get('/api/endpoints/:id/docker/swarm', () =>
HttpResponse.json({ message: 'Swarm not found' }, { status: 404 })
)
);
await expect(
duplicateStack({
name: 'test-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerSwarm,
})
).rejects.toThrow();
});
it('should propagate errors from createSwarmStackFromFileContent', async () => {
server.use(
http.get('/api/endpoints/:id/docker/swarm', () =>
HttpResponse.json({ ID: 'swarm123' })
),
http.post('/api/stacks/create/:type/:method', () =>
HttpResponse.json({ message: 'Stack creation failed' }, { status: 500 })
)
);
await expect(
duplicateStack({
name: 'test-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerSwarm,
})
).rejects.toThrow();
});
it('should propagate errors from createStandaloneStackFromFileContent', async () => {
server.use(
http.post('/api/stacks/create/:type/:method', () =>
HttpResponse.json(
{ message: 'Stack name already exists' },
{ status: 409 }
)
)
);
await expect(
duplicateStack({
name: 'existing-stack',
fileContent: 'version: "3"\nservices:\n app:\n image: nginx',
targetEnvironmentId: 2,
type: StackType.DockerCompose,
})
).rejects.toThrow();
});
});