balena-cli/tests/balena-api-mock.ts
Paulo Castro 2307a15b10 balena ssh: Refactor error handling and test cases
Connects-to: #1896
Change-type: patch
2020-07-02 00:50:32 +01:00

413 lines
9.3 KiB
TypeScript

/**
* @license
* Copyright 2019-2020 Balena Ltd.
*
* 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 * as _ from 'lodash';
import * as path from 'path';
import { NockMock, ScopeOpts } from './nock-mock';
export const apiResponsePath = path.normalize(
path.join(__dirname, 'test-data', 'api-response'),
);
const jHeader = { 'Content-Type': 'application/json' };
export class BalenaAPIMock extends NockMock {
constructor() {
super(/api\.balena-cloud\.com/);
}
public expectGetApplication({
notFound = false,
optional = false,
persist = false,
} = {}) {
const interceptor = this.optGet(/^\/v5\/application($|[(?])/, {
optional,
persist,
});
if (notFound) {
interceptor.reply(200, { d: [] });
} else {
interceptor.replyWithFile(
200,
path.join(apiResponsePath, 'application-GET-v5-expanded-app-type.json'),
jHeader,
);
}
}
public expectDownloadConfig(opts: ScopeOpts = {}) {
this.optPost('/download-config', opts).reply(
200,
JSON.parse(`{
"applicationId":1301645,
"deviceType":"raspberrypi3",
"userId":43699,
"appUpdatePollInterval":600000,
"listenPort":48484,
"vpnPort":443,
"apiEndpoint":"https://api.balena-cloud.com",
"vpnEndpoint":"vpn.balena-cloud.com",
"registryEndpoint":"registry2.balena-cloud.com",
"deltaEndpoint":"https://delta.balena-cloud.com",
"mixpanelToken":"",
"apiKey":"nothingtoseehere"
}`),
);
}
public expectApplicationProvisioning(opts: ScopeOpts = {}) {
this.optPost(/^\/api-key\/application\/[0-9]+\/provisioning$/, opts).reply(
200,
'dummykey',
);
}
public expectGetMyApplication(opts: ScopeOpts = {}) {
this.optGet(/^\/v5\/my_application($|[(?])/, opts).reply(
200,
JSON.parse(`{"d": [{
"user": [{ "username": "bob", "__metadata": {} }],
"id": 1301645,
"__metadata": { "uri": "/resin/my_application(@id)?@id=1301645" }}]}
`),
);
}
public expectGetAuth(opts: ScopeOpts = {}) {
this.optGet(/^\/auth\/v1\//, opts).reply(200, {
token: 'test',
});
}
public expectGetRelease(opts: ScopeOpts = {}) {
this.optGet(/^\/v5\/release($|[(?])/, opts).replyWithFile(
200,
path.join(apiResponsePath, 'release-GET-v5.json'),
jHeader,
);
}
public expectPatchRelease({
replyBody = 'OK',
statusCode = 200,
inspectRequest = this.inspectNoOp,
optional = false,
persist = false,
}) {
this.optPatch(/^\/v5\/release($|[(?])/, { optional, persist }).reply(
statusCode,
this.getInspectedReplyBodyFunction(inspectRequest, replyBody),
);
}
public expectPostRelease(opts: ScopeOpts = {}) {
this.optPost(/^\/v5\/release($|[(?])/, opts).replyWithFile(
200,
path.join(apiResponsePath, 'release-POST-v5.json'),
jHeader,
);
}
public expectPatchImage({
replyBody = 'OK',
statusCode = 200,
inspectRequest = this.inspectNoOp,
optional = false,
persist = false,
}) {
this.optPatch(/^\/v5\/image($|[(?])/, { optional, persist }).reply(
statusCode,
this.getInspectedReplyBodyFunction(inspectRequest, replyBody),
);
}
public expectPostImage(opts: ScopeOpts = {}) {
this.optPost(/^\/v5\/image($|[(?])/, opts).replyWithFile(
201,
path.join(apiResponsePath, 'image-POST-v5.json'),
jHeader,
);
}
public expectPostImageLabel(opts: ScopeOpts = {}) {
this.optPost(/^\/v5\/image_label($|[(?])/, opts).replyWithFile(
201,
path.join(apiResponsePath, 'image-label-POST-v5.json'),
jHeader,
);
}
public expectPostImageIsPartOfRelease(opts: ScopeOpts = {}) {
this.optPost(
/^\/v5\/image__is_part_of__release($|[(?])/,
opts,
).replyWithFile(
200,
path.join(apiResponsePath, 'image-is-part-of-release-POST-v5.json'),
jHeader,
);
}
public expectGetDevice(opts: {
fullUUID: string;
inaccessibleApp?: boolean;
optional?: boolean;
persist?: boolean;
}) {
const id = 7654321;
this.optGet(/^\/v\d+\/device($|\?)/, opts).reply(200, {
d: [
{
id,
uuid: opts.fullUUID,
belongs_to__application: opts.inaccessibleApp
? []
: [{ app_name: 'test' }],
},
],
});
}
public expectGetDeviceStatus(opts: ScopeOpts = {}) {
this.optGet(
/^\/v\d+\/device\?.+&\$select=overall_status$/,
opts,
).replyWithFile(
200,
path.join(apiResponsePath, 'device-status.json'),
jHeader,
);
}
public expectGetAppEnvVars(opts: ScopeOpts = {}) {
this.optGet(/^\/v\d+\/application_environment_variable($|\?)/, opts).reply(
200,
{
d: [
{
id: 120101,
name: 'var1',
value: 'var1-val',
},
{
id: 120102,
name: 'var2',
value: '22',
},
],
},
);
}
public expectGetAppConfigVars(opts: ScopeOpts = {}) {
this.optGet(/^\/v\d+\/application_config_variable($|\?)/, opts).reply(200, {
d: [
{
id: 120300,
name: 'RESIN_SUPERVISOR_NATIVE_LOGGER',
value: 'false',
},
],
});
}
public expectGetAppServiceVars(opts: ScopeOpts = {}) {
this.optGet(/^\/v\d+\/service_environment_variable($|\?)/, opts).reply(
function (uri, _requestBody) {
const match = uri.match(/service_name%20eq%20%27(.+?)%27/);
const serviceName = (match && match[1]) || undefined;
let varArray: any[];
if (serviceName) {
const varObj = appServiceVarsByService[serviceName];
varArray = varObj ? [varObj] : [];
} else {
varArray = _.map(appServiceVarsByService, (value) => value);
}
return [200, { d: varArray }];
},
);
}
public expectGetDeviceEnvVars(opts: ScopeOpts = {}) {
this.optGet(/^\/v\d+\/device_environment_variable($|\?)/, opts).reply(200, {
d: [
{
id: 120203,
name: 'var3',
value: 'var3-val',
},
{
id: 120204,
name: 'var4',
value: '44',
},
],
});
}
public expectGetDeviceConfigVars(opts: ScopeOpts = {}) {
this.optGet(/^\/v\d+\/device_config_variable($|\?)/, opts).reply(200, {
d: [
{
id: 120400,
name: 'RESIN_SUPERVISOR_POLL_INTERVAL',
value: '900900',
},
],
});
}
public expectGetDeviceServiceVars(opts: ScopeOpts = {}) {
this.optGet(
/^\/v\d+\/device_service_environment_variable($|\?)/,
opts,
).reply(function (uri, _requestBody) {
const match = uri.match(/service_name%20eq%20%27(.+?)%27/);
const serviceName = (match && match[1]) || undefined;
let varArray: any[];
if (serviceName) {
const varObj = deviceServiceVarsByService[serviceName];
varArray = varObj ? [varObj] : [];
} else {
varArray = _.map(deviceServiceVarsByService, (value) => value);
}
return [200, { d: varArray }];
});
}
public expectGetDeviceTypes(opts: ScopeOpts = {}) {
this.optGet('/device-types/v1', opts).replyWithFile(
200,
path.join(apiResponsePath, 'device-types-GET-v1.json'),
jHeader,
);
}
public expectGetConfigVars(opts: ScopeOpts = {}) {
this.optGet('/config/vars', opts).reply(200, {
reservedNames: [],
reservedNamespaces: [],
invalidRegex: '/^d|W/',
whiteListedNames: [],
whiteListedNamespaces: [],
blackListedNames: [],
configVarSchema: [],
});
}
public expectGetService(opts: {
optional?: boolean;
persist?: boolean;
serviceId?: number;
serviceName: string;
}) {
const serviceId = opts.serviceId || 243768;
this.optGet(/^\/v\d+\/service($|\?)/, opts).reply(200, {
d: [{ id: serviceId, service_name: opts.serviceName }],
});
}
public expectPostService404(opts: ScopeOpts = {}) {
this.optPost(/^\/v\d+\/service$/, opts).reply(
404,
'Unique key constraint violated',
);
}
public expectGetUser(opts: ScopeOpts = {}) {
this.optGet(/^\/v5\/user/, opts).reply(200, {
d: [
{
id: 99999,
actor: 1234567,
username: 'gh_user',
created_at: '2018-08-19T13:55:04.485Z',
__metadata: {
uri: '/resin/user(@id)?@id=43699',
},
},
],
});
}
// User details are cached in the SDK
// so often we don't know if we can expect the whoami request
public expectGetWhoAmI(opts: ScopeOpts = { optional: true }) {
this.optGet('/user/v1/whoami', opts).reply(200, {
id: 99999,
username: 'gh_user',
email: 'testuser@test.com',
});
}
public expectGetMixpanel(opts: ScopeOpts = {}) {
this.optGet(/^\/mixpanel\/track/, opts).reply(200, {});
}
}
const appServiceVarsByService: { [key: string]: any } = {
service1: {
id: 120110,
name: 'svar1',
value: 'svar1-value',
service: [
{
id: 210110,
service_name: 'service1',
},
],
},
service2: {
id: 120111,
name: 'svar2',
value: 'svar2-value',
service: [
{
id: 210111,
service_name: 'service2',
},
],
},
};
const deviceServiceVarsByService: { [key: string]: any } = {
service1: {
id: 120120,
name: 'svar3',
value: 'svar3-value',
service: [
{
id: 210110,
service_name: 'service1',
},
],
},
service2: {
id: 120121,
name: 'svar4',
value: 'svar4-value',
service: [
{
id: 210111,
service_name: 'service2',
},
],
},
};