From 45755000f301c7a62f18271eca48cbd660b34de6 Mon Sep 17 00:00:00 2001 From: Michel Jonathan Schmitz Date: Wed, 19 Jun 2019 16:13:50 +0200 Subject: [PATCH] style: apply stricter ts lint rules --- src/bulk.ts | 8 +-- src/cli.ts | 61 +++++++++++-------- src/client.ts | 49 ++++++++------- ...connectorClient.ts => connector-client.ts} | 39 +++++++++--- src/copy.ts | 6 +- src/e2e.ts | 5 +- src/errors.ts | 6 +- ...tInterface.ts => http-client-interface.ts} | 7 ++- src/{httpClient.ts => http-client.ts} | 11 +++- test/bulk.spec.ts | 4 +- test/client.spec.ts | 10 +-- ...lient.spec.ts => connector-client.spec.ts} | 20 +++--- test/copy.spec.ts | 2 +- test/e2e.spec.ts | 5 +- ...httpClient.spec.ts => http-client.spec.ts} | 16 +---- 15 files changed, 145 insertions(+), 104 deletions(-) rename src/{connectorClient.ts => connector-client.ts} (87%) rename src/{httpClientInterface.ts => http-client-interface.ts} (93%) rename src/{httpClient.ts => http-client.ts} (89%) rename test/{connectorClient.spec.ts => connector-client.spec.ts} (96%) rename test/{httpClient.spec.ts => http-client.spec.ts} (89%) diff --git a/src/bulk.ts b/src/bulk.ts index 3c46263e..c45a648a 100644 --- a/src/bulk.ts +++ b/src/bulk.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 StApps + * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -46,9 +46,9 @@ export class Bulk { * @see Client.bulk */ constructor( - private type: SCThingType, - private client: Client, - private bulkResponse: SCBulkResponse, + private readonly type: SCThingType, + private readonly client: Client, + private readonly bulkResponse: SCBulkResponse, ) { // noop } diff --git a/src/cli.ts b/src/cli.ts index 5882a6b2..93626964 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 StApps + * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -19,38 +19,43 @@ import {join} from 'path'; import {URL} from 'url'; import {copy} from './copy'; import {indexSamples} from './e2e'; -import {HttpClient} from './httpClient'; +import {HttpClient} from './http-client'; -const logger = new Logger(); -const pkgJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json')).toString()); +const pkgJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json')) + .toString()); const client = new HttpClient(); let actionDone = false; -process.on('unhandledRejection', (error) => { - logger.error('unhandledRejection', error); +process.on('unhandledRejection', async (error) => { + await Logger.error('unhandledRejection', error); }); commander .command('e2e ') .description('Run in end to end test mode. Indexing all test files from @openstapp/core to the backend') .option('-s --samples [path]', 'Path to @openstapp/core test files', './node_modules/@openstapps/core/test/resources') - .action((to, e2eCommand) => { + .action(async (to, e2eCommand) => { + let toURL = ''; // validate url try { - to = (new URL(to)).toString(); + toURL = (new URL(to)).toString(); } catch (err) { - logger.error('expected parameter "to" to be valid url', err); + await Logger.error('expected parameter "to" to be valid url', err); e2eCommand.outputHelp(); process.exit(-1); } actionDone = true; - indexSamples(client, {to: to, samples: e2eCommand.samples}).then(() => { - logger.ok('Done'); - }); + indexSamples(client, {to: toURL, samples: e2eCommand.samples}) + .then(() => { + Logger.ok('Done'); + }) + .catch(async (reason) => { + await Logger.error(reason); + }); }); commander @@ -65,48 +70,52 @@ commander // TODO: remove .option('-a, --appVersion ', 'The App version to use [unset by default]') .allowUnknownOption(false) - .action((type, from, to, batchSize, copyCommand) => { + .action(async (type, from, to, batchSize, copyCommand) => { // validate type if (typeof type !== 'string') { - logger.error('expected parameter "type" to be of type: string'); + await Logger.error('expected parameter "type" to be of type: string'); copyCommand.outputHelp(); process.exit(-1); } + let fromURL = ''; + let toURL = ''; + // validate urls try { - from = (new URL(from)).toString(); - to = (new URL(to)).toString(); + fromURL = (new URL(from)).toString(); + toURL = (new URL(to)).toString(); } catch (err) { - logger.error('expected parameters "from" and "to" to be valid urls', err); + await Logger.error('expected parameters "from" and "to" to be valid urls', err); copyCommand.outputHelp(); process.exit(-1); } // validate batchSize if (isNaN(parseInt(batchSize, 10))) { - logger.error('expected parameter "batchSize" to be of type: number'); + await Logger.error('expected parameter "batchSize" to be of type: number'); copyCommand.outputHelp(); process.exit(-1); } actionDone = true; - logger.info('Copying ' + type + ' objects from ' + from + ' to ' + to); + Logger.info(`Copying ${type} objects from ${fromURL} to ${toURL}`); copy(client, { batchSize: parseInt(batchSize, 10), - from: from, + from: fromURL, source: copyCommand.bulkSource, - to: to, + to: toURL, type: type, version: copyCommand.appVersion, - }).then(() => { - logger.ok('Done'); - }, (err) => { - throw err; - }); + }) + .then(() => { + Logger.ok('Done'); + }, (err) => { + throw err; + }); }); commander diff --git a/src/client.ts b/src/client.ts index 479c2fd2..bfd86a85 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 StApps + * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -14,6 +14,7 @@ */ import { SCAbstractRoute, + SCErrorResponse, SCFeedbackRequest, SCFeedbackResponse, SCFeedbackRoute, @@ -32,7 +33,7 @@ import { SCThings, } from '@openstapps/core'; import {ApiError, CoreVersionIncompatibleError, OutOfRangeError} from './errors'; -import {HttpClientHeaders, HttpClientInterface} from './httpClientInterface'; +import {HttpClientHeaders, HttpClientInterface} from './http-client-interface'; /** * StApps-API client @@ -115,7 +116,7 @@ export class Client { * @param feedback Feedback to send */ async feedback(feedback: SCFeedbackRequest): Promise { - return await this.invokeRoute(this.feedbackRoute, undefined, feedback); + return this.invokeRoute(this.feedbackRoute, undefined, feedback); } /** @@ -167,7 +168,7 @@ export class Client { * @param body Body for the request */ async invokeRoute(route: SCAbstractRoute, - parameters?: { [k: string]: string }, + parameters?: { [k: string]: string; }, body?: SCRequests): Promise { // make the request const response = await this.httpClient.request({ @@ -182,7 +183,7 @@ export class Client { return response.body as T; } - throw new ApiError(response.body); + throw new ApiError(response.body as SCErrorResponse); } /** @@ -197,21 +198,24 @@ export class Client { let preFlightNecessary = false; // gather search requests where size is not set - Object.keys(multiSearchRequest).forEach((key) => { - const searchRequest = multiSearchRequest[key]; + Object.keys(multiSearchRequest) + .forEach((key) => { + const searchRequest = multiSearchRequest[key]; - if (typeof searchRequest.size === 'undefined') { - preFlightRequest[key] = { - ...searchRequest, - }; - preFlightRequest[key].size = 0; - preFlightNecessary = true; - } - }); + if (typeof searchRequest.size === 'undefined') { + preFlightRequest[key] = { + ...searchRequest, + }; + preFlightRequest[key].size = 0; + preFlightNecessary = true; + } + }); + + let returnMultiSearchRequest = multiSearchRequest; if (preFlightNecessary) { // copy multi search request - multiSearchRequest = { + returnMultiSearchRequest = { ...multiSearchRequest, }; @@ -223,13 +227,14 @@ export class Client { ); // set size for multi search requests that were in pre flight request - Object.keys(preFlightRequest).forEach((key) => { - multiSearchRequest[key].size = preFlightResponse[key].pagination.total; - }); + Object.keys(preFlightRequest) + .forEach((key) => { + returnMultiSearchRequest[key].size = preFlightResponse[key].pagination.total; + }); } // actually invoke the route - return await this.invokeRoute(this.multiSearchRoute, undefined, multiSearchRequest); + return this.invokeRoute(this.multiSearchRoute, undefined, returnMultiSearchRequest); } /** @@ -251,7 +256,7 @@ export class Client { size = preFlightResponse.pagination.total; } - return await this.invokeRoute(this.searchRoute, undefined, { + return this.invokeRoute(this.searchRoute, undefined, { ...searchRequest, size, }); @@ -264,8 +269,10 @@ export class Client { * @param searchResponse Search response for supplied search request */ async searchNext(searchRequest: SCSearchRequest, searchResponse: SCSearchResponse): Promise<{ + /* tslint:disable:completed-docs */ searchRequest: SCSearchRequest; searchResponse: SCSearchResponse; + /* tslint:enable:completed-docs */ }> { const nextSearchRequest = Client.nextWindow(searchRequest, searchResponse); diff --git a/src/connectorClient.ts b/src/connector-client.ts similarity index 87% rename from src/connectorClient.ts rename to src/connector-client.ts index 83e33b62..871d5ef9 100644 --- a/src/connectorClient.ts +++ b/src/connector-client.ts @@ -30,19 +30,32 @@ import {Bulk} from './bulk'; import {Client} from './client'; import {EmptyBulkError, NamespaceNotDefinedError} from './errors'; -/* tslint:disable:no-var-requires */ +const V5_VERSION = 0x50; +/* tslint:disable:no-var-requires */ /** * The package @types/uuid unfortunately doesn't expose the browser versions of the hashing functions. * That's why we need to use a little trickery to get to it. */ const v35 = require('uuid/lib/v35'); const sha1Browser = require('uuid/lib/sha1-browser'); -const v5 = v35('v5', 0x50, sha1Browser); +const v5 = v35('v5', V5_VERSION, sha1Browser); -/* tslint:enable */ +/* tslint:enable:no-var-requires */ +/** + * StApps-API client + */ export class ConnectorClient extends Client { + /** + * The default timeout for the bulk to expire + */ + static readonly BULK_TIMEOUT = 3600; + /** + * The limit of how many items should be indexed concurrently + */ + static readonly ITEM_CONCURRENT_LIMIT = 5; + /** * Instance of multi search request route */ @@ -78,10 +91,12 @@ export class ConnectorClient extends Client { * @param thing Thing to remove references from */ static removeReferences(thing: THING): SCAssociatedThingWithoutReferences { + /* tslint:disable:no-any */ const thingWithoutReferences: any = { ...{}, ...thing, }; + /* tslint:enable:no-any */ // iterate over all properties for (const key in thingWithoutReferences) { @@ -151,13 +166,18 @@ export class ConnectorClient extends Client { * @param timeout Timeout in seconds when the bulk should expire */ async bulk(type: SCThingType, source: string, timeout?: number): Promise> { + let bulkTimeout: number; // set default value for timeout to one hour if (typeof timeout !== 'number') { - timeout = 3600; + bulkTimeout = ConnectorClient.BULK_TIMEOUT; + } else { + bulkTimeout = timeout; } const bulkData = await this.invokeRoute(this.bulkRoute, undefined, { - expiration: moment().add(timeout, 'seconds').format(), + expiration: moment() + .add(bulkTimeout, 'seconds') + .format(), source: source, type: type, }); @@ -182,16 +202,19 @@ export class ConnectorClient extends Client { throw new EmptyBulkError(); } + let thingSource: string; // set default source if none is given if (typeof source === 'undefined') { - source = 'stapps-api'; + thingSource = 'stapps-api'; + } else { + thingSource = source; } // request a new bulk - const bulk = await this.bulk(things[0].type, source, timeout); + const bulk = await this.bulk(things[0].type, thingSource, timeout); // add items to the bulk - 5 concurrently - await asyncPool(5, things, (thing) => bulk.add(thing)); + await asyncPool(ConnectorClient.ITEM_CONCURRENT_LIMIT, things, (thing) => bulk.add(thing)); // close bulk await bulk.done(); diff --git a/src/copy.ts b/src/copy.ts index 6b5a7813..6a1f41d5 100644 --- a/src/copy.ts +++ b/src/copy.ts @@ -16,9 +16,9 @@ import {asyncPool} from '@krlwlfrt/async-pool'; import {SCSearchRequest, SCThingType} from '@openstapps/core'; import {Bar} from 'cli-progress'; import {Client} from './client'; -import {ConnectorClient} from './connectorClient'; +import {ConnectorClient} from './connector-client'; import {OutOfRangeError} from './errors'; -import {HttpClientInterface} from './httpClientInterface'; +import {HttpClientInterface} from './http-client-interface'; /** * Options to set up copying data from one backend to another @@ -93,7 +93,7 @@ export async function copy(client: HttpClientInterface, options: CopyOptions): P try { ({searchRequest, searchResponse} = await apiIn.searchNext(searchRequest, searchResponse)); - await asyncPool(5, searchResponse.data, (item) => { + await asyncPool(ConnectorClient.ITEM_CONCURRENT_LIMIT, searchResponse.data, async (item) => { progressBar.increment(1); return bulk.add(item); diff --git a/src/e2e.ts b/src/e2e.ts index ad3f79c5..699b21e9 100644 --- a/src/e2e.ts +++ b/src/e2e.ts @@ -17,8 +17,8 @@ import {SCThings, SCThingType} from '@openstapps/core'; import {readdir, readFile} from 'fs'; import {join} from 'path'; import {promisify} from 'util'; -import {ConnectorClient} from './connectorClient'; -import {HttpClientInterface} from './httpClientInterface'; +import {ConnectorClient} from './connector-client'; +import {HttpClientInterface} from './http-client-interface'; /** * Options to set up indexing core test files to backend @@ -100,5 +100,6 @@ export async function getItemsFromSamples(samplesDirectory: } catch (error) { return error; } + return things; } diff --git a/src/errors.ts b/src/errors.ts index 0a7c99c0..ca97f952 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 StApps + * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -39,12 +39,12 @@ export class ApiError extends Error { // add additional data if (typeof this.data.additionalData !== 'undefined') { - str += '\n\n' + JSON.stringify(this.data.additionalData); + str += `\n\n${JSON.stringify(this.data.additionalData)}`; } // add "remote" stack trace if (typeof this.data.stack !== 'undefined') { - str += '\n\n' + this.data.stack; + str += `\n\n${this.data.stack}`; } return str; diff --git a/src/httpClientInterface.ts b/src/http-client-interface.ts similarity index 93% rename from src/httpClientInterface.ts rename to src/http-client-interface.ts index 05d730df..2f98711d 100644 --- a/src/httpClientInterface.ts +++ b/src/http-client-interface.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 StApps + * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -32,7 +32,9 @@ export interface HttpClientInterface { * A map of headers */ export interface HttpClientHeaders { + /* tslint:disable:no-any */ [key: string]: any; + /* tslint:enable:no-any */ } /** @@ -72,6 +74,9 @@ export interface HttpClientRequest { * A HTTP client response */ export interface HttpClientResponse { + /** + * Body of the response + */ body: T | SCErrorResponse; /** diff --git a/src/httpClient.ts b/src/http-client.ts similarity index 89% rename from src/httpClient.ts rename to src/http-client.ts index 2eff3076..076d8f0f 100644 --- a/src/httpClient.ts +++ b/src/http-client.ts @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 StApps + * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. @@ -18,6 +18,9 @@ import * as request from 'request'; * Request options that requires a url */ export interface RequestOptions extends request.CoreOptions { + /** + * Target URL of the request + */ url: URL; } @@ -25,6 +28,9 @@ export interface RequestOptions extends request.CoreOptions { * Response with generic for the type of body that is returned from the request */ export interface Response extends request.Response { + /** + * Typed body of the response + */ body: TYPE_OF_BODY; } @@ -36,7 +42,8 @@ export class HttpClient { * Make a request * @param requestConfig Configuration of the request */ - request( + // tslint:disable-next-line:prefer-function-over-method + async request( requestConfig: RequestOptions, ): Promise> { const params: request.CoreOptions = { diff --git a/test/bulk.spec.ts b/test/bulk.spec.ts index 0bca9222..5728cf2f 100644 --- a/test/bulk.spec.ts +++ b/test/bulk.spec.ts @@ -22,7 +22,7 @@ import * as moment from 'moment'; import {Bulk} from '../src/bulk'; import {Client} from '../src/client'; import {BulkWithMultipleTypesError} from '../src/errors'; -import {HttpClient} from '../src/httpClient'; +import {HttpClient} from '../src/http-client'; chai.should(); chai.use(chaiSpies); @@ -89,7 +89,7 @@ export class BulkSpec { audiences: [ 'students', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foobar', origin: { indexed: moment().format(), diff --git a/test/client.spec.ts b/test/client.spec.ts index 53569b5f..5280d85f 100644 --- a/test/client.spec.ts +++ b/test/client.spec.ts @@ -34,8 +34,8 @@ import * as chaiSpies from 'chai-spies'; import {suite, test} from 'mocha-typescript'; import {Client} from '../src/client'; import {ApiError, OutOfRangeError} from '../src/errors'; -import {HttpClient} from '../src/httpClient'; -import {HttpClientResponse} from '../src/httpClientInterface'; +import {HttpClient} from '../src/http-client'; +import {HttpClientResponse} from '../src/http-client-interface'; chai.should(); chai.use(chaiSpies); @@ -133,7 +133,7 @@ export class ClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', metaData: { debug: true, platform: 'android', @@ -168,7 +168,7 @@ export class ClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foo', origin: { indexed: 'foo', @@ -254,7 +254,7 @@ export class ClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foo', origin: { indexed: 'foo', diff --git a/test/connectorClient.spec.ts b/test/connector-client.spec.ts similarity index 96% rename from test/connectorClient.spec.ts rename to test/connector-client.spec.ts index 57dfbb4a..4565e5bc 100644 --- a/test/connectorClient.spec.ts +++ b/test/connector-client.spec.ts @@ -22,11 +22,11 @@ import { SCBulkResponse, SCBulkRoute, SCMessage, - SCThing, SCThingOriginType, SCThingType, SCThingUpdateResponse, SCThingUpdateRoute, + SCThingWithoutReferences, } from '@openstapps/core'; import * as chai from 'chai'; import {expect} from 'chai'; @@ -38,10 +38,10 @@ import * as moment from 'moment'; import {join, resolve} from 'path'; import * as traverse from 'traverse'; import {promisify} from 'util'; -import {ConnectorClient} from '../src/connectorClient'; +import {ConnectorClient} from '../src/connector-client'; import {EmptyBulkError, NamespaceNotDefinedError} from '../src/errors'; -import {HttpClient} from '../src/httpClient'; -import {HttpClientRequest, HttpClientResponse} from '../src/httpClientInterface'; +import {HttpClient} from '../src/http-client'; +import {HttpClientRequest, HttpClientResponse} from '../src/http-client-interface'; chai.should(); chai.use(chaiSpies); @@ -64,7 +64,7 @@ const httpClient = new HttpClient(); * * @param thing Thing to check */ -function doesContainThings(thing: T): boolean { +function doesContainThings(thing: T): boolean { /* tslint:disable-next-line:only-arrow-functions */ return traverse(thing).reduce(function(sum, item) { if (this.isRoot) { @@ -154,7 +154,7 @@ export class ConnectorClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foo', origin: { indexed: 'foo', @@ -168,7 +168,7 @@ export class ConnectorClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foo', origin: { indexed: 'foo', @@ -241,7 +241,7 @@ export class ConnectorClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foo', origin: { indexed: 'foo', @@ -255,7 +255,7 @@ export class ConnectorClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foo', origin: { indexed: 'foo', @@ -367,7 +367,7 @@ export class ConnectorClientSpec { audiences: [ 'employees', ], - message: 'Lorem ipsum.', + messageBody: 'Lorem ipsum.', name: 'foo', origin: { indexed: 'foo', diff --git a/test/copy.spec.ts b/test/copy.spec.ts index bd2b6130..28db3407 100644 --- a/test/copy.spec.ts +++ b/test/copy.spec.ts @@ -31,7 +31,7 @@ import {suite, test} from 'mocha-typescript'; import * as moment from 'moment'; import {copy} from '../src/copy'; import {ApiError} from '../src/errors'; -import {HttpClient, RequestOptions, Response} from '../src/httpClient'; +import {HttpClient, RequestOptions, Response} from '../src/http-client'; import {RecursivePartial} from './client.spec'; chai.should(); diff --git a/test/e2e.spec.ts b/test/e2e.spec.ts index 0dbe997d..e3aa22a1 100644 --- a/test/e2e.spec.ts +++ b/test/e2e.spec.ts @@ -24,13 +24,14 @@ import { import * as chai from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import * as chaiSpies from 'chai-spies'; -import {existsSync, mkdirSync} from 'fs'; +import {existsSync, mkdirSync, rmdirSync, unlinkSync} from 'fs'; import {suite, test} from 'mocha-typescript'; import {join} from 'path'; import {getItemsFromSamples, indexSamples} from '../src/e2e'; import {ApiError} from '../src/errors'; -import {HttpClient, RequestOptions, Response} from '../src/httpClient'; +import {HttpClient, RequestOptions, Response} from '../src/http-client'; import {RecursivePartial} from './client.spec'; +import {createFileSync} from 'fs-extra'; chai.should(); chai.use(chaiSpies); diff --git a/test/httpClient.spec.ts b/test/http-client.spec.ts similarity index 89% rename from test/httpClient.spec.ts rename to test/http-client.spec.ts index 26fd6e4a..85a2c8c9 100644 --- a/test/httpClient.spec.ts +++ b/test/http-client.spec.ts @@ -12,25 +12,13 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -import * as chai from 'chai'; import {expect} from 'chai'; -import * as chaiAsPromised from 'chai-as-promised'; -import * as chaiSpies from 'chai-spies'; import {suite, test} from 'mocha-typescript'; import * as nock from 'nock'; -import {HttpClient} from '../src/httpClient'; - -chai.should(); -chai.use(chaiSpies); -chai.use(chaiAsPromised); - -const sandbox = chai.spy.sandbox(); +import {HttpClient} from '../src/http-client'; @suite() -export class ConnectorClientSpec { - async after() { - sandbox.restore(); - } +export class HttpClientSpec { @test async construct() {