Merge pull request #10146 from backstage/rugvip/noglobalmocks
scaffolder-backend,techdocs-node: remove global dependency mocks
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
export const mockGitlabClient = {
|
||||
Namespaces: {
|
||||
show: jest.fn(),
|
||||
},
|
||||
Projects: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
Users: {
|
||||
current: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export class Gitlab {
|
||||
constructor() {
|
||||
return mockGitlabClient;
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
export const mockGitApi = {
|
||||
createRepository: jest.fn(),
|
||||
};
|
||||
|
||||
export class GitApi {
|
||||
constructor() {
|
||||
return mockGitApi;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
export const mockIndex = {
|
||||
addAll: jest.fn(),
|
||||
write: jest.fn(),
|
||||
writeTree: jest.fn().mockResolvedValue('mockoid'),
|
||||
};
|
||||
|
||||
export const mockRepo = {
|
||||
refreshIndex: jest.fn().mockResolvedValue(mockIndex),
|
||||
createCommit: jest.fn(),
|
||||
};
|
||||
|
||||
export const mockRemote = {
|
||||
push: jest.fn(),
|
||||
};
|
||||
|
||||
const Repository = { init: jest.fn().mockResolvedValue(mockRepo) };
|
||||
const Remote = { create: jest.fn().mockResolvedValue(mockRemote) };
|
||||
const Signature = { now: jest.fn() };
|
||||
const Cred = {
|
||||
userpassPlaintextNew: jest.fn(),
|
||||
};
|
||||
|
||||
export { Repository, Remote, Signature, Cred };
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
export const mockGithubClient = {
|
||||
rest: {
|
||||
actions: {
|
||||
createWorkflowDispatch: jest.fn(),
|
||||
},
|
||||
repos: {
|
||||
createInOrg: jest.fn(),
|
||||
createForAuthenticatedUser: jest.fn(),
|
||||
createWebhook: jest.fn(),
|
||||
addCollaborator: jest.fn(),
|
||||
replaceAllTopics: jest.fn(),
|
||||
},
|
||||
users: {
|
||||
getByUsername: jest.fn(),
|
||||
},
|
||||
teams: {
|
||||
addOrUpdateRepoPermissionsInOrg: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export class Octokit {
|
||||
constructor() {
|
||||
return mockGithubClient;
|
||||
}
|
||||
}
|
||||
+19
-7
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
jest.mock('octokit');
|
||||
import { TemplateAction } from '../../types';
|
||||
import { createGithubActionsDispatchAction } from './githubActionsDispatch';
|
||||
|
||||
@@ -27,6 +26,21 @@ import { ConfigReader } from '@backstage/config';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
actions: {
|
||||
createWorkflowDispatch: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock('octokit', () => ({
|
||||
Octokit: class {
|
||||
constructor() {
|
||||
return mockOctokit;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('github:actions:dispatch', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
@@ -54,8 +68,6 @@ describe('github:actions:dispatch', () => {
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
const { mockGithubClient } = require('octokit');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
githubCredentialsProvider =
|
||||
@@ -67,7 +79,7 @@ describe('github:actions:dispatch', () => {
|
||||
});
|
||||
|
||||
it('should call the githubApis for creating WorkflowDispatch without an input object', async () => {
|
||||
mockGithubClient.rest.actions.createWorkflowDispatch.mockResolvedValue({
|
||||
mockOctokit.rest.actions.createWorkflowDispatch.mockResolvedValue({
|
||||
data: {
|
||||
foo: 'bar',
|
||||
},
|
||||
@@ -82,7 +94,7 @@ describe('github:actions:dispatch', () => {
|
||||
await action.handler(ctx);
|
||||
|
||||
expect(
|
||||
mockGithubClient.rest.actions.createWorkflowDispatch,
|
||||
mockOctokit.rest.actions.createWorkflowDispatch,
|
||||
).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
@@ -92,7 +104,7 @@ describe('github:actions:dispatch', () => {
|
||||
});
|
||||
|
||||
it('should call the githubApis for creating WorkflowDispatch with an input object', async () => {
|
||||
mockGithubClient.rest.actions.createWorkflowDispatch.mockResolvedValue({
|
||||
mockOctokit.rest.actions.createWorkflowDispatch.mockResolvedValue({
|
||||
data: {
|
||||
foo: 'bar',
|
||||
},
|
||||
@@ -108,7 +120,7 @@ describe('github:actions:dispatch', () => {
|
||||
await action.handler(ctx);
|
||||
|
||||
expect(
|
||||
mockGithubClient.rest.actions.createWorkflowDispatch,
|
||||
mockOctokit.rest.actions.createWorkflowDispatch,
|
||||
).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
|
||||
+22
-11
@@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
jest.mock('octokit');
|
||||
|
||||
import { createGithubWebhookAction } from './githubWebhook';
|
||||
import {
|
||||
ScmIntegrations,
|
||||
@@ -27,6 +25,21 @@ import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
import { TemplateAction } from '../..';
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
repos: {
|
||||
createWebhook: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock('octokit', () => ({
|
||||
Octokit: class {
|
||||
constructor() {
|
||||
return mockOctokit;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('github:repository:webhook:create', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
@@ -65,8 +78,6 @@ describe('github:repository:webhook:create', () => {
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
const { mockGithubClient } = require('octokit');
|
||||
|
||||
it('should call the githubApi for creating repository Webhook', async () => {
|
||||
const repoUrl = 'github.com?repo=repo&owner=owner';
|
||||
const webhookUrl = 'https://example.com/payload';
|
||||
@@ -75,7 +86,7 @@ describe('github:repository:webhook:create', () => {
|
||||
});
|
||||
await action.handler(ctx);
|
||||
|
||||
expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
events: ['push'],
|
||||
@@ -97,7 +108,7 @@ describe('github:repository:webhook:create', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
events: ['push'],
|
||||
@@ -118,7 +129,7 @@ describe('github:repository:webhook:create', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
events: ['push', 'pull_request'],
|
||||
@@ -139,7 +150,7 @@ describe('github:repository:webhook:create', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
events: ['push'],
|
||||
@@ -160,7 +171,7 @@ describe('github:repository:webhook:create', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
events: ['push'],
|
||||
@@ -181,7 +192,7 @@ describe('github:repository:webhook:create', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
events: ['push'],
|
||||
@@ -202,7 +213,7 @@ describe('github:repository:webhook:create', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createWebhook).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
events: ['push'],
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import { TemplateAction } from '../../types';
|
||||
|
||||
jest.mock('../helpers');
|
||||
jest.mock('octokit');
|
||||
|
||||
import { createPublishGithubAction } from './github';
|
||||
import {
|
||||
@@ -34,6 +33,30 @@ import {
|
||||
} from '../helpers';
|
||||
import { when } from 'jest-when';
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
users: {
|
||||
getByUsername: jest.fn(),
|
||||
},
|
||||
repos: {
|
||||
addCollaborator: jest.fn(),
|
||||
createInOrg: jest.fn(),
|
||||
createForAuthenticatedUser: jest.fn(),
|
||||
replaceAllTopics: jest.fn(),
|
||||
},
|
||||
teams: {
|
||||
addOrUpdateRepoPermissionsInOrg: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock('octokit', () => ({
|
||||
Octokit: class {
|
||||
constructor() {
|
||||
return mockOctokit;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('publish:github', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
@@ -62,8 +85,6 @@ describe('publish:github', () => {
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
const { mockGithubClient } = require('octokit');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
githubCredentialsProvider =
|
||||
@@ -76,14 +97,14 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should call the githubApis with the correct values for createInOrg', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'Organization' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createInOrg.mockResolvedValue({ data: {} });
|
||||
mockOctokit.rest.repos.createInOrg.mockResolvedValue({ data: {} });
|
||||
|
||||
await action.handler(mockContext);
|
||||
expect(mockGithubClient.rest.repos.createInOrg).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({
|
||||
description: 'description',
|
||||
name: 'repo',
|
||||
org: 'owner',
|
||||
@@ -102,7 +123,7 @@ describe('publish:github', () => {
|
||||
repoVisibility: 'public',
|
||||
},
|
||||
});
|
||||
expect(mockGithubClient.rest.repos.createInOrg).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.createInOrg).toHaveBeenCalledWith({
|
||||
description: 'description',
|
||||
name: 'repo',
|
||||
org: 'owner',
|
||||
@@ -116,17 +137,17 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should call the githubApis with the correct values for createForAuthenticatedUser', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {},
|
||||
});
|
||||
|
||||
await action.handler(mockContext);
|
||||
expect(
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser,
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser,
|
||||
).toHaveBeenCalledWith({
|
||||
description: 'description',
|
||||
name: 'repo',
|
||||
@@ -145,7 +166,7 @@ describe('publish:github', () => {
|
||||
},
|
||||
});
|
||||
expect(
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser,
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser,
|
||||
).toHaveBeenCalledWith({
|
||||
description: 'description',
|
||||
name: 'repo',
|
||||
@@ -158,11 +179,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should call initRepoAndPush with the correct values', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -182,11 +203,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should call initRepoAndPush with the correct defaultBranch main', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -235,11 +256,11 @@ describe('publish:github', () => {
|
||||
githubCredentialsProvider,
|
||||
});
|
||||
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -279,11 +300,11 @@ describe('publish:github', () => {
|
||||
githubCredentialsProvider,
|
||||
});
|
||||
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -304,11 +325,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should add access for the team when it starts with the owner', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -318,7 +339,7 @@ describe('publish:github', () => {
|
||||
await action.handler(mockContext);
|
||||
|
||||
expect(
|
||||
mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg,
|
||||
mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg,
|
||||
).toHaveBeenCalledWith({
|
||||
org: 'owner',
|
||||
team_slug: 'blam',
|
||||
@@ -329,11 +350,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should add outside collaborators when provided', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -348,7 +369,7 @@ describe('publish:github', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.addCollaborator).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.addCollaborator).toHaveBeenCalledWith({
|
||||
username: 'outsidecollaborator',
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
@@ -357,11 +378,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should add multiple collaborators when provided', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -392,7 +413,7 @@ describe('publish:github', () => {
|
||||
};
|
||||
|
||||
expect(
|
||||
mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[1],
|
||||
mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[1],
|
||||
).toEqual([
|
||||
{
|
||||
...commonProperties,
|
||||
@@ -402,7 +423,7 @@ describe('publish:github', () => {
|
||||
]);
|
||||
|
||||
expect(
|
||||
mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2],
|
||||
mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2],
|
||||
).toEqual([
|
||||
{
|
||||
...commonProperties,
|
||||
@@ -413,18 +434,18 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should ignore failures when adding multiple collaborators', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
},
|
||||
});
|
||||
|
||||
when(mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg)
|
||||
when(mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg)
|
||||
.calledWith({
|
||||
org: 'owner',
|
||||
owner: 'owner',
|
||||
@@ -452,7 +473,7 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
expect(
|
||||
mockGithubClient.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2],
|
||||
mockOctokit.rest.teams.addOrUpdateRepoPermissionsInOrg.mock.calls[2],
|
||||
).toEqual([
|
||||
{
|
||||
org: 'owner',
|
||||
@@ -465,18 +486,18 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should add topics when provided', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
},
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.replaceAllTopics.mockResolvedValue({
|
||||
mockOctokit.rest.repos.replaceAllTopics.mockResolvedValue({
|
||||
data: {
|
||||
names: ['node.js'],
|
||||
},
|
||||
@@ -490,7 +511,7 @@ describe('publish:github', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.replaceAllTopics).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.replaceAllTopics).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
names: ['node.js'],
|
||||
@@ -498,18 +519,18 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should lowercase topics when provided', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
},
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.replaceAllTopics.mockResolvedValue({
|
||||
mockOctokit.rest.repos.replaceAllTopics.mockResolvedValue({
|
||||
data: {
|
||||
names: ['backstage'],
|
||||
},
|
||||
@@ -523,7 +544,7 @@ describe('publish:github', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockGithubClient.rest.repos.replaceAllTopics).toHaveBeenCalledWith({
|
||||
expect(mockOctokit.rest.repos.replaceAllTopics).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
names: ['backstage'],
|
||||
@@ -531,11 +552,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should call output with the remoteUrl and the repoContentsUrl', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -555,11 +576,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should use main as default branch', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
@@ -585,11 +606,11 @@ describe('publish:github', () => {
|
||||
});
|
||||
|
||||
it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of requireCodeOwnerReviews', async () => {
|
||||
mockGithubClient.rest.users.getByUsername.mockResolvedValue({
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockGithubClient.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
name: 'repository',
|
||||
},
|
||||
@@ -599,7 +620,7 @@ describe('publish:github', () => {
|
||||
|
||||
expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
client: mockGithubClient,
|
||||
client: mockOctokit,
|
||||
repoName: 'repository',
|
||||
logger: mockContext.logger,
|
||||
defaultBranch: 'master',
|
||||
@@ -616,7 +637,7 @@ describe('publish:github', () => {
|
||||
|
||||
expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
client: mockGithubClient,
|
||||
client: mockOctokit,
|
||||
repoName: 'repository',
|
||||
logger: mockContext.logger,
|
||||
defaultBranch: 'master',
|
||||
@@ -633,7 +654,7 @@ describe('publish:github', () => {
|
||||
|
||||
expect(enableBranchProtectionOnDefaultRepoBranch).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
client: mockGithubClient,
|
||||
client: mockOctokit,
|
||||
repoName: 'repository',
|
||||
logger: mockContext.logger,
|
||||
defaultBranch: 'master',
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
jest.mock('../helpers');
|
||||
jest.mock('@gitbeaker/node');
|
||||
|
||||
import { createPublishGitlabAction } from './gitlab';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
@@ -23,6 +22,25 @@ import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { PassThrough } from 'stream';
|
||||
import { initRepoAndPush } from '../helpers';
|
||||
|
||||
const mockGitlabClient = {
|
||||
Namespaces: {
|
||||
show: jest.fn(),
|
||||
},
|
||||
Projects: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
Users: {
|
||||
current: jest.fn(),
|
||||
},
|
||||
};
|
||||
jest.mock('@gitbeaker/node', () => ({
|
||||
Gitlab: class {
|
||||
constructor() {
|
||||
return mockGitlabClient;
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
describe('publish:gitlab', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
@@ -54,8 +72,6 @@ describe('publish:gitlab', () => {
|
||||
createTemporaryDirectory: jest.fn(),
|
||||
};
|
||||
|
||||
const { mockGitlabClient } = require('@gitbeaker/node');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
export class DefaultAzureCredential {
|
||||
/**
|
||||
* Creates an instance of the DefaultAzureCredential class.
|
||||
*/
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
import {
|
||||
BlobUploadCommonResponse,
|
||||
ContainerGetPropertiesResponse,
|
||||
} from '@azure/storage-blob';
|
||||
import { EventEmitter } from 'events';
|
||||
import { IStorageFilesMock } from '../../src/testUtils/types';
|
||||
|
||||
const storage = global.storageFilesMock as IStorageFilesMock;
|
||||
|
||||
export class BlockBlobClient {
|
||||
private readonly blobName;
|
||||
|
||||
constructor(blobName: string) {
|
||||
this.blobName = blobName;
|
||||
}
|
||||
|
||||
uploadFile(source: string): Promise<BlobUploadCommonResponse> {
|
||||
storage.writeFile(this.blobName, source);
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 200,
|
||||
headers: {} as any,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
exists() {
|
||||
return storage.fileExists(this.blobName);
|
||||
}
|
||||
|
||||
download() {
|
||||
const emitter = new EventEmitter();
|
||||
setTimeout(() => {
|
||||
if (storage.fileExists(this.blobName)) {
|
||||
emitter.emit('data', storage.readFile(this.blobName));
|
||||
emitter.emit('end');
|
||||
} else {
|
||||
emitter.emit(
|
||||
'error',
|
||||
new Error(`The file ${this.blobName} does not exist!`),
|
||||
);
|
||||
}
|
||||
}, 0);
|
||||
return Promise.resolve({
|
||||
readableStreamBody: emitter,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class BlockBlobClientFailUpload extends BlockBlobClient {
|
||||
uploadFile(): Promise<BlobUploadCommonResponse> {
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 500,
|
||||
headers: {} as any,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerClientIterator {
|
||||
private containerName: string;
|
||||
|
||||
constructor(containerName) {
|
||||
this.containerName = containerName;
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (
|
||||
this.containerName === 'delete_stale_files_success' ||
|
||||
this.containerName === 'delete_stale_files_error'
|
||||
) {
|
||||
return {
|
||||
value: {
|
||||
segment: {
|
||||
blobItems: [{ name: `stale_file.png` }],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: {
|
||||
segment: {
|
||||
blobItems: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ContainerClient {
|
||||
getProperties(): Promise<ContainerGetPropertiesResponse> {
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 200,
|
||||
headers: {} as any,
|
||||
parsedHeaders: {},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getBlockBlobClient(blobName: string) {
|
||||
return new BlockBlobClient(blobName);
|
||||
}
|
||||
|
||||
listBlobsFlat() {
|
||||
return {
|
||||
byPage: () => {
|
||||
return new ContainerClientIterator(this.containerName);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
deleteBlob() {
|
||||
if (this.containerName === 'delete_stale_files_error') {
|
||||
throw new Error('Message');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerClientFailGetProperties extends ContainerClient {
|
||||
getProperties(): Promise<ContainerGetPropertiesResponse> {
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 404,
|
||||
headers: {} as any,
|
||||
parsedHeaders: {},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerClientFailUpload extends ContainerClient {
|
||||
getBlockBlobClient(blobName: string) {
|
||||
return new BlockBlobClientFailUpload(blobName);
|
||||
}
|
||||
}
|
||||
|
||||
export class BlobServiceClient {
|
||||
private readonly url;
|
||||
private readonly credential;
|
||||
|
||||
constructor(url: string, credential?: StorageSharedKeyCredential) {
|
||||
storage.emptyFiles();
|
||||
this.url = url;
|
||||
this.credential = credential;
|
||||
}
|
||||
|
||||
getContainerClient(containerName: string) {
|
||||
if (containerName === 'bad_container') {
|
||||
return new ContainerClientFailGetProperties();
|
||||
}
|
||||
if (this.credential.accountName === 'bad_account_credentials') {
|
||||
return new ContainerClientFailUpload();
|
||||
}
|
||||
return new ContainerClient();
|
||||
}
|
||||
}
|
||||
|
||||
export class StorageSharedKeyCredential {
|
||||
private readonly accountName;
|
||||
private readonly accountKey;
|
||||
|
||||
constructor(accountName: string, accountKey: string) {
|
||||
this.accountName = accountName;
|
||||
this.accountKey = accountKey;
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
import { Readable } from 'stream';
|
||||
import { IStorageFilesMock } from '../../src/testUtils/types';
|
||||
|
||||
const storage = global.storageFilesMock as IStorageFilesMock;
|
||||
|
||||
class GCSFile {
|
||||
private readonly path: string;
|
||||
|
||||
constructor(path: string) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
exists() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (storage.fileExists(this.path)) {
|
||||
resolve([true]);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createReadStream() {
|
||||
const readable = new Readable();
|
||||
readable._read = () => {};
|
||||
|
||||
process.nextTick(() => {
|
||||
if (storage.fileExists(this.path)) {
|
||||
if (readable.eventNames().includes('pipe')) {
|
||||
readable.emit('pipe');
|
||||
}
|
||||
readable.emit('data', storage.readFile(this.path));
|
||||
readable.emit('end');
|
||||
} else {
|
||||
readable.emit(
|
||||
'error',
|
||||
new Error(`The file ${this.path} does not exist!`),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return readable;
|
||||
}
|
||||
|
||||
delete() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class Bucket {
|
||||
private readonly bucketName;
|
||||
|
||||
constructor(bucketName: string) {
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
|
||||
async getMetadata() {
|
||||
if (this.bucketName === 'bad_bucket_name') {
|
||||
throw Error('Bucket does not exist');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
upload(source: string, { destination }) {
|
||||
return new Promise(async resolve => {
|
||||
storage.writeFile(destination, source);
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
file(destinationFilePath: string) {
|
||||
if (this.bucketName === 'delete_stale_files_error') {
|
||||
throw Error('Message');
|
||||
}
|
||||
return new GCSFile(destinationFilePath);
|
||||
}
|
||||
|
||||
getFilesStream() {
|
||||
const readable = new Readable();
|
||||
readable._read = () => {};
|
||||
|
||||
process.nextTick(() => {
|
||||
if (
|
||||
this.bucketName === 'delete_stale_files_success' ||
|
||||
this.bucketName === 'delete_stale_files_error'
|
||||
) {
|
||||
readable.emit('data', { name: 'stale-file.png' });
|
||||
}
|
||||
readable.emit('end');
|
||||
});
|
||||
|
||||
return readable;
|
||||
}
|
||||
}
|
||||
|
||||
export class Storage {
|
||||
constructor() {
|
||||
storage.emptyFiles();
|
||||
}
|
||||
|
||||
bucket(bucketName) {
|
||||
return new Bucket(bucketName);
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
import fs from 'fs-extra';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import {
|
||||
ContainerMetaResponse,
|
||||
DownloadResponse,
|
||||
NotFound,
|
||||
ObjectMetaResponse,
|
||||
UploadResponse,
|
||||
} from '@trendyol-js/openstack-swift-sdk';
|
||||
import { Stream, Readable } from 'stream';
|
||||
|
||||
const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
|
||||
const checkFileExists = async (Key: string): Promise<boolean> => {
|
||||
// Key will always have / as file separator irrespective of OS since cloud providers expects /.
|
||||
// Normalize Key to OS specific path before checking if file exists.
|
||||
const filePath = path.join(rootDir, Key);
|
||||
|
||||
try {
|
||||
await fs.access(filePath, fs.constants.F_OK);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const streamToBuffer = (stream: Stream | Readable): Promise<Buffer> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const chunks: any[] = [];
|
||||
stream.on('data', chunk => chunks.push(chunk));
|
||||
stream.on('error', reject);
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to parse the response data ${e.message}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export class SwiftClient {
|
||||
async getMetadata(_containerName: string, file: string) {
|
||||
const fileExists = await checkFileExists(file);
|
||||
if (fileExists) {
|
||||
return new ObjectMetaResponse({
|
||||
fullPath: file,
|
||||
});
|
||||
}
|
||||
return new NotFound();
|
||||
}
|
||||
|
||||
async getContainerMetadata(containerName: string) {
|
||||
if (containerName === 'mock') {
|
||||
return new ContainerMetaResponse({
|
||||
size: 10,
|
||||
});
|
||||
}
|
||||
return new NotFound();
|
||||
}
|
||||
|
||||
async upload(_containerName: string, destination: string, stream: Readable) {
|
||||
try {
|
||||
const filePath = path.join(rootDir, destination);
|
||||
const fileBuffer = await streamToBuffer(stream);
|
||||
|
||||
await fs.writeFile(filePath, fileBuffer);
|
||||
const fileExists = await checkFileExists(destination);
|
||||
|
||||
if (fileExists) {
|
||||
return new UploadResponse(filePath);
|
||||
}
|
||||
const errorMessage = `Unable to upload file(s) to OpenStack Swift.`;
|
||||
throw new Error(errorMessage);
|
||||
} catch (error) {
|
||||
const errorMessage = `Unable to upload file(s) to OpenStack Swift. ${error}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
async download(_containerName: string, file: string) {
|
||||
const filePath = path.join(rootDir, file);
|
||||
const fileExists = await checkFileExists(file);
|
||||
if (!fileExists) {
|
||||
return new NotFound();
|
||||
}
|
||||
return new DownloadResponse([], fs.createReadStream(filePath));
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
import { EventEmitter } from 'events';
|
||||
import { ReadStream } from 'fs';
|
||||
import { IStorageFilesMock } from '../src/testUtils/types';
|
||||
|
||||
export { Credentials } from 'aws-sdk';
|
||||
|
||||
const storage = global.storageFilesMock as IStorageFilesMock;
|
||||
|
||||
export class S3 {
|
||||
constructor() {
|
||||
storage.emptyFiles();
|
||||
}
|
||||
|
||||
headObject({ Key }: { Key: string }) {
|
||||
return {
|
||||
promise: async () => {
|
||||
if (!storage.fileExists(Key)) {
|
||||
throw new Error('File does not exist');
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getObject({ Key }: { Key: string }) {
|
||||
return {
|
||||
promise: async () => storage.fileExists(Key),
|
||||
createReadStream: () => {
|
||||
const emitter = new EventEmitter();
|
||||
process.nextTick(() => {
|
||||
if (storage.fileExists(Key)) {
|
||||
emitter.emit('data', Buffer.from(storage.readFile(Key)));
|
||||
emitter.emit('end');
|
||||
} else {
|
||||
emitter.emit('error', new Error(`The file ${Key} does not exist!`));
|
||||
}
|
||||
});
|
||||
return emitter;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
headBucket({ Bucket }) {
|
||||
return {
|
||||
promise: async () => {
|
||||
if (Bucket === 'errorBucket') {
|
||||
throw new Error('Bucket does not exist');
|
||||
}
|
||||
return {};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
upload({ Key, Body }: { Key: string; Body: ReadStream }) {
|
||||
return {
|
||||
promise: () =>
|
||||
new Promise(async resolve => {
|
||||
const chunks = [];
|
||||
Body.on('data', chunk => {
|
||||
chunks.push(chunk);
|
||||
});
|
||||
Body.once('end', () => {
|
||||
storage.writeFile(Key, Buffer.concat(chunks));
|
||||
resolve(null);
|
||||
});
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
listObjectsV2({ Bucket }) {
|
||||
return {
|
||||
promise: () => {
|
||||
if (
|
||||
Bucket === 'delete_stale_files_success' ||
|
||||
Bucket === 'delete_stale_files_error'
|
||||
) {
|
||||
return Promise.resolve({
|
||||
Contents: [{ Key: 'stale_file.png' }],
|
||||
});
|
||||
}
|
||||
return Promise.resolve({});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
deleteObject({ Bucket }) {
|
||||
return {
|
||||
promise: () => {
|
||||
if (Bucket === 'delete_stale_files_error') {
|
||||
throw new Error('Message');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
S3,
|
||||
};
|
||||
@@ -72,10 +72,5 @@
|
||||
"@types/recursive-readdir": "^2.2.0",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"supertest": "^6.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
".."
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
import { StorageFilesMock } from './testUtils/StorageFilesMock';
|
||||
|
||||
(global as any).rootDir = StorageFilesMock.rootDir;
|
||||
(global as any).storageFilesMock = new StorageFilesMock();
|
||||
@@ -21,12 +21,112 @@ import express from 'express';
|
||||
import request from 'supertest';
|
||||
import mockFs from 'mock-fs';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import fs, { ReadStream } from 'fs-extra';
|
||||
import { EventEmitter } from 'events';
|
||||
import { AwsS3Publish } from './awsS3';
|
||||
import { storageRootDir } from '../../testUtils/StorageFilesMock';
|
||||
|
||||
// NOTE: /plugins/techdocs-node/__mocks__ is being used to mock aws-sdk client library
|
||||
jest.mock('aws-sdk', () => {
|
||||
const { StorageFilesMock } = require('../../testUtils/StorageFilesMock');
|
||||
const storage = new StorageFilesMock();
|
||||
|
||||
const rootDir = (global as any).rootDir; // Set by setupTests.ts
|
||||
return {
|
||||
__esModule: true,
|
||||
Credentials: jest.requireActual('aws-sdk').Credentials,
|
||||
default: {
|
||||
S3: class {
|
||||
constructor() {
|
||||
storage.emptyFiles();
|
||||
}
|
||||
|
||||
headObject({ Key }: { Key: string }) {
|
||||
return {
|
||||
promise: async () => {
|
||||
if (!storage.fileExists(Key)) {
|
||||
throw new Error('File does not exist');
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getObject({ Key }: { Key: string }) {
|
||||
return {
|
||||
promise: async () => storage.fileExists(Key),
|
||||
createReadStream: () => {
|
||||
const emitter = new EventEmitter();
|
||||
process.nextTick(() => {
|
||||
if (storage.fileExists(Key)) {
|
||||
emitter.emit('data', Buffer.from(storage.readFile(Key)));
|
||||
emitter.emit('end');
|
||||
} else {
|
||||
emitter.emit(
|
||||
'error',
|
||||
new Error(`The file ${Key} does not exist!`),
|
||||
);
|
||||
}
|
||||
});
|
||||
return emitter;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
headBucket({ Bucket }: { Bucket: string }) {
|
||||
return {
|
||||
promise: async () => {
|
||||
if (Bucket === 'errorBucket') {
|
||||
throw new Error('Bucket does not exist');
|
||||
}
|
||||
return {};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
upload({ Key, Body }: { Key: string; Body: ReadStream }) {
|
||||
return {
|
||||
promise: () =>
|
||||
new Promise(async resolve => {
|
||||
const chunks = new Array<Buffer>();
|
||||
Body.on('data', chunk => {
|
||||
chunks.push(chunk as Buffer);
|
||||
});
|
||||
Body.once('end', () => {
|
||||
storage.writeFile(Key, Buffer.concat(chunks));
|
||||
resolve(null);
|
||||
});
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
listObjectsV2({ Bucket }: { Bucket: string }) {
|
||||
return {
|
||||
promise: () => {
|
||||
if (
|
||||
Bucket === 'delete_stale_files_success' ||
|
||||
Bucket === 'delete_stale_files_error'
|
||||
) {
|
||||
return Promise.resolve({
|
||||
Contents: [{ Key: 'stale_file.png' }],
|
||||
});
|
||||
}
|
||||
return Promise.resolve({});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
deleteObject({ Bucket }: { Bucket: string }) {
|
||||
return {
|
||||
promise: () => {
|
||||
if (Bucket === 'delete_stale_files_error') {
|
||||
throw new Error('Message');
|
||||
}
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const getEntityRootDir = (entity: Entity) => {
|
||||
const {
|
||||
@@ -34,7 +134,7 @@ const getEntityRootDir = (entity: Entity) => {
|
||||
metadata: { namespace, name },
|
||||
} = entity;
|
||||
|
||||
return path.join(rootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
return path.join(storageRootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
};
|
||||
|
||||
const logger = getVoidLogger();
|
||||
@@ -213,7 +313,7 @@ describe('AwsS3Publish', () => {
|
||||
|
||||
it('should fail to publish a directory', async () => {
|
||||
const wrongPathToGeneratedDirectory = path.join(
|
||||
rootDir,
|
||||
storageRootDir,
|
||||
'wrong',
|
||||
'path',
|
||||
'to',
|
||||
|
||||
@@ -23,10 +23,207 @@ import mockFs from 'mock-fs';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { AzureBlobStoragePublish } from './azureBlobStorage';
|
||||
import { EventEmitter } from 'events';
|
||||
import {
|
||||
BlobUploadCommonResponse,
|
||||
ContainerGetPropertiesResponse,
|
||||
} from '@azure/storage-blob';
|
||||
import {
|
||||
storageRootDir,
|
||||
StorageFilesMock,
|
||||
} from '../../testUtils/StorageFilesMock';
|
||||
|
||||
// NOTE: /plugins/techdocs-node/__mocks__ is being used to mock Azure client library
|
||||
jest.mock('@azure/identity', () => ({
|
||||
__esModule: true,
|
||||
DefaultAzureCredential: class {},
|
||||
}));
|
||||
|
||||
const rootDir = (global as any).rootDir; // Set by setupTests.ts
|
||||
jest.mock('@azure/storage-blob', () => {
|
||||
class BlockBlobClient {
|
||||
constructor(
|
||||
private readonly blobName: string,
|
||||
private readonly storage: StorageFilesMock,
|
||||
) {}
|
||||
|
||||
uploadFile(source: string): Promise<BlobUploadCommonResponse> {
|
||||
this.storage.writeFile(this.blobName, source);
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 200,
|
||||
headers: {} as any,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
exists() {
|
||||
return this.storage.fileExists(this.blobName);
|
||||
}
|
||||
|
||||
download() {
|
||||
const emitter = new EventEmitter();
|
||||
setTimeout(() => {
|
||||
if (this.storage.fileExists(this.blobName)) {
|
||||
emitter.emit('data', this.storage.readFile(this.blobName));
|
||||
emitter.emit('end');
|
||||
} else {
|
||||
emitter.emit(
|
||||
'error',
|
||||
new Error(`The file ${this.blobName} does not exist!`),
|
||||
);
|
||||
}
|
||||
}, 0);
|
||||
return Promise.resolve({
|
||||
readableStreamBody: emitter,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class BlockBlobClientFailUpload extends BlockBlobClient {
|
||||
uploadFile(): Promise<BlobUploadCommonResponse> {
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 500,
|
||||
headers: {} as any,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerClientIterator {
|
||||
private containerName: string;
|
||||
|
||||
constructor(containerName: string) {
|
||||
this.containerName = containerName;
|
||||
}
|
||||
|
||||
async next() {
|
||||
if (
|
||||
this.containerName === 'delete_stale_files_success' ||
|
||||
this.containerName === 'delete_stale_files_error'
|
||||
) {
|
||||
return {
|
||||
value: {
|
||||
segment: {
|
||||
blobItems: [{ name: `stale_file.png` }],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
value: {
|
||||
segment: {
|
||||
blobItems: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerClient {
|
||||
constructor(
|
||||
private readonly containerName: string,
|
||||
protected readonly storage: StorageFilesMock,
|
||||
) {}
|
||||
|
||||
getProperties(): Promise<ContainerGetPropertiesResponse> {
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 200,
|
||||
headers: {} as any,
|
||||
parsedHeaders: {},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
getBlockBlobClient(blobName: string) {
|
||||
return new BlockBlobClient(blobName, this.storage);
|
||||
}
|
||||
|
||||
listBlobsFlat() {
|
||||
return {
|
||||
byPage: () => {
|
||||
return new ContainerClientIterator(this.containerName);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
deleteBlob() {
|
||||
if (this.containerName === 'delete_stale_files_error') {
|
||||
throw new Error('Message');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerClientFailGetProperties extends ContainerClient {
|
||||
getProperties(): Promise<ContainerGetPropertiesResponse> {
|
||||
return Promise.resolve({
|
||||
_response: {
|
||||
request: {
|
||||
url: `https://example.blob.core.windows.net`,
|
||||
} as any,
|
||||
status: 404,
|
||||
headers: {} as any,
|
||||
parsedHeaders: {},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerClientFailUpload extends ContainerClient {
|
||||
getBlockBlobClient(blobName: string) {
|
||||
return new BlockBlobClientFailUpload(blobName, this.storage);
|
||||
}
|
||||
}
|
||||
|
||||
class BlobServiceClient {
|
||||
storage = new StorageFilesMock();
|
||||
|
||||
constructor(
|
||||
public readonly url: string,
|
||||
private readonly credential?: StorageSharedKeyCredential,
|
||||
) {
|
||||
this.storage.emptyFiles();
|
||||
}
|
||||
|
||||
getContainerClient(containerName: string) {
|
||||
if (containerName === 'bad_container') {
|
||||
return new ContainerClientFailGetProperties(
|
||||
containerName,
|
||||
this.storage,
|
||||
);
|
||||
}
|
||||
if (this.credential?.accountName === 'bad_account_credentials') {
|
||||
return new ContainerClientFailUpload(containerName, this.storage);
|
||||
}
|
||||
return new ContainerClient(containerName, this.storage);
|
||||
}
|
||||
}
|
||||
|
||||
class StorageSharedKeyCredential {
|
||||
readonly accountName;
|
||||
readonly accountKey;
|
||||
|
||||
constructor(accountName: string, accountKey: string) {
|
||||
this.accountName = accountName;
|
||||
this.accountKey = accountKey;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
BlobServiceClient,
|
||||
StorageSharedKeyCredential,
|
||||
};
|
||||
});
|
||||
|
||||
const getEntityRootDir = (entity: Entity) => {
|
||||
const {
|
||||
@@ -34,7 +231,7 @@ const getEntityRootDir = (entity: Entity) => {
|
||||
metadata: { namespace, name },
|
||||
} = entity;
|
||||
|
||||
return path.join(rootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
return path.join(storageRootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
};
|
||||
|
||||
const logger = getVoidLogger();
|
||||
@@ -178,7 +375,7 @@ describe('AzureBlobStoragePublish', () => {
|
||||
|
||||
it('should fail to publish a directory', async () => {
|
||||
const wrongPathToGeneratedDirectory = path.join(
|
||||
rootDir,
|
||||
storageRootDir,
|
||||
'wrong',
|
||||
'path',
|
||||
'to',
|
||||
|
||||
@@ -22,11 +22,119 @@ import request from 'supertest';
|
||||
import mockFs from 'mock-fs';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { Readable } from 'stream';
|
||||
import { GoogleGCSPublish } from './googleStorage';
|
||||
import {
|
||||
storageRootDir,
|
||||
StorageFilesMock,
|
||||
} from '../../testUtils/StorageFilesMock';
|
||||
|
||||
// NOTE: /plugins/techdocs-node/__mocks__ is being used to mock Google Cloud Storage client library
|
||||
jest.mock('@google-cloud/storage', () => {
|
||||
class GCSFile {
|
||||
constructor(
|
||||
private readonly filePath: string,
|
||||
private readonly storage: StorageFilesMock,
|
||||
) {}
|
||||
|
||||
const rootDir = (global as any).rootDir; // Set by setupTests.ts
|
||||
exists() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (this.storage.fileExists(this.filePath)) {
|
||||
resolve([true]);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createReadStream() {
|
||||
const readable = new Readable();
|
||||
readable._read = () => {};
|
||||
|
||||
process.nextTick(() => {
|
||||
if (this.storage.fileExists(this.filePath)) {
|
||||
if (readable.eventNames().includes('pipe')) {
|
||||
readable.emit('pipe');
|
||||
}
|
||||
readable.emit('data', this.storage.readFile(this.filePath));
|
||||
readable.emit('end');
|
||||
} else {
|
||||
readable.emit(
|
||||
'error',
|
||||
new Error(`The file ${this.filePath} does not exist!`),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return readable;
|
||||
}
|
||||
|
||||
delete() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class Bucket {
|
||||
constructor(
|
||||
private readonly bucketName: string,
|
||||
private readonly storage: StorageFilesMock,
|
||||
) {}
|
||||
|
||||
async getMetadata() {
|
||||
if (this.bucketName === 'bad_bucket_name') {
|
||||
throw Error('Bucket does not exist');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
upload(source: string, { destination }: { destination: string }) {
|
||||
return new Promise(async resolve => {
|
||||
this.storage.writeFile(destination, source);
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
file(destinationFilePath: string) {
|
||||
if (this.bucketName === 'delete_stale_files_error') {
|
||||
throw Error('Message');
|
||||
}
|
||||
return new GCSFile(destinationFilePath, this.storage);
|
||||
}
|
||||
|
||||
getFilesStream() {
|
||||
const readable = new Readable();
|
||||
readable._read = () => {};
|
||||
|
||||
process.nextTick(() => {
|
||||
if (
|
||||
this.bucketName === 'delete_stale_files_success' ||
|
||||
this.bucketName === 'delete_stale_files_error'
|
||||
) {
|
||||
readable.emit('data', { name: 'stale-file.png' });
|
||||
}
|
||||
readable.emit('end');
|
||||
});
|
||||
|
||||
return readable;
|
||||
}
|
||||
}
|
||||
|
||||
class Storage {
|
||||
storage = new StorageFilesMock();
|
||||
|
||||
constructor() {
|
||||
this.storage.emptyFiles();
|
||||
}
|
||||
|
||||
bucket(bucketName: string) {
|
||||
return new Bucket(bucketName, this.storage);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
Storage,
|
||||
};
|
||||
});
|
||||
|
||||
const getEntityRootDir = (entity: Entity) => {
|
||||
const {
|
||||
@@ -34,7 +142,7 @@ const getEntityRootDir = (entity: Entity) => {
|
||||
metadata: { namespace, name },
|
||||
} = entity;
|
||||
|
||||
return path.join(rootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
return path.join(storageRootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
};
|
||||
|
||||
const logger = getVoidLogger();
|
||||
@@ -193,7 +301,7 @@ describe('GoogleGCSPublish', () => {
|
||||
|
||||
it('should fail to publish a directory', async () => {
|
||||
const wrongPathToGeneratedDirectory = path.join(
|
||||
rootDir,
|
||||
storageRootDir,
|
||||
'wrong',
|
||||
'path',
|
||||
'to',
|
||||
|
||||
@@ -24,12 +24,106 @@ import { ConfigReader } from '@backstage/config';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import mockFs from 'mock-fs';
|
||||
import os from 'os';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { OpenStackSwiftPublish } from './openStackSwift';
|
||||
import { PublisherBase, TechDocsMetadata } from './types';
|
||||
import { storageRootDir } from '../../testUtils/StorageFilesMock';
|
||||
import { Stream, Readable } from 'stream';
|
||||
|
||||
// NOTE: /plugins/techdocs-node/__mocks__ is being used to mock @trendyol-js/openstack-swift-sdk client library
|
||||
jest.mock('@trendyol-js/openstack-swift-sdk', () => {
|
||||
const {
|
||||
ContainerMetaResponse,
|
||||
DownloadResponse,
|
||||
NotFound,
|
||||
ObjectMetaResponse,
|
||||
UploadResponse,
|
||||
}: typeof import('@trendyol-js/openstack-swift-sdk') = jest.requireActual(
|
||||
'@trendyol-js/openstack-swift-sdk',
|
||||
);
|
||||
|
||||
const checkFileExists = async (Key: string): Promise<boolean> => {
|
||||
// Key will always have / as file separator irrespective of OS since cloud providers expects /.
|
||||
// Normalize Key to OS specific path before checking if file exists.
|
||||
const filePath = path.join(storageRootDir, Key);
|
||||
|
||||
try {
|
||||
await fs.access(filePath, fs.constants.F_OK);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const streamToBuffer = (stream: Stream | Readable): Promise<Buffer> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const chunks: any[] = [];
|
||||
stream.on('data', chunk => chunks.push(chunk));
|
||||
stream.on('error', reject);
|
||||
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
} catch (e) {
|
||||
throw new Error(`Unable to parse the response data ${e.message}`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
SwiftClient: class {
|
||||
async getMetadata(_containerName: string, file: string) {
|
||||
const fileExists = await checkFileExists(file);
|
||||
if (fileExists) {
|
||||
return new ObjectMetaResponse({
|
||||
fullPath: file,
|
||||
});
|
||||
}
|
||||
return new NotFound();
|
||||
}
|
||||
|
||||
async getContainerMetadata(containerName: string) {
|
||||
if (containerName === 'mock') {
|
||||
return new ContainerMetaResponse({
|
||||
size: 10,
|
||||
});
|
||||
}
|
||||
return new NotFound();
|
||||
}
|
||||
|
||||
async upload(
|
||||
_containerName: string,
|
||||
destination: string,
|
||||
stream: Readable,
|
||||
) {
|
||||
try {
|
||||
const filePath = path.join(storageRootDir, destination);
|
||||
const fileBuffer = await streamToBuffer(stream);
|
||||
|
||||
await fs.writeFile(filePath, fileBuffer);
|
||||
const fileExists = await checkFileExists(destination);
|
||||
|
||||
if (fileExists) {
|
||||
return new UploadResponse(filePath);
|
||||
}
|
||||
const errorMessage = `Unable to upload file(s) to OpenStack Swift.`;
|
||||
throw new Error(errorMessage);
|
||||
} catch (error) {
|
||||
const errorMessage = `Unable to upload file(s) to OpenStack Swift. ${error}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
async download(_containerName: string, file: string) {
|
||||
const filePath = path.join(storageRootDir, file);
|
||||
const fileExists = await checkFileExists(file);
|
||||
if (!fileExists) {
|
||||
return new NotFound();
|
||||
}
|
||||
return new DownloadResponse([], fs.createReadStream(filePath));
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const createMockEntity = (annotations = {}): Entity => {
|
||||
return {
|
||||
@@ -51,15 +145,13 @@ const createMockEntityName = (): CompoundEntityRef => ({
|
||||
namespace: 'test-namespace',
|
||||
});
|
||||
|
||||
const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
|
||||
const getEntityRootDir = (entity: Entity) => {
|
||||
const {
|
||||
kind,
|
||||
metadata: { namespace, name },
|
||||
} = entity;
|
||||
|
||||
return path.join(rootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
return path.join(storageRootDir, namespace || DEFAULT_NAMESPACE, kind, name);
|
||||
};
|
||||
|
||||
const getPosixEntityRootDir = (entity: Entity) => {
|
||||
@@ -179,7 +271,7 @@ describe('OpenStackSwiftPublish', () => {
|
||||
|
||||
it('should fail to publish a directory', async () => {
|
||||
const wrongPathToGeneratedDirectory = path.join(
|
||||
rootDir,
|
||||
storageRootDir,
|
||||
'wrong',
|
||||
'path',
|
||||
'to',
|
||||
|
||||
@@ -31,6 +31,11 @@ const discovery: jest.Mocked<PluginEndpointDiscovery> = {
|
||||
getExternalBaseUrl: jest.fn(),
|
||||
};
|
||||
|
||||
jest.mock('@azure/identity', () => ({
|
||||
__esModule: true,
|
||||
DefaultAzureCredential: class {},
|
||||
}));
|
||||
|
||||
describe('Publisher', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules(); // clear the cache
|
||||
|
||||
@@ -19,12 +19,13 @@ import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import { IStorageFilesMock } from './types';
|
||||
|
||||
const rootDir: string = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
export const storageRootDir: string =
|
||||
os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
|
||||
const encoding = 'utf8';
|
||||
|
||||
export class StorageFilesMock implements IStorageFilesMock {
|
||||
static rootDir = rootDir;
|
||||
static rootDir = storageRootDir;
|
||||
|
||||
private files: Record<string, string>;
|
||||
|
||||
@@ -37,20 +38,20 @@ export class StorageFilesMock implements IStorageFilesMock {
|
||||
}
|
||||
|
||||
public fileExists(targetPath: string): boolean {
|
||||
const filePath = path.join(rootDir, targetPath);
|
||||
const filePath = path.join(storageRootDir, targetPath);
|
||||
const posixPath = filePath.split(path.posix.sep).join(path.sep);
|
||||
return this.files[posixPath] !== undefined;
|
||||
}
|
||||
|
||||
public readFile(targetPath: string): Buffer {
|
||||
const filePath = path.join(rootDir, targetPath);
|
||||
const filePath = path.join(storageRootDir, targetPath);
|
||||
return Buffer.from(this.files[filePath] ?? '', encoding);
|
||||
}
|
||||
|
||||
public writeFile(targetPath: string, sourcePath: string): void;
|
||||
public writeFile(targetPath: string, sourceBuffer: Buffer): void;
|
||||
public writeFile(targetPath: string, source: string | Buffer): void {
|
||||
const filePath = path.join(rootDir, targetPath);
|
||||
const filePath = path.join(storageRootDir, targetPath);
|
||||
if (typeof source === 'string') {
|
||||
this.files[filePath] = fs.readFileSync(source).toString(encoding);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user