From b658d339442f30ac3ee209c8118fcdcb3139183f Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 5 Jul 2021 20:38:27 +0200 Subject: [PATCH] chore: added some tests for cookiecutter but they need re-writing Signed-off-by: blam --- .../builtin/fetch/cookiecutter.test.ts | 194 ++++++++++++++++++ .../actions/builtin/fetch/cookiecutter.ts | 1 + 2 files changed, 195 insertions(+) create mode 100644 plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts new file mode 100644 index 0000000000..53f255a639 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.test.ts @@ -0,0 +1,194 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const runCommand = jest.fn(); +const commandExists = jest.fn(); +const fetchContents = jest.fn(); +jest.mock('./helpers', () => ({ runCommand, fetchContents })); +jest.mock('command-exists', () => commandExists); +jest.mock('./helpers'); +jest.mock('fs-extra'); + +import fs from 'fs-extra'; +import { + getVoidLogger, + UrlReader, + ContainerRunner, +} from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import mock from 'mock-fs'; +import os from 'os'; +import { resolve as resolvePath } from 'path'; +import { PassThrough } from 'stream'; +import { createFetchCookiecutterAction } from './cookiecutter'; + +describe('fetch:cookiecutter', () => { + const integrations = ScmIntegrations.fromConfig( + new ConfigReader({ + integrations: { + azure: [ + { host: 'dev.azure.com', token: 'tokenlols' }, + { host: 'myazurehostnotoken.com' }, + ], + }, + }), + ); + + const mockTmpDir = os.tmpdir(); + const mockContext = { + input: { + url: 'https://google.com/cookie/cutter', + targetPath: 'something', + values: { + help: 'me', + }, + }, + baseUrl: 'somebase', + workspacePath: mockTmpDir, + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir), + }; + + const containerRunner: jest.Mocked = { + runContainer: jest.fn(), + }; + + const mockReader: UrlReader = { + read: jest.fn(), + readTree: jest.fn(), + search: jest.fn(), + }; + + const action = createFetchCookiecutterAction({ + integrations, + containerRunner, + reader: mockReader, + }); + + beforeEach(() => { + (fs.readdir as jest.Mock).mockReturnValueOnce(['cookiecutter.json']); + + mock({ + [`${mockContext.workspacePath}/template`]: { 'cookiecutter.json': '{}' }, + }); + mock({ [`${mockContext.workspacePath}/result`]: {} }); + mock({ + [`${mockContext.workspacePath}/intermediate`]: {}, + }); + jest.restoreAllMocks(); + commandExists.mockRejectedValue(null); + }); + + afterEach(() => { + mock.restore(); + }); + + it('should call fetchContents with the correct values', async () => { + await action.handler(mockContext); + + expect(fetchContents).toHaveBeenCalledWith({ + reader: mockReader, + integrations, + baseUrl: mockContext.baseUrl, + fetchUrl: mockContext.input.url, + outputPath: resolvePath( + mockContext.workspacePath, + `template/{{cookiecutter and 'contents'}}`, + ), + }); + }); + + it('should execute the cookiecutter templater with optional inputs if they are present and valid', async () => { + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + copyWithoutRender: ['goreleaser.yml'], + extensions: [ + 'jinja2_custom_filters_extension.string_filters_extension.StringFilterExtension', + ], + imageName: 'foo/cookiecutter-image-with-extensions', + }, + }); + + expect(cookiecutterTemplater.run).toHaveBeenCalledWith({ + workspacePath: mockTmpDir, + logStream: mockContext.logStream, + values: { + ...mockContext.input.values, + _copy_without_render: ['goreleaser.yml'], + _extensions: [ + 'jinja2_custom_filters_extension.string_filters_extension.StringFilterExtension', + ], + imageName: 'foo/cookiecutter-image-with-extensions', + }, + }); + }); + + // it('should throw if copyWithoutRender is not an Array', async () => { + // await expect( + // action.handler({ + // ...mockContext, + // input: { + // ...mockContext.input, + // copyWithoutRender: 'xyz', + // }, + // }), + // ).rejects.toThrow(/copyWithoutRender must be an Array/); + // }); + + // it('should throw if extensions is not an Array', async () => { + // await expect( + // action.handler({ + // ...mockContext, + // input: { + // ...mockContext.input, + // extensions: 'xyz', + // }, + // }), + // ).rejects.toThrow(/extensions must be an Array/); + // }); + + // it('should throw if there is no cookiecutter templater initialized', async () => { + // const templatersWithoutCookiecutter = new Templaters(); + + // const newAction = createFetchCookiecutterAction({ + // integrations, + // templaters: templatersWithoutCookiecutter, + // reader: mockReader, + // }); + + // await expect(newAction.handler(mockContext)).rejects.toThrow( + // /No templater registered/, + // ); + // }); + + // it('should throw if the target directory is outside of the workspace path', async () => { + // await expect( + // action.handler({ + // ...mockContext, + // input: { + // ...mockContext.input, + // targetPath: '/foo', + // }, + // }), + // ).rejects.toThrow( + // /Relative path is not allowed to refer to a directory outside its parent/, + // ); + // }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts index e5720204c7..2bf22ce4fd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts @@ -108,6 +108,7 @@ export class CookiecutterRunner { // if cookiecutter was successful, intermediateDir will contain // exactly one directory. + const [generated] = await fs.readdir(intermediateDir); if (generated === undefined) {