Skip to content
This repository was archived by the owner on Apr 13, 2020. It is now read-only.

Commit b5f7124

Browse files
authored
[REFACTORING] Refactor infra validate ts (#237)
* [REFACTOR] infra validate.ts * corrected incorrect inclusion of decorator json
1 parent 27d927c commit b5f7124

File tree

6 files changed

+121
-64
lines changed

6 files changed

+121
-64
lines changed

src/commands/infra/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Command } from "../command";
22
import { commandDecorator as generateCommandDecorator } from "./generate";
33
import { commandDecorator as scaffoldCommandDecorator } from "./scaffold";
4-
import { validateCommandDecorator } from "./validate";
4+
import { commandDecorator as validateCommandDecorator } from "./validate";
55

66
export const infraCommand = Command(
77
"infra",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"command": "validate",
3+
"alias": "v",
4+
"description": "Validate will verify that all infrastructure deployment prerequisites have been correctly installed.",
5+
"options": []
6+
}

src/commands/infra/validate.test.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import child_process from "child_process";
22
import fs from "fs";
33
import yaml from "js-yaml";
4-
import os from "os";
54
import * as path from "path";
65
import { promisify } from "util";
76
import { Config, defaultConfigFile, loadConfiguration } from "../../config";
@@ -11,10 +10,12 @@ import {
1110
logger
1211
} from "../../logger";
1312
import {
13+
execute,
1414
validateAzure,
1515
validateEnvVariables,
1616
validatePrereqs
1717
} from "./validate";
18+
import * as validate from "./validate";
1819

1920
beforeAll(() => {
2021
enableVerboseLogging();
@@ -26,6 +27,39 @@ afterAll(() => {
2627
jest.setTimeout(5000);
2728
});
2829

30+
beforeEach(() => {
31+
jest.clearAllMocks();
32+
jest.restoreAllMocks();
33+
});
34+
35+
describe("test execute function", () => {
36+
it("positive tests", async () => {
37+
const exitFn = jest.fn();
38+
const validatePrereqsMock = jest.spyOn(validate, "validatePrereqs");
39+
validatePrereqsMock.mockImplementation(() => true);
40+
const validateAzureMock = jest.spyOn(validate, "validateAzure");
41+
validateAzureMock.mockImplementation(() => Promise.resolve(true));
42+
const validateEnvVariablesMock = jest.spyOn(
43+
validate,
44+
"validateEnvVariables"
45+
);
46+
validateEnvVariablesMock.mockImplementation(() => true);
47+
await execute(exitFn);
48+
expect(exitFn).toBeCalledTimes(1);
49+
expect(exitFn.mock.calls).toEqual([[0]]);
50+
});
51+
it("negative tests", async () => {
52+
const exitFn = jest.fn();
53+
const validatePrereqsMock = jest.spyOn(validate, "validatePrereqs");
54+
validatePrereqsMock.mockImplementation(() => {
55+
throw new Error("dummy");
56+
});
57+
await execute(exitFn);
58+
expect(exitFn).toBeCalledTimes(1);
59+
expect(exitFn.mock.calls).toEqual([[1]]);
60+
});
61+
});
62+
2963
describe("Validating executable prerequisites", () => {
3064
test("Validate that array of executables do not exists in PATH", async () => {
3165
// Iterate through an array of non-existent binaries to create a force fail. If fails, then test pass
@@ -39,7 +73,7 @@ describe("Validating executable prerequisites", () => {
3973
fs.writeFileSync(defaultConfigFile(), yaml.safeDump(data));
4074

4175
const fakeBinaries: string[] = ["ydawgie"];
42-
const value = await validatePrereqs(fakeBinaries, false);
76+
const value = validatePrereqs(fakeBinaries, false);
4377
expect(value).toBe(false);
4478
});
4579
});
@@ -53,7 +87,8 @@ describe("Validating executable prerequisites in spk-config", () => {
5387
process.env.test_key = "my_storage_key";
5488
loadConfiguration(filename);
5589
const fakeBinaries: string[] = ["ydawgie"];
56-
await validatePrereqs(fakeBinaries, true);
90+
validatePrereqs(fakeBinaries, true);
91+
5792
expect(Config().infra!).toBeDefined();
5893
expect(Config().infra!.checks!).toBeDefined();
5994
expect(Config().infra!.checks!.ydawgie!).toBe(false);

src/commands/infra/validate.ts

Lines changed: 43 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import emoji from "node-emoji";
44
import shelljs from "shelljs";
55
import { promisify } from "util";
66
import { Config } from "../../config";
7+
import { build as buildCmd, exit as exitCmd } from "../../lib/commandBuilder";
78
import { logger } from "../../logger";
9+
import { IConfigYaml } from "../../types";
10+
import decorator from "./validate.decorator.json";
811

912
const binaries: string[] = ["terraform", "git", "az", "helm"];
1013
const envVar: string[] = [
@@ -14,62 +17,54 @@ const envVar: string[] = [
1417
"ARM_TENANT_ID"
1518
];
1619

17-
/**
18-
* Adds the validate command to the commander command object
19-
*
20-
* @param command Commander command object to decorate
21-
*/
22-
export const validateCommandDecorator = (command: commander.Command): void => {
23-
command
24-
.command("validate")
25-
.alias("v")
26-
.description(
27-
"Validate will verify that all infrastructure deployment prerequisites have been correctly installed."
28-
)
29-
.action(async opts => {
30-
try {
31-
if (await validatePrereqs(binaries, false)) {
20+
export const execute = async (exitFn: (status: number) => Promise<void>) => {
21+
try {
22+
if (validatePrereqs(binaries, false)) {
23+
logger.info(
24+
emoji.emojify(
25+
"Installation of Prerequisites verified: :white_check_mark:"
26+
)
27+
);
28+
if (await validateAzure(false)) {
29+
logger.info(
30+
emoji.emojify("Azure account verified: :white_check_mark:")
31+
);
32+
if (validateEnvVariables(envVar, false)) {
3233
logger.info(
33-
emoji.emojify(
34-
"Installation of Prerequisites verified: :white_check_mark:"
35-
)
34+
emoji.emojify("Environment variables verified: :white_check_mark:")
3635
);
37-
if (await validateAzure(false)) {
38-
logger.info(
39-
emoji.emojify("Azure account verified: :white_check_mark:")
40-
);
41-
if (await validateEnvVariables(envVar, false)) {
42-
logger.info(
43-
emoji.emojify(
44-
"Environment variables verified: :white_check_mark:"
45-
)
46-
);
47-
}
48-
}
4936
}
50-
} catch (err) {
51-
logger.error(`Error validating init prerequisites`);
52-
logger.error(err);
5337
}
38+
}
39+
await exitFn(0);
40+
} catch (err) {
41+
logger.error(`Error validating init prerequisites`);
42+
logger.error(err);
43+
await exitFn(1);
44+
}
45+
};
46+
47+
export const commandDecorator = (command: commander.Command): void => {
48+
buildCmd(command, decorator).action(async () => {
49+
execute(async (status: number) => {
50+
await exitCmd(logger, process.exit, status);
5451
});
52+
});
5553
};
5654

5755
/**
5856
* Validates that prerequisites are installed
5957
*
6058
* @param executables Array of exectuables to check for in PATH
6159
*/
62-
export const validatePrereqs = async (
60+
export const validatePrereqs = (
6361
executables: string[],
6462
globalInit: boolean
65-
): Promise<boolean> => {
63+
): boolean => {
6664
const config = Config();
67-
if (!config.infra) {
68-
config.infra = {};
69-
}
70-
if (!config.infra.checks) {
71-
config.infra.checks = {};
72-
}
65+
config.infra = config.infra || {};
66+
config.infra.checks = config.infra.checks || {};
67+
7368
// Validate executables in PATH
7469
for (const i of executables) {
7570
if (!shelljs.which(i)) {
@@ -94,13 +89,9 @@ export const validatePrereqs = async (
9489
*/
9590
export const validateAzure = async (globalInit: boolean): Promise<boolean> => {
9691
const config = Config();
97-
// Validate authentication with Azure
98-
if (!config.infra) {
99-
config.infra = {};
100-
}
101-
if (!config.infra.checks) {
102-
config.infra.checks = {};
103-
}
92+
config.infra = config.infra || {};
93+
config.infra.checks = config.infra.checks || {};
94+
10495
try {
10596
await promisify(child_process.exec)("az account show -o none");
10697
} catch (err) {
@@ -123,18 +114,14 @@ export const validateAzure = async (globalInit: boolean): Promise<boolean> => {
123114
*
124115
* @param variables Array of environment vairables to check for
125116
*/
126-
export const validateEnvVariables = async (
117+
export const validateEnvVariables = (
127118
variables: string[],
128119
globalInit: boolean
129-
): Promise<boolean> => {
120+
): boolean => {
130121
const config = Config();
122+
config.infra = config.infra || {};
123+
config.infra.checks = config.infra.checks || {};
131124

132-
if (!config.infra) {
133-
config.infra = {};
134-
}
135-
if (!config.infra.checks) {
136-
config.infra.checks = {};
137-
}
138125
// Validate environment variables
139126
for (const i of variables) {
140127
if (!process.env[i]) {

src/lib/commandBuilder.test.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,19 @@ interface ICommandOption {
1414
}
1515

1616
describe("Tests Command Builder's build function", () => {
17+
it("Declaration with no options", () => {
18+
const descriptor: ICommandBuildElements = {
19+
alias: "cbt",
20+
command: "command-build-test",
21+
description: "description of command"
22+
};
23+
24+
const cmd = build(new commander.Command(), descriptor);
25+
26+
expect(cmd.description()).toBe("description of command");
27+
expect(cmd.alias()).toBe("cbt");
28+
expect(cmd.options.length).toBe(0);
29+
});
1730
it("Sanity tests", () => {
1831
const descriptor: ICommandBuildElements = {
1932
alias: "cbt",
@@ -56,7 +69,7 @@ describe("Tests Command Builder's build function", () => {
5669
expect(cmd.alias()).toBe("cbt");
5770

5871
cmd.options.forEach((opt: ICommandOption, i: number) => {
59-
const declared = descriptor.options[i];
72+
const declared = (descriptor.options || [])[i];
6073
expect(opt.flags).toBe(declared.arg);
6174
expect(opt.description).toBe(declared.description);
6275

@@ -68,6 +81,17 @@ describe("Tests Command Builder's build function", () => {
6881
});
6982

7083
describe("Tests Command Builder's validation function", () => {
84+
it("Validation tests", () => {
85+
const descriptor: ICommandBuildElements = {
86+
alias: "cbt",
87+
command: "command-build-test",
88+
description: "description of command"
89+
};
90+
91+
const errors = validateForRequiredValues(descriptor, {});
92+
93+
expect(errors.length).toBe(0);
94+
});
7195
it("Validation tests", () => {
7296
const descriptor: ICommandBuildElements = {
7397
alias: "cbt",

src/lib/commandBuilder.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface ICommandBuildElements {
2020
command: string;
2121
alias: string;
2222
description: string;
23-
options: ICommandOption[];
23+
options?: ICommandOption[];
2424
}
2525

2626
interface ICommandVariableName2Opt {
@@ -44,7 +44,7 @@ export const build = (
4444
.alias(decorator.alias)
4545
.description(decorator.description);
4646

47-
decorator.options.forEach(opt => {
47+
(decorator.options || []).forEach(opt => {
4848
if (opt.defaultValue !== undefined) {
4949
cmd.option(opt.arg, opt.description, opt.defaultValue);
5050
} else {
@@ -68,7 +68,12 @@ export const validateForRequiredValues = (
6868
values: { [key: string]: string | undefined }
6969
): string[] => {
7070
// gather the required options
71-
const requireds = decorator.options.filter(opt => opt.required === true);
71+
const requireds = (decorator.options || []).filter(opt => opt.required);
72+
73+
// no required variables hence return empty error array
74+
if (requireds.length === 0) {
75+
return [];
76+
}
7277

7378
// opt name to variable name mapping
7479
// example --org-name is orgName

0 commit comments

Comments
 (0)