Skip to content

Commit

Permalink
pull common tests into registryClientHelper, sync up fhir and npm reg…
Browse files Browse the repository at this point in the history
…istry client tests, add in versioning check to npm client, cleanup
  • Loading branch information
KaelynJefferson committed Jul 23, 2024
1 parent 47f0078 commit c5a322e
Show file tree
Hide file tree
Showing 5 changed files with 607 additions and 294 deletions.
46 changes: 4 additions & 42 deletions src/registry/FHIRRegistryClient.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Readable } from 'stream';
import { maxSatisfying } from 'semver';
import { LogFunction, axiosGet } from '../utils';
import { RegistryClient, RegistryClientOptions } from './RegistryClient';
import { IncorrectWildcardVersionFormatError, LatestVersionUnavailableError } from '../errors';
import { IncorrectWildcardVersionFormatError } from '../errors';
import { lookUpLatestVersion, lookUpLatestPatchVersion } from './registryClientHelper';

export class FHIRRegistryClient implements RegistryClient {
public endpoint: string;
Expand All @@ -17,9 +17,9 @@ export class FHIRRegistryClient implements RegistryClient {
async download(name: string, version: string): Promise<Readable> {
// Resolve version if necessary
if (version === 'latest') {
version = await this.lookUpLatestVersion(name);
version = await lookUpLatestVersion(this.endpoint, name);
} else if (/^\d+\.\d+\.x$/.test(version)) {
version = await this.lookUpLatestPatchVersion(name, version);
version = await lookUpLatestPatchVersion(this.endpoint, name, version);
} else if (/^\d+\.x$/.test(version)) {
throw new IncorrectWildcardVersionFormatError(name, version);
}
Expand All @@ -34,42 +34,4 @@ export class FHIRRegistryClient implements RegistryClient {
}
throw new Error(`Failed to download ${name}#${version} from ${url}`);
}

private async lookUpLatestVersion(name: string): Promise<string> {
try {
const res = await axiosGet(`${this.endpoint}/${name}`, {
responseType: 'json'
});
if (res?.data?.['dist-tags']?.latest?.length) {
return res.data['dist-tags'].latest;
} else {
throw new LatestVersionUnavailableError(name);
}
} catch {
throw new LatestVersionUnavailableError(name);
}
}

private async lookUpLatestPatchVersion(name: string, version: string): Promise<string> {
if (!/^\d+\.\d+\.x$/.test(version)) {
throw new IncorrectWildcardVersionFormatError(name, version);
}
try {
const res = await axiosGet(`${this.endpoint}/${name}`, {
responseType: 'json'
});
if (res?.data?.versions) {
const versions = Object.keys(res.data.versions);
const latest = maxSatisfying(versions, version);
if (latest == null) {
throw new LatestVersionUnavailableError(name, null, true);
}
return latest;
} else {
throw new LatestVersionUnavailableError(name, null, true);
}
} catch {
throw new LatestVersionUnavailableError(name, null, true);
}
}
}
11 changes: 11 additions & 0 deletions src/registry/NPMRegistryClient.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Readable } from 'stream';
import { LogFunction, axiosGet } from '../utils';
import { RegistryClient, RegistryClientOptions } from './RegistryClient';
import { IncorrectWildcardVersionFormatError } from '../errors';
import { lookUpLatestVersion, lookUpLatestPatchVersion } from './registryClientHelper';

export class NPMRegistryClient implements RegistryClient {
public endpoint: string;
Expand All @@ -13,6 +15,15 @@ export class NPMRegistryClient implements RegistryClient {
}

async download(name: string, version: string): Promise<Readable> {
// Resolve version if necessary
if (version === 'latest') {
version = await lookUpLatestVersion(this.endpoint, name);
} else if (/^\d+\.\d+\.x$/.test(version)) {
version = await lookUpLatestPatchVersion(this.endpoint, name, version);
} else if (/^\d+\.x$/.test(version)) {
throw new IncorrectWildcardVersionFormatError(name, version);
}

// Get the manifest information about the package from the registry
let url;
try {
Expand Down
45 changes: 45 additions & 0 deletions src/registry/registryClientHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { IncorrectWildcardVersionFormatError, LatestVersionUnavailableError } from '../errors';
import { axiosGet } from '../utils';
import { maxSatisfying } from 'semver';

export async function lookUpLatestVersion(endpoint: string, name: string): Promise<string> {
try {
const res = await axiosGet(`${endpoint}/${name}`, {
responseType: 'json'
});
if (res?.data?.['dist-tags']?.latest?.length) {
return res.data['dist-tags'].latest;
} else {
throw new LatestVersionUnavailableError(name);
}
} catch {
throw new LatestVersionUnavailableError(name);
}
}

export async function lookUpLatestPatchVersion(
endpoint: string,
name: string,
version: string
): Promise<string> {
if (!/^\d+\.\d+\.x$/.test(version)) {
throw new IncorrectWildcardVersionFormatError(name, version);
}
try {
const res = await axiosGet(`${endpoint}/${name}`, {
responseType: 'json'
});
if (res?.data?.versions) {
const versions = Object.keys(res.data.versions);
const latest = maxSatisfying(versions, version);
if (latest == null) {
throw new LatestVersionUnavailableError(name, null, true);
}
return latest;
} else {
throw new LatestVersionUnavailableError(name, null, true);
}
} catch {
throw new LatestVersionUnavailableError(name, null, true);
}
}
170 changes: 124 additions & 46 deletions test/registry/FHIRRegistryClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,6 @@ const TERM_PKG_RESPONSE = {
fhirVersion: 'R4',
url: 'https://packages.simplifier.net/hl7.terminology.r4/1.2.3-test'
},
'1.1.0': {
name: 'hl7.terminology.r4',
version: '1.1.0',
description: 'None.',
dist: {
shasum: '1a1467bce19aace45771e0a51ef2ad9c3fe749820',
tarball: 'https://packages.simplifier.net/hl7.terminology.r4/1.1.0'
},
fhirVersion: 'R4',
url: 'https://packages.simplifier.net/hl7.terminology.r4/1.1.0'
},
'1.1.2': {
name: 'hl7.terminology.r4',
version: '1.1.2',
Expand Down Expand Up @@ -82,12 +71,12 @@ describe('FHIRRegistryClient', () => {
axiosSpy = jest.spyOn(axios, 'get').mockImplementation((uri: string): any => {
if (uri === 'https://packages.fhir.org/hl7.terminology.r4') {
return { data: TERM_PKG_RESPONSE };
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/7.7.7') {
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/1.1.2') {
return {
status: 404,
data: Readable.from(['3.3.3-test-data'])
data: Readable.from(['1.1.2-test-data'])
};
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/7.7.9') {
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/1.1.1') {
return {
status: 200,
data: null
Expand All @@ -103,60 +92,149 @@ describe('FHIRRegistryClient', () => {
});

it('should throw error when trying to get the version of a package on the packages server but status is not 200', async () => {
const latest = client.download('hl7.terminology.r4', '7.7.7');
const latest = client.download('hl7.terminology.r4', '1.1.2');
expect(loggerSpy.getLastMessage('info')).toBe(
'Attempting to download hl7.terminology.r4#7.7.7 from https://packages.fhir.org/hl7.terminology.r4/7.7.7'
'Attempting to download hl7.terminology.r4#1.1.2 from https://packages.fhir.org/hl7.terminology.r4/1.1.2'
);
await expect(latest).rejects.toThrow(Error);
await expect(latest).rejects.toThrow(
'Failed to download hl7.terminology.r4#7.7.7 from https://packages.fhir.org/hl7.terminology.r4/7.7.7'
'Failed to download hl7.terminology.r4#1.1.2 from https://packages.fhir.org/hl7.terminology.r4/1.1.2'
);
});

it('should throw error when trying to get the version of a package on the packages server but returns no data', async () => {
// Note: don't know of a scenario where this would occur but testing for completeness.
const latest = client.download('hl7.terminology.r4', '7.7.9');
const latest = client.download('hl7.terminology.r4', '1.1.1');
expect(loggerSpy.getLastMessage('info')).toBe(
'Attempting to download hl7.terminology.r4#7.7.9 from https://packages.fhir.org/hl7.terminology.r4/7.7.9'
'Attempting to download hl7.terminology.r4#1.1.1 from https://packages.fhir.org/hl7.terminology.r4/1.1.1'
);
await expect(latest).rejects.toThrow(Error);
await expect(latest).rejects.toThrow(
'Failed to download hl7.terminology.r4#7.7.9 from https://packages.fhir.org/hl7.terminology.r4/7.7.9'
'Failed to download hl7.terminology.r4#1.1.1 from https://packages.fhir.org/hl7.terminology.r4/1.1.1'
);
});
});

describe('#downloadSpecificVersion', () => {
beforeEach(() => {
loggerSpy.reset();
});
beforeAll(() => {
axiosSpy = jest.spyOn(axios, 'get').mockImplementation((uri: string): any => {
if (uri === 'https://packages.fhir.org/hl7.terminology.r4') {
return { data: TERM_PKG_RESPONSE };
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/3.3.3') {
return {
status: 200,
data: Readable.from(['3.3.3-test-data'])
};
} else {
throw new Error('Not found');
}
describe('#CreateURL', () => {
beforeEach(() => {
loggerSpy.reset();
});
beforeAll(() => {
axiosSpy = jest.spyOn(axios, 'get').mockImplementation((uri: string): any => {
if (uri === 'https://packages.fhir.org/hl7.terminology.r4') {
return { data: TERM_PKG_RESPONSE };
} else {
throw new Error('Not found');
}
});
});
});

afterAll(() => {
axiosSpy.mockRestore();
afterAll(() => {
axiosSpy.mockRestore();
});

it('should throw error if no name given for download method', async () => {
const latest = client.download('', '5.5.5');
// no message logged
await expect(latest).rejects.toThrow(Error);
await expect(latest).rejects.toThrow('Not found');
});

it('should throw error if no version given for download method', async () => {
const latest = client.download('hl7.terminology.r4', '');
// no message logged
await expect(latest).rejects.toThrow(Error);
await expect(latest).rejects.toThrow('Not found');
});

it('should throw error if no endpoint given for download method', async () => {
const emptyClient = new FHIRRegistryClient('', { log: loggerSpy.log });
const latest = emptyClient.download('hl7.terminology.r4', '1.2.3-test');
// no message logged
await expect(latest).rejects.toThrow(Error);
await expect(latest).rejects.toThrow('Not found');
});
});

it('should get the specific version of a package on the packages server', async () => {
const latest = await client.download('hl7.terminology.r4', '3.3.3');
expect(loggerSpy.getLastMessage('info')).toBe(
'Attempting to download hl7.terminology.r4#3.3.3 from https://packages.fhir.org/hl7.terminology.r4/3.3.3'
);
expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
expect(latest).toBeInstanceOf(Readable);
expect(latest.read()).toBe('3.3.3-test-data');
describe('#DownloadFromURL', () => {
beforeEach(() => {
loggerSpy.reset();
});
beforeAll(() => {
axiosSpy = jest.spyOn(axios, 'get').mockImplementation((uri: string): any => {
if (uri === 'https://packages.fhir.org/hl7.terminology.r4') {
return { data: TERM_PKG_RESPONSE };
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/1.2.3-test') {
return {
status: 200,
data: Readable.from(['1.2.3-test-data'])
};
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/2.2.2') {
return {
status: 200,
data: ''
};
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/3.3.3') {
return {
status: 200
};
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/5.5.5') {
return {
status: 'wrong-type',
data: Readable.from(['1.2.4-no-manifest-test-data'])
};
} else if (uri === 'https://packages.fhir.org/hl7.terminology.r4/5.5.6-test') {
return {
data: Readable.from(['5.5.6-test-data'])
};
} else {
throw new Error('Not found');
}
});
});

afterAll(() => {
axiosSpy.mockRestore();
});

it('should get the data of the package when 200 response', async () => {
const latest = await client.download('hl7.terminology.r4', '1.2.3-test');
expect(loggerSpy.getLastMessage('info')).toBe(
'Attempting to download hl7.terminology.r4#1.2.3-test from https://packages.fhir.org/hl7.terminology.r4/1.2.3-test'
);
expect(loggerSpy.getAllMessages('error')).toHaveLength(0);
expect(latest).toBeInstanceOf(Readable);
expect(latest.read()).toBe('1.2.3-test-data');
});

it('should throw error when trying to get the version of a package on the server but returns 200 status and data of incorrect type', async () => {
const latest = client.download('hl7.terminology.r4', '2.2.2');
await expect(latest).rejects.toThrow(
'Failed to download hl7.terminology.r4#2.2.2 from https://packages.fhir.org/hl7.terminology.r4/2.2.2'
);
});

it('should throw error when trying to get the version of a package on the server but returns 200 status and no data field', async () => {
const latest = client.download('hl7.terminology.r4', '3.3.3');
await expect(latest).rejects.toThrow(
'Failed to download hl7.terminology.r4#3.3.3 from https://packages.fhir.org/hl7.terminology.r4/3.3.3'
);
});

it('should throw error when trying to get the version of a package on the server but returns status with incorrect type', async () => {
const latest = client.download('hl7.terminology.r4', '5.5.5');
await expect(latest).rejects.toThrow(
'Failed to download hl7.terminology.r4#5.5.5 from https://packages.fhir.org/hl7.terminology.r4/5.5.5'
);
});

it('should throw error when trying to get the version of a package on the server but returns no status', async () => {
const latest = client.download('hl7.terminology.r4', '5.5.6-test');
await expect(latest).rejects.toThrow(
'Failed to download hl7.terminology.r4#5.5.6-test from https://packages.fhir.org/hl7.terminology.r4/5.5.6-test'
);
});
});
});

Expand Down
Loading

0 comments on commit c5a322e

Please sign in to comment.