@pagopa/dx-cli 0.23.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/templates/monorepo/nx.json +0 -1
- package/dist/adapters/azure/__tests__/cloud-account-repository.test.d.ts +0 -1
- package/dist/adapters/azure/__tests__/cloud-account-repository.test.js +0 -95
- package/dist/adapters/azure/__tests__/cloud-account-service.test.d.ts +0 -1
- package/dist/adapters/azure/__tests__/cloud-account-service.test.js +0 -378
- package/dist/adapters/codemods/__tests__/registry.test.d.ts +0 -1
- package/dist/adapters/codemods/__tests__/registry.test.js +0 -56
- package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.d.ts +0 -1
- package/dist/adapters/codemods/__tests__/use-azure-appsvc.test.js +0 -77
- package/dist/adapters/codemods/__tests__/use-pnpm.test.d.ts +0 -1
- package/dist/adapters/codemods/__tests__/use-pnpm.test.js +0 -148
- package/dist/adapters/commander/__tests__/env.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/env.test.js +0 -45
- package/dist/adapters/commander/__tests__/error-reporting.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/error-reporting.test.js +0 -63
- package/dist/adapters/commander/__tests__/exit-with-error.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/exit-with-error.test.js +0 -92
- package/dist/adapters/commander/__tests__/global-options.test.d.ts +0 -1
- package/dist/adapters/commander/__tests__/global-options.test.js +0 -33
- package/dist/adapters/commander/__tests__/spec.test.d.ts +0 -8
- package/dist/adapters/commander/__tests__/spec.test.js +0 -264
- package/dist/adapters/commander/commands/__tests__/add.test.d.ts +0 -4
- package/dist/adapters/commander/commands/__tests__/add.test.js +0 -167
- package/dist/adapters/commander/commands/__tests__/init-command.test.d.ts +0 -4
- package/dist/adapters/commander/commands/__tests__/init-command.test.js +0 -44
- package/dist/adapters/commander/commands/__tests__/init.test.d.ts +0 -4
- package/dist/adapters/commander/commands/__tests__/init.test.js +0 -48
- package/dist/adapters/commander/commands/__tests__/preconditions.test.d.ts +0 -1
- package/dist/adapters/commander/commands/__tests__/preconditions.test.js +0 -32
- package/dist/adapters/commander/commands/__tests__/savemoney.test.d.ts +0 -8
- package/dist/adapters/commander/commands/__tests__/savemoney.test.js +0 -60
- package/dist/adapters/commander/commands/__tests__/spec.test.d.ts +0 -7
- package/dist/adapters/commander/commands/__tests__/spec.test.js +0 -85
- package/dist/adapters/commander/presenters/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/commander/presenters/__tests__/index.test.js +0 -23
- package/dist/adapters/commander/presenters/__tests__/json.test.d.ts +0 -1
- package/dist/adapters/commander/presenters/__tests__/json.test.js +0 -108
- package/dist/adapters/commander/presenters/__tests__/text.test.d.ts +0 -1
- package/dist/adapters/commander/presenters/__tests__/text.test.js +0 -60
- package/dist/adapters/github/__tests__/github-repo.spec.d.ts +0 -1
- package/dist/adapters/github/__tests__/github-repo.spec.js +0 -67
- package/dist/adapters/node/__tests__/data.d.ts +0 -18
- package/dist/adapters/node/__tests__/data.js +0 -22
- package/dist/adapters/node/__tests__/package-json.test.d.ts +0 -1
- package/dist/adapters/node/__tests__/package-json.test.js +0 -86
- package/dist/adapters/node/__tests__/repository.test.d.ts +0 -1
- package/dist/adapters/node/__tests__/repository.test.js +0 -77
- package/dist/adapters/node/fs/__tests__/file-reader.test.d.ts +0 -1
- package/dist/adapters/node/fs/__tests__/file-reader.test.js +0 -80
- package/dist/adapters/node/json/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/node/json/__tests__/index.test.js +0 -14
- package/dist/adapters/octokit/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/octokit/__tests__/index.test.js +0 -414
- package/dist/adapters/pagopa-technology/__tests__/authorization.test.d.ts +0 -4
- package/dist/adapters/pagopa-technology/__tests__/authorization.test.js +0 -548
- package/dist/adapters/plop/__tests__/run-actions.test.d.ts +0 -1
- package/dist/adapters/plop/__tests__/run-actions.test.js +0 -68
- package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.d.ts +0 -1
- package/dist/adapters/plop/actions/__tests__/init-cloud-accounts.test.js +0 -171
- package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.d.ts +0 -1
- package/dist/adapters/plop/actions/__tests__/provision-terraform-backend.test.js +0 -134
- package/dist/adapters/plop/generators/__tests__/temp-dir.d.ts +0 -2
- package/dist/adapters/plop/generators/__tests__/temp-dir.js +0 -13
- package/dist/adapters/plop/generators/environment/__tests__/actions.test.d.ts +0 -2
- package/dist/adapters/plop/generators/environment/__tests__/actions.test.js +0 -92
- package/dist/adapters/plop/generators/environment/__tests__/generation.test.d.ts +0 -1
- package/dist/adapters/plop/generators/environment/__tests__/generation.test.js +0 -213
- package/dist/adapters/plop/generators/environment/__tests__/prompts.test.d.ts +0 -1
- package/dist/adapters/plop/generators/environment/__tests__/prompts.test.js +0 -182
- package/dist/adapters/plop/generators/monorepo/__tests__/generation.test.d.ts +0 -1
- package/dist/adapters/plop/generators/monorepo/__tests__/generation.test.js +0 -79
- package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.d.ts +0 -1
- package/dist/adapters/plop/helpers/__tests__/resource-prefix.test.js +0 -113
- package/dist/adapters/plop/helpers/__tests__/terraform-state-key.test.d.ts +0 -1
- package/dist/adapters/plop/helpers/__tests__/terraform-state-key.test.js +0 -35
- package/dist/adapters/plop/helpers/__tests__/validate-prompt.test.d.ts +0 -1
- package/dist/adapters/plop/helpers/__tests__/validate-prompt.test.js +0 -60
- package/dist/adapters/yaml/__tests__/index.test.d.ts +0 -1
- package/dist/adapters/yaml/__tests__/index.test.js +0 -53
- package/dist/domain/__tests__/data.d.ts +0 -15
- package/dist/domain/__tests__/data.js +0 -26
- package/dist/domain/__tests__/environment.test.d.ts +0 -1
- package/dist/domain/__tests__/environment.test.js +0 -332
- package/dist/domain/__tests__/info.test.d.ts +0 -1
- package/dist/domain/__tests__/info.test.js +0 -77
- package/dist/domain/__tests__/package-json.test.d.ts +0 -1
- package/dist/domain/__tests__/package-json.test.js +0 -39
- package/dist/domain/__tests__/repository.test.d.ts +0 -1
- package/dist/domain/__tests__/repository.test.js +0 -111
- package/dist/domain/__tests__/workspace.test.d.ts +0 -1
- package/dist/domain/__tests__/workspace.test.js +0 -57
- package/dist/use-cases/__tests__/apply-codemod.test.d.ts +0 -1
- package/dist/use-cases/__tests__/apply-codemod.test.js +0 -71
- package/dist/use-cases/__tests__/list-codemods.test.d.ts +0 -1
- package/dist/use-cases/__tests__/list-codemods.test.js +0 -37
- package/dist/use-cases/__tests__/request-authorization.test.d.ts +0 -4
- package/dist/use-cases/__tests__/request-authorization.test.js +0 -43
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for authorizeCloudAccounts in the init command.
|
|
3
|
-
*/
|
|
4
|
-
import { errAsync, okAsync } from "neverthrow";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
import { mock } from "vitest-mock-extended";
|
|
7
|
-
import { AuthorizationError, AuthorizationResult, } from "../../../../domain/authorization.js";
|
|
8
|
-
import { authorizeCloudAccounts } from "../add.js";
|
|
9
|
-
const makeEnvPayload = (overrides = {}) => ({
|
|
10
|
-
env: {
|
|
11
|
-
cloudAccounts: [
|
|
12
|
-
{
|
|
13
|
-
csp: "azure",
|
|
14
|
-
defaultLocation: "italynorth",
|
|
15
|
-
displayName: "DEV-FooBar",
|
|
16
|
-
id: "sub-123",
|
|
17
|
-
},
|
|
18
|
-
],
|
|
19
|
-
name: "dev",
|
|
20
|
-
prefix: "dx",
|
|
21
|
-
},
|
|
22
|
-
github: { owner: "pagopa", repo: "test-repo" },
|
|
23
|
-
tags: { BusinessUnit: "BU", CostCenter: "TS000", ManagementTeam: "MT" },
|
|
24
|
-
workspace: { domain: "" },
|
|
25
|
-
...overrides,
|
|
26
|
-
});
|
|
27
|
-
describe("authorizeCloudAccounts", () => {
|
|
28
|
-
it("returns empty array when init is undefined (env already initialized)", async () => {
|
|
29
|
-
const authService = mock();
|
|
30
|
-
const envPayload = makeEnvPayload({ init: undefined });
|
|
31
|
-
const result = await authorizeCloudAccounts(authService)(envPayload);
|
|
32
|
-
expect(result.isOk()).toBe(true);
|
|
33
|
-
expect(result._unsafeUnwrap()).toEqual([]);
|
|
34
|
-
expect(authService.requestAuthorization).not.toHaveBeenCalled();
|
|
35
|
-
});
|
|
36
|
-
it("returns empty array when cloudAccountsToInitialize is empty", async () => {
|
|
37
|
-
const authService = mock();
|
|
38
|
-
const envPayload = makeEnvPayload({
|
|
39
|
-
init: { cloudAccountsToInitialize: [] },
|
|
40
|
-
});
|
|
41
|
-
const result = await authorizeCloudAccounts(authService)(envPayload);
|
|
42
|
-
expect(result.isOk()).toBe(true);
|
|
43
|
-
expect(result._unsafeUnwrap()).toEqual([]);
|
|
44
|
-
});
|
|
45
|
-
it("requests authorization for each initialized account", async () => {
|
|
46
|
-
const authService = mock();
|
|
47
|
-
const expectedPr = new AuthorizationResult("https://github.com/pagopa/eng-azure-authorization/pull/42");
|
|
48
|
-
authService.requestAuthorization.mockReturnValue(okAsync(expectedPr));
|
|
49
|
-
const account = {
|
|
50
|
-
csp: "azure",
|
|
51
|
-
defaultLocation: "italynorth",
|
|
52
|
-
displayName: "DEV-FooBar",
|
|
53
|
-
id: "sub-123",
|
|
54
|
-
};
|
|
55
|
-
const envPayload = makeEnvPayload({
|
|
56
|
-
env: {
|
|
57
|
-
cloudAccounts: [account],
|
|
58
|
-
name: "dev",
|
|
59
|
-
prefix: "dx",
|
|
60
|
-
},
|
|
61
|
-
init: { cloudAccountsToInitialize: [account] },
|
|
62
|
-
});
|
|
63
|
-
const result = await authorizeCloudAccounts(authService)(envPayload);
|
|
64
|
-
expect(result.isOk()).toBe(true);
|
|
65
|
-
expect(result._unsafeUnwrap()).toEqual([expectedPr]);
|
|
66
|
-
expect(authService.requestAuthorization).toHaveBeenCalledWith(expect.objectContaining({
|
|
67
|
-
bootstrapIdentityId: "dx-d-itn-bootstrap-id-01",
|
|
68
|
-
envShort: "d",
|
|
69
|
-
prefix: "dx",
|
|
70
|
-
subscriptionName: "DEV-FooBar",
|
|
71
|
-
}));
|
|
72
|
-
});
|
|
73
|
-
it("computes correct identity for westeurope location", async () => {
|
|
74
|
-
const authService = mock();
|
|
75
|
-
authService.requestAuthorization.mockReturnValue(okAsync(new AuthorizationResult("https://example.com/pr/1")));
|
|
76
|
-
const account = {
|
|
77
|
-
csp: "azure",
|
|
78
|
-
defaultLocation: "westeurope",
|
|
79
|
-
displayName: "PROD-Bar",
|
|
80
|
-
id: "sub-456",
|
|
81
|
-
};
|
|
82
|
-
const envPayload = makeEnvPayload({
|
|
83
|
-
env: {
|
|
84
|
-
cloudAccounts: [account],
|
|
85
|
-
name: "prod",
|
|
86
|
-
prefix: "io",
|
|
87
|
-
},
|
|
88
|
-
init: { cloudAccountsToInitialize: [account] },
|
|
89
|
-
});
|
|
90
|
-
const result = await authorizeCloudAccounts(authService)(envPayload);
|
|
91
|
-
expect(result.isOk()).toBe(true);
|
|
92
|
-
expect(authService.requestAuthorization).toHaveBeenCalledWith(expect.objectContaining({
|
|
93
|
-
bootstrapIdentityId: "io-p-weu-bootstrap-id-01",
|
|
94
|
-
envShort: "p",
|
|
95
|
-
prefix: "io",
|
|
96
|
-
subscriptionName: "PROD-Bar",
|
|
97
|
-
}));
|
|
98
|
-
});
|
|
99
|
-
it("skips accounts with unsupported locations", async () => {
|
|
100
|
-
const authService = mock();
|
|
101
|
-
const account = {
|
|
102
|
-
csp: "azure",
|
|
103
|
-
defaultLocation: "eastus",
|
|
104
|
-
displayName: "DEV-Unsupported",
|
|
105
|
-
id: "sub-789",
|
|
106
|
-
};
|
|
107
|
-
const envPayload = makeEnvPayload({
|
|
108
|
-
init: { cloudAccountsToInitialize: [account] },
|
|
109
|
-
});
|
|
110
|
-
const result = await authorizeCloudAccounts(authService)(envPayload);
|
|
111
|
-
expect(result.isOk()).toBe(true);
|
|
112
|
-
expect(result._unsafeUnwrap()).toEqual([]);
|
|
113
|
-
expect(authService.requestAuthorization).not.toHaveBeenCalled();
|
|
114
|
-
});
|
|
115
|
-
it("continues when authorization fails for one account", async () => {
|
|
116
|
-
const authService = mock();
|
|
117
|
-
const account1 = {
|
|
118
|
-
csp: "azure",
|
|
119
|
-
defaultLocation: "italynorth",
|
|
120
|
-
displayName: "DEV-First",
|
|
121
|
-
id: "sub-1",
|
|
122
|
-
};
|
|
123
|
-
const account2 = {
|
|
124
|
-
csp: "azure",
|
|
125
|
-
defaultLocation: "italynorth",
|
|
126
|
-
displayName: "DEV-Second",
|
|
127
|
-
id: "sub-2",
|
|
128
|
-
};
|
|
129
|
-
const expectedPr = new AuthorizationResult("https://example.com/pr/2");
|
|
130
|
-
authService.requestAuthorization
|
|
131
|
-
.mockReturnValueOnce(errAsync(new AuthorizationError("Branch already exists")))
|
|
132
|
-
.mockReturnValueOnce(okAsync(expectedPr));
|
|
133
|
-
const envPayload = makeEnvPayload({
|
|
134
|
-
env: {
|
|
135
|
-
cloudAccounts: [account1, account2],
|
|
136
|
-
name: "dev",
|
|
137
|
-
prefix: "dx",
|
|
138
|
-
},
|
|
139
|
-
init: { cloudAccountsToInitialize: [account1, account2] },
|
|
140
|
-
});
|
|
141
|
-
const result = await authorizeCloudAccounts(authService)(envPayload);
|
|
142
|
-
expect(result.isOk()).toBe(true);
|
|
143
|
-
const prs = result._unsafeUnwrap();
|
|
144
|
-
expect(prs).toHaveLength(1);
|
|
145
|
-
expect(prs[0]).toEqual(expectedPr);
|
|
146
|
-
});
|
|
147
|
-
it("returns a no-op authorization result when nothing changed", async () => {
|
|
148
|
-
const authService = mock();
|
|
149
|
-
const account = {
|
|
150
|
-
csp: "azure",
|
|
151
|
-
defaultLocation: "italynorth",
|
|
152
|
-
displayName: "DEV-Existing",
|
|
153
|
-
id: "sub-exists",
|
|
154
|
-
};
|
|
155
|
-
// No-op result: Ok with no URL (identity + groups already configured)
|
|
156
|
-
authService.requestAuthorization.mockReturnValue(okAsync(new AuthorizationResult()));
|
|
157
|
-
const envPayload = makeEnvPayload({
|
|
158
|
-
init: { cloudAccountsToInitialize: [account] },
|
|
159
|
-
});
|
|
160
|
-
const result = await authorizeCloudAccounts(authService)(envPayload);
|
|
161
|
-
expect(result.isOk()).toBe(true);
|
|
162
|
-
// The no-op result is still collected but has no URL
|
|
163
|
-
const prs = result._unsafeUnwrap();
|
|
164
|
-
expect(prs).toHaveLength(1);
|
|
165
|
-
expect(prs[0].url).toBeUndefined();
|
|
166
|
-
});
|
|
167
|
-
});
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the init Commander command workflow.
|
|
3
|
-
*/
|
|
4
|
-
import { Command } from "commander";
|
|
5
|
-
import { okAsync } from "neverthrow";
|
|
6
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
|
-
import { mock } from "vitest-mock-extended";
|
|
8
|
-
const { tfMock } = vi.hoisted(() => ({
|
|
9
|
-
tfMock: vi.fn(),
|
|
10
|
-
}));
|
|
11
|
-
vi.mock("ora", () => ({
|
|
12
|
-
oraPromise: (promise) => promise,
|
|
13
|
-
}));
|
|
14
|
-
vi.mock("../../../execa/terraform.js", () => ({
|
|
15
|
-
tf$: tfMock,
|
|
16
|
-
}));
|
|
17
|
-
import { makeInitCommand } from "../init.js";
|
|
18
|
-
const makeRoot = (command) => new Command()
|
|
19
|
-
.exitOverride()
|
|
20
|
-
.addCommand(command)
|
|
21
|
-
.configureOutput({
|
|
22
|
-
writeErr: () => {
|
|
23
|
-
/* silence stderr in tests */
|
|
24
|
-
},
|
|
25
|
-
writeOut: () => {
|
|
26
|
-
/* silence stdout in tests */
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
describe("makeInitCommand", () => {
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
tfMock.mockReset();
|
|
32
|
-
});
|
|
33
|
-
it("checks local preconditions before requiring GitHub auth", async () => {
|
|
34
|
-
tfMock.mockRejectedValueOnce(new Error("Terraform not installed"));
|
|
35
|
-
const requireGitHubAuthSpy = vi.fn(() => okAsync({
|
|
36
|
-
authorizationService: mock(),
|
|
37
|
-
gitHubService: mock(),
|
|
38
|
-
}));
|
|
39
|
-
const requireGitHubAuth = requireGitHubAuthSpy;
|
|
40
|
-
const root = makeRoot(makeInitCommand(requireGitHubAuth));
|
|
41
|
-
await expect(root.parseAsync(["init"], { from: "user" })).rejects.toBeTruthy();
|
|
42
|
-
expect(requireGitHubAuthSpy).not.toHaveBeenCalled();
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for confirmGitHubRepoCreation in the init command.
|
|
3
|
-
*/
|
|
4
|
-
import inquirer from "inquirer";
|
|
5
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
|
-
import { confirmGitHubRepoCreation } from "../init.js";
|
|
7
|
-
vi.mock("inquirer");
|
|
8
|
-
const makePayload = (overrides = {}) => ({
|
|
9
|
-
repoDescription: "A test repo",
|
|
10
|
-
repoName: "test-repo",
|
|
11
|
-
repoOwner: "pagopa",
|
|
12
|
-
...overrides,
|
|
13
|
-
});
|
|
14
|
-
describe("confirmGitHubRepoCreation", () => {
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
vi.restoreAllMocks();
|
|
17
|
-
});
|
|
18
|
-
it("returns true when the user confirms", async () => {
|
|
19
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: true });
|
|
20
|
-
const result = await confirmGitHubRepoCreation(makePayload());
|
|
21
|
-
expect(result.isOk()).toBe(true);
|
|
22
|
-
expect(result._unsafeUnwrap()).toBe(true);
|
|
23
|
-
});
|
|
24
|
-
it("returns false when the user declines", async () => {
|
|
25
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: false });
|
|
26
|
-
const result = await confirmGitHubRepoCreation(makePayload());
|
|
27
|
-
expect(result.isOk()).toBe(true);
|
|
28
|
-
expect(result._unsafeUnwrap()).toBe(false);
|
|
29
|
-
});
|
|
30
|
-
it("prompts with the correct repository name and owner", async () => {
|
|
31
|
-
vi.mocked(inquirer.prompt).mockResolvedValue({ confirm: true });
|
|
32
|
-
const payload = makePayload({ repoName: "my-repo", repoOwner: "my-org" });
|
|
33
|
-
await confirmGitHubRepoCreation(payload);
|
|
34
|
-
expect(inquirer.prompt).toHaveBeenCalledWith(expect.objectContaining({
|
|
35
|
-
message: expect.stringContaining("my-org/my-repo"),
|
|
36
|
-
type: "confirm",
|
|
37
|
-
}));
|
|
38
|
-
});
|
|
39
|
-
it("returns an error result when the prompt rejects", async () => {
|
|
40
|
-
const cause = new Error("non-interactive TTY");
|
|
41
|
-
vi.mocked(inquirer.prompt).mockRejectedValue(cause);
|
|
42
|
-
const result = await confirmGitHubRepoCreation(makePayload());
|
|
43
|
-
expect(result.isErr()).toBe(true);
|
|
44
|
-
const err = result._unsafeUnwrapErr();
|
|
45
|
-
expect(err).toBeInstanceOf(Error);
|
|
46
|
-
expect(err.cause).toBe(cause);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
const mocks = vi.hoisted(() => ({
|
|
3
|
-
oraPromise: vi.fn((promise) => promise),
|
|
4
|
-
tf$: vi.fn(async (...args) => {
|
|
5
|
-
void args;
|
|
6
|
-
return { stdout: '{"user":{"name":"test@example.com"}}' };
|
|
7
|
-
}),
|
|
8
|
-
}));
|
|
9
|
-
vi.mock("ora", () => ({ oraPromise: mocks.oraPromise }));
|
|
10
|
-
vi.mock("../../../execa/terraform.js", () => ({ tf$: mocks.tf$ }));
|
|
11
|
-
import { checkAddEnvironmentPreconditions, checkInitPreconditions, } from "../init.js";
|
|
12
|
-
const calledCommands = () => mocks.tf$.mock.calls.map(([strings, ...values]) => strings.reduce((command, part, index) => command + part + String(values[index] ?? ""), ""));
|
|
13
|
-
describe("init preconditions", () => {
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
vi.clearAllMocks();
|
|
16
|
-
});
|
|
17
|
-
it("checkInitPreconditions does not require Azure login", async () => {
|
|
18
|
-
const result = await checkInitPreconditions();
|
|
19
|
-
expect(result.isOk()).toBe(true);
|
|
20
|
-
expect(calledCommands()).toEqual(["terraform -version", "corepack -v"]);
|
|
21
|
-
});
|
|
22
|
-
it("checkAddEnvironmentPreconditions requires Azure login", async () => {
|
|
23
|
-
const result = await checkAddEnvironmentPreconditions();
|
|
24
|
-
expect(result.isOk()).toBe(true);
|
|
25
|
-
expect(calledCommands()).toEqual([
|
|
26
|
-
"terraform -version",
|
|
27
|
-
"az account show",
|
|
28
|
-
"az group list",
|
|
29
|
-
"corepack -v",
|
|
30
|
-
]);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the pure helper functions in the savemoney command.
|
|
3
|
-
*
|
|
4
|
-
* The full command action (Azure API calls, spinner) is not exercised here
|
|
5
|
-
* because it requires live credentials. These tests cover the stateless
|
|
6
|
-
* parsing helpers that can be validated without any I/O.
|
|
7
|
-
*/
|
|
8
|
-
export {};
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for the pure helper functions in the savemoney command.
|
|
3
|
-
*
|
|
4
|
-
* The full command action (Azure API calls, spinner) is not exercised here
|
|
5
|
-
* because it requires live credentials. These tests cover the stateless
|
|
6
|
-
* parsing helpers that can be validated without any I/O.
|
|
7
|
-
*/
|
|
8
|
-
import { InvalidArgumentError } from "commander";
|
|
9
|
-
import { describe, expect, it } from "vitest";
|
|
10
|
-
import { makeSavemoneyCommand, parseSourceOption, parseTagsOption, } from "../savemoney.js";
|
|
11
|
-
describe("parseSourceOption", () => {
|
|
12
|
-
it("accepts 'advisor'", () => {
|
|
13
|
-
expect(parseSourceOption("advisor")).toBe("advisor");
|
|
14
|
-
});
|
|
15
|
-
it("accepts 'custom'", () => {
|
|
16
|
-
expect(parseSourceOption("custom")).toBe("custom");
|
|
17
|
-
});
|
|
18
|
-
it("accepts 'all'", () => {
|
|
19
|
-
expect(parseSourceOption("all")).toBe("all");
|
|
20
|
-
});
|
|
21
|
-
it("throws InvalidArgumentError for an unknown value", () => {
|
|
22
|
-
expect(() => parseSourceOption("aws")).toThrow(InvalidArgumentError);
|
|
23
|
-
});
|
|
24
|
-
it("throws InvalidArgumentError for an empty string", () => {
|
|
25
|
-
expect(() => parseSourceOption("")).toThrow(InvalidArgumentError);
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
describe("parseTagsOption", () => {
|
|
29
|
-
it("returns an empty Map when tagsOption is undefined", () => {
|
|
30
|
-
expect(parseTagsOption(undefined)).toEqual(new Map());
|
|
31
|
-
});
|
|
32
|
-
it("returns an empty Map when tagsOption is an empty array", () => {
|
|
33
|
-
expect(parseTagsOption([])).toEqual(new Map());
|
|
34
|
-
});
|
|
35
|
-
it("parses a single key=value pair", () => {
|
|
36
|
-
expect(parseTagsOption(["env=dev"])).toEqual(new Map([["env", "dev"]]));
|
|
37
|
-
});
|
|
38
|
-
it("parses multiple key=value pairs", () => {
|
|
39
|
-
expect(parseTagsOption(["env=dev", "team=platform"])).toEqual(new Map([
|
|
40
|
-
["env", "dev"],
|
|
41
|
-
["team", "platform"],
|
|
42
|
-
]));
|
|
43
|
-
});
|
|
44
|
-
it("handles values that contain '='", () => {
|
|
45
|
-
expect(parseTagsOption(["url=https://example.com/path=1"])).toEqual(new Map([["url", "https://example.com/path=1"]]));
|
|
46
|
-
});
|
|
47
|
-
it("ignores entries without a '=' separator", () => {
|
|
48
|
-
expect(parseTagsOption(["noequalssign"])).toEqual(new Map());
|
|
49
|
-
});
|
|
50
|
-
it("trims whitespace from keys and values", () => {
|
|
51
|
-
expect(parseTagsOption([" env = dev "])).toEqual(new Map([["env", "dev"]]));
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
describe("makeSavemoneyCommand", () => {
|
|
55
|
-
it("documents that --tags does not filter subscription-level Advisor findings", () => {
|
|
56
|
-
const command = makeSavemoneyCommand();
|
|
57
|
-
const tagsOption = command.options.find((option) => option.flags.includes("--tags"));
|
|
58
|
-
expect(tagsOption?.description).toContain("Advisor subscription-level findings remain global.");
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the `spec` Commander command.
|
|
3
|
-
*
|
|
4
|
-
* Verifies that `makeSpecCommand` calls the `getSpec` callback and writes the
|
|
5
|
-
* resulting CliSpec as pretty-printed JSON to stdout.
|
|
6
|
-
*/
|
|
7
|
-
import { Command } from "commander";
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
|
|
9
|
-
import { makeSpecCommand } from "../spec.js";
|
|
10
|
-
const makeMinimalSpec = () => ({
|
|
11
|
-
commands: [],
|
|
12
|
-
description: "The CLI for DX-Platform",
|
|
13
|
-
globalOptions: [],
|
|
14
|
-
name: "dx",
|
|
15
|
-
specVersion: "1",
|
|
16
|
-
version: "0.0.0",
|
|
17
|
-
});
|
|
18
|
-
describe("makeSpecCommand", () => {
|
|
19
|
-
let stdoutSpy;
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
stdoutSpy = vi
|
|
22
|
-
.spyOn(process.stdout, "write")
|
|
23
|
-
.mockImplementation(() => true);
|
|
24
|
-
});
|
|
25
|
-
afterEach(() => {
|
|
26
|
-
stdoutSpy.mockRestore();
|
|
27
|
-
});
|
|
28
|
-
it("is registered as the 'spec' subcommand", () => {
|
|
29
|
-
const cmd = makeSpecCommand(() => makeMinimalSpec());
|
|
30
|
-
expect(cmd.name()).toBe("spec");
|
|
31
|
-
});
|
|
32
|
-
it("calls getSpec and writes JSON to stdout when invoked", async () => {
|
|
33
|
-
const spec = makeMinimalSpec();
|
|
34
|
-
const getSpec = vi.fn().mockReturnValue(spec);
|
|
35
|
-
const cmd = makeSpecCommand(getSpec);
|
|
36
|
-
// Parse with an isolated parent so Commander doesn't exit
|
|
37
|
-
const root = new Command().exitOverride().addCommand(cmd);
|
|
38
|
-
root.configureOutput({
|
|
39
|
-
writeErr: () => {
|
|
40
|
-
/* silence */
|
|
41
|
-
},
|
|
42
|
-
writeOut: () => {
|
|
43
|
-
/* silence */
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
await root.parseAsync(["spec"], { from: "user" });
|
|
47
|
-
expect(getSpec).toHaveBeenCalledOnce();
|
|
48
|
-
const written = stdoutSpy.mock.calls.map((c) => c[0]).join("");
|
|
49
|
-
expect(JSON.parse(written)).toEqual(spec);
|
|
50
|
-
});
|
|
51
|
-
it("outputs valid pretty-printed JSON (indented)", async () => {
|
|
52
|
-
const spec = makeMinimalSpec();
|
|
53
|
-
const cmd = makeSpecCommand(() => spec);
|
|
54
|
-
const root = new Command().exitOverride().addCommand(cmd);
|
|
55
|
-
root.configureOutput({
|
|
56
|
-
writeErr: () => {
|
|
57
|
-
/* silence */
|
|
58
|
-
},
|
|
59
|
-
writeOut: () => {
|
|
60
|
-
/* silence */
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
await root.parseAsync(["spec"], { from: "user" });
|
|
64
|
-
const written = stdoutSpy.mock.calls.map((c) => c[0]).join("");
|
|
65
|
-
// Pretty-printed JSON has newlines
|
|
66
|
-
expect(written).toContain("\n");
|
|
67
|
-
expect(written).toContain(" ");
|
|
68
|
-
});
|
|
69
|
-
it("includes specVersion '1' in the output", async () => {
|
|
70
|
-
const cmd = makeSpecCommand(() => makeMinimalSpec());
|
|
71
|
-
const root = new Command().exitOverride().addCommand(cmd);
|
|
72
|
-
root.configureOutput({
|
|
73
|
-
writeErr: () => {
|
|
74
|
-
/* silence */
|
|
75
|
-
},
|
|
76
|
-
writeOut: () => {
|
|
77
|
-
/* silence */
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
await root.parseAsync(["spec"], { from: "user" });
|
|
81
|
-
const written = stdoutSpy.mock.calls.map((c) => c[0]).join("");
|
|
82
|
-
const parsed = JSON.parse(written);
|
|
83
|
-
expect(parsed.specVersion).toBe("1");
|
|
84
|
-
});
|
|
85
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for isNonInteractive and createCommandPresenter factory.
|
|
3
|
-
*/
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
import { createCommandPresenter, isNonInteractive } from "../index.js";
|
|
6
|
-
import { JsonCommandPresenter } from "../json-command-presenter.js";
|
|
7
|
-
import { TextCommandPresenter } from "../text-command-presenter.js";
|
|
8
|
-
describe("isNonInteractive", () => {
|
|
9
|
-
it("returns true when CI is true", () => {
|
|
10
|
-
expect(isNonInteractive({ CI: true })).toBe(true);
|
|
11
|
-
});
|
|
12
|
-
it("returns false when CI is false", () => {
|
|
13
|
-
expect(isNonInteractive({ CI: false })).toBe(false);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
describe("createCommandPresenter", () => {
|
|
17
|
-
it("returns a TextCommandPresenter when output is 'text'", () => {
|
|
18
|
-
expect(createCommandPresenter("text")).toBeInstanceOf(TextCommandPresenter);
|
|
19
|
-
});
|
|
20
|
-
it("returns a JsonCommandPresenter when output is 'json'", () => {
|
|
21
|
-
expect(createCommandPresenter("json")).toBeInstanceOf(JsonCommandPresenter);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for JsonCommandPresenter.
|
|
3
|
-
*
|
|
4
|
-
* JsonCommandPresenter uses a split-stream convention:
|
|
5
|
-
* - stdout for the final result/error envelope
|
|
6
|
-
* - stderr for step lifecycle (progress) events
|
|
7
|
-
*
|
|
8
|
-
* Tests verify the wire format, correct stream usage, and that values
|
|
9
|
-
* flow through correctly.
|
|
10
|
-
*/
|
|
11
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
12
|
-
import { JsonCommandPresenter } from "../json-command-presenter.js";
|
|
13
|
-
const captureStdout = () => {
|
|
14
|
-
const written = [];
|
|
15
|
-
vi.spyOn(process.stdout, "write").mockImplementation((data) => {
|
|
16
|
-
written.push(String(data));
|
|
17
|
-
return true;
|
|
18
|
-
});
|
|
19
|
-
return { written };
|
|
20
|
-
};
|
|
21
|
-
const captureStderr = () => {
|
|
22
|
-
const written = [];
|
|
23
|
-
vi.spyOn(process.stderr, "write").mockImplementation((data) => {
|
|
24
|
-
written.push(String(data));
|
|
25
|
-
return true;
|
|
26
|
-
});
|
|
27
|
-
return { written };
|
|
28
|
-
};
|
|
29
|
-
describe("JsonCommandPresenter", () => {
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
vi.restoreAllMocks();
|
|
32
|
-
});
|
|
33
|
-
describe("trackStep", () => {
|
|
34
|
-
it("executes the task and returns its resolved value", async () => {
|
|
35
|
-
captureStderr();
|
|
36
|
-
const logger = new JsonCommandPresenter();
|
|
37
|
-
const result = await logger.trackStep("check terraform", () => Promise.resolve(42));
|
|
38
|
-
expect(result).toBe(42);
|
|
39
|
-
});
|
|
40
|
-
it("emits start then success events to stderr", async () => {
|
|
41
|
-
const stderr = captureStderr();
|
|
42
|
-
const logger = new JsonCommandPresenter();
|
|
43
|
-
await logger.trackStep("check terraform", () => Promise.resolve("done"));
|
|
44
|
-
expect(stderr.written).toHaveLength(2);
|
|
45
|
-
expect(JSON.parse(stderr.written[0])).toMatchObject({
|
|
46
|
-
name: "check terraform",
|
|
47
|
-
status: "start",
|
|
48
|
-
type: "step",
|
|
49
|
-
});
|
|
50
|
-
expect(JSON.parse(stderr.written[1])).toMatchObject({
|
|
51
|
-
name: "check terraform",
|
|
52
|
-
status: "success",
|
|
53
|
-
type: "step",
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
it("emits error event to stderr and rethrows on task failure", async () => {
|
|
57
|
-
const stderr = captureStderr();
|
|
58
|
-
const logger = new JsonCommandPresenter();
|
|
59
|
-
const error = new Error("terraform not found");
|
|
60
|
-
await expect(logger.trackStep("check terraform", () => Promise.reject(error))).rejects.toThrow("terraform not found");
|
|
61
|
-
expect(JSON.parse(stderr.written[1])).toMatchObject({
|
|
62
|
-
error: "terraform not found",
|
|
63
|
-
name: "check terraform",
|
|
64
|
-
status: "error",
|
|
65
|
-
type: "step",
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
it("can run multiple sequential steps", async () => {
|
|
69
|
-
captureStderr();
|
|
70
|
-
const logger = new JsonCommandPresenter();
|
|
71
|
-
const order = [];
|
|
72
|
-
await logger.trackStep("step A", async () => {
|
|
73
|
-
order.push("A");
|
|
74
|
-
});
|
|
75
|
-
await logger.trackStep("step B", async () => {
|
|
76
|
-
order.push("B");
|
|
77
|
-
});
|
|
78
|
-
expect(order).toEqual(["A", "B"]);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
describe("reportResult", () => {
|
|
82
|
-
it("emits an ok:true JSON envelope to stdout", () => {
|
|
83
|
-
const stdout = captureStdout();
|
|
84
|
-
const logger = new JsonCommandPresenter();
|
|
85
|
-
logger.reportResult({ repository: { name: "my-repo" } });
|
|
86
|
-
expect(JSON.parse(stdout.written[0])).toEqual({
|
|
87
|
-
data: { repository: { name: "my-repo" } },
|
|
88
|
-
ok: true,
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
describe("reportError", () => {
|
|
93
|
-
it("emits an ok:false JSON envelope to stdout", () => {
|
|
94
|
-
const stdout = captureStdout();
|
|
95
|
-
const logger = new JsonCommandPresenter();
|
|
96
|
-
logger.reportError(new Error("something failed"));
|
|
97
|
-
expect(JSON.parse(stdout.written[0])).toMatchObject({
|
|
98
|
-
error: "something failed",
|
|
99
|
-
ok: false,
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
it("handles non-Error values without throwing", () => {
|
|
103
|
-
captureStdout();
|
|
104
|
-
const logger = new JsonCommandPresenter();
|
|
105
|
-
expect(() => logger.reportError("plain string error")).not.toThrow();
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|