techdocs-common: Add tests for google publisher and some more
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
type storageOptions = {
|
||||
projectId?: string;
|
||||
keyFilename?: string;
|
||||
};
|
||||
|
||||
class Bucket {
|
||||
private readonly bucketName;
|
||||
|
||||
constructor(bucketName: string) {
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
|
||||
getMetadata() {
|
||||
return new Promise(resolve => {
|
||||
resolve('');
|
||||
});
|
||||
}
|
||||
|
||||
upload(source: string, { destination }) {
|
||||
return new Promise(resolve => {
|
||||
resolve({ source, destination });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class Storage {
|
||||
private readonly projectId;
|
||||
private readonly keyFilename;
|
||||
|
||||
constructor(options: storageOptions) {
|
||||
this.projectId = options.projectId;
|
||||
this.keyFilename = options.keyFilename;
|
||||
}
|
||||
|
||||
bucket(bucketName) {
|
||||
return new Bucket(bucketName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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';
|
||||
|
||||
const walk = () => {
|
||||
const emitter = new EventEmitter();
|
||||
setTimeout(() => {
|
||||
emitter.emit('end');
|
||||
}, 10);
|
||||
return emitter;
|
||||
};
|
||||
|
||||
export default walk;
|
||||
@@ -54,7 +54,12 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/klaw": "^3.0.1",
|
||||
"@backstage/cli": "^0.4.0"
|
||||
"@backstage/cli": "^0.4.0",
|
||||
"@types/klaw": "^3.0.1"
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
".."
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,108 @@
|
||||
*/
|
||||
|
||||
import { Readable } from 'stream';
|
||||
import { getDocFilesFromRepository } from './helpers';
|
||||
import {
|
||||
getDocFilesFromRepository,
|
||||
getLocationForEntity,
|
||||
parseReferenceAnnotation,
|
||||
} from './helpers';
|
||||
import { UrlReader, ReadTreeResponse } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
const entityBase: Entity = {
|
||||
metadata: {
|
||||
namespace: 'default',
|
||||
name: 'mytestcomponent',
|
||||
description: 'A component for testing',
|
||||
},
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
spec: {
|
||||
type: 'documentation',
|
||||
lifecycle: 'experimental',
|
||||
owner: 'testuser',
|
||||
},
|
||||
};
|
||||
|
||||
const metadataBase = {
|
||||
namespace: 'default',
|
||||
name: 'mytestcomponent',
|
||||
description: 'A component for testing',
|
||||
};
|
||||
|
||||
const goodAnnotation = {
|
||||
annotations: {
|
||||
'backstage.io/techdocs-ref':
|
||||
'url:https://github.com/backstage/backstage/blob/master/subfolder/',
|
||||
},
|
||||
};
|
||||
|
||||
const mockEntityWithAnnotation: Entity = {
|
||||
...entityBase,
|
||||
...{
|
||||
metadata: {
|
||||
...metadataBase,
|
||||
...goodAnnotation,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const badAnnotation = {
|
||||
annotations: {
|
||||
'backstage.io/techdocs-ref': 'bad-annotation',
|
||||
},
|
||||
};
|
||||
|
||||
const mockEntityWithBadAnnotation: Entity = {
|
||||
...entityBase,
|
||||
...{
|
||||
metadata: {
|
||||
...metadataBase,
|
||||
...badAnnotation,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('parseReferenceAnnotation', () => {
|
||||
it('should parse annotation', () => {
|
||||
const parsedLocationAnnotation = parseReferenceAnnotation(
|
||||
'backstage.io/techdocs-ref',
|
||||
mockEntityWithAnnotation,
|
||||
);
|
||||
expect(parsedLocationAnnotation.type).toBe('url');
|
||||
expect(parsedLocationAnnotation.target).toBe(
|
||||
'https://github.com/backstage/backstage/blob/master/subfolder/',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error without annotation', () => {
|
||||
expect(() => {
|
||||
parseReferenceAnnotation('backstage.io/techdocs-ref', entityBase);
|
||||
}).toThrow(/No location annotation/);
|
||||
});
|
||||
|
||||
it('should throw error with bad annotation', () => {
|
||||
expect(() => {
|
||||
parseReferenceAnnotation(
|
||||
'backstage.io/techdocs-ref',
|
||||
mockEntityWithBadAnnotation,
|
||||
);
|
||||
}).toThrow(/Failure to parse/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLocationForEntity', () => {
|
||||
it('should get location for entity', () => {
|
||||
const parsedLocationAnnotation = getLocationForEntity(
|
||||
mockEntityWithAnnotation,
|
||||
);
|
||||
expect(parsedLocationAnnotation.type).toBe('url');
|
||||
expect(parsedLocationAnnotation.target).toBe(
|
||||
'https://github.com/backstage/backstage/blob/master/subfolder/',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDocFilesFromRepository', () => {
|
||||
it('should read a remote directory using UrlReader.readTree', async () => {
|
||||
class MockUrlReader implements UrlReader {
|
||||
@@ -41,28 +139,9 @@ describe('getDocFilesFromRepository', () => {
|
||||
}
|
||||
}
|
||||
|
||||
const mockEntity: Entity = {
|
||||
metadata: {
|
||||
namespace: 'default',
|
||||
annotations: {
|
||||
'backstage.io/techdocs-ref':
|
||||
'url:https://github.com/backstage/backstage/blob/master/subfolder/',
|
||||
},
|
||||
name: 'mytestcomponent',
|
||||
description: 'A component for testing',
|
||||
},
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'Component',
|
||||
spec: {
|
||||
type: 'documentation',
|
||||
lifecycle: 'experimental',
|
||||
owner: 'testuser',
|
||||
},
|
||||
};
|
||||
|
||||
const output = await getDocFilesFromRepository(
|
||||
new MockUrlReader(),
|
||||
mockEntity,
|
||||
mockEntityWithAnnotation,
|
||||
);
|
||||
|
||||
expect(output).toBe('/tmp/testfolder');
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 mockFs from 'mock-fs';
|
||||
import * as winston from 'winston';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { GoogleGCSPublish } from './googleStorage';
|
||||
import { PublisherBase } from './types';
|
||||
|
||||
const createMockEntity = (annotations = {}) => {
|
||||
return {
|
||||
apiVersion: 'version',
|
||||
kind: 'TestKind',
|
||||
metadata: {
|
||||
name: 'test-component-name',
|
||||
annotations: {
|
||||
...annotations,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const logger = winston.createLogger();
|
||||
jest.spyOn(logger, 'info').mockReturnValue(logger);
|
||||
|
||||
let publisher: PublisherBase;
|
||||
|
||||
beforeEach(() => {
|
||||
mockFs({
|
||||
'/path/to/google-application-credentials.json': '{}',
|
||||
});
|
||||
|
||||
const mockConfig = ConfigReader.fromConfigs([
|
||||
{
|
||||
context: '',
|
||||
data: {
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7000',
|
||||
publisher: {
|
||||
type: 'google_gcs',
|
||||
google: {
|
||||
pathToKey: '/path/to/google-application-credentials.json',
|
||||
projectId: 'gcp-project-id',
|
||||
bucketName: 'bucketName',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
publisher = GoogleGCSPublish.fromConfig(mockConfig, logger);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
describe('GoogleGCSPublish', () => {
|
||||
it('should publish a directory', () => {
|
||||
mockFs({
|
||||
'/path/to/generatedDirectory': {
|
||||
'index.html': '',
|
||||
'404.html': '',
|
||||
assets: {
|
||||
'main.css': '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const entity = createMockEntity();
|
||||
return expect(
|
||||
publisher.publish({ entity, directory: '/path/to/generatedDirectory' }),
|
||||
).resolves.toStrictEqual({});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 mockFs from 'mock-fs';
|
||||
import {
|
||||
getVoidLogger,
|
||||
PluginEndpointDiscovery,
|
||||
} from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { Publisher } from './publish';
|
||||
import { LocalPublish } from './local';
|
||||
import { GoogleGCSPublish } from './googleStorage';
|
||||
|
||||
const logger = getVoidLogger();
|
||||
const testDiscovery: jest.Mocked<PluginEndpointDiscovery> = {
|
||||
getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7000'),
|
||||
getExternalBaseUrl: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Publisher', () => {
|
||||
it('should create local publisher by default', () => {
|
||||
const mockConfig = ConfigReader.fromConfigs([
|
||||
{
|
||||
context: '',
|
||||
data: {
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7000',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery);
|
||||
expect(publisher).toBeInstanceOf(LocalPublish);
|
||||
});
|
||||
|
||||
it('should create local publisher from config', () => {
|
||||
const mockConfig = ConfigReader.fromConfigs([
|
||||
{
|
||||
context: '',
|
||||
data: {
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7000',
|
||||
publisher: {
|
||||
type: 'local',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery);
|
||||
expect(publisher).toBeInstanceOf(LocalPublish);
|
||||
});
|
||||
|
||||
it('should create google gcs publisher from config', () => {
|
||||
mockFs({
|
||||
'/path/to/google-application-credentials.json': '{}',
|
||||
});
|
||||
|
||||
const mockConfig = ConfigReader.fromConfigs([
|
||||
{
|
||||
context: '',
|
||||
data: {
|
||||
techdocs: {
|
||||
requestUrl: 'http://localhost:7000',
|
||||
publisher: {
|
||||
type: 'google_gcs',
|
||||
google: {
|
||||
pathToKey: '/path/to/google-application-credentials.json',
|
||||
projectId: 'gcp-project-id',
|
||||
bucketName: 'bucketName',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery);
|
||||
expect(publisher).toBeInstanceOf(GoogleGCSPublish);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user