@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,332 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { environmentSchema, getInitializationStatus, getTerraformBackend, hasUserPermissionToInitialize, } from "../environment.js";
|
|
3
|
-
const createMockCloudAccountService = (overrides = {}) => ({
|
|
4
|
-
getTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
5
|
-
hasUserPermissionToInitialize: vi.fn().mockResolvedValue(true),
|
|
6
|
-
initialize: vi.fn().mockResolvedValue(undefined),
|
|
7
|
-
isInitialized: vi.fn().mockResolvedValue(true),
|
|
8
|
-
provisionTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
9
|
-
...overrides,
|
|
10
|
-
});
|
|
11
|
-
const createMockCloudAccount = (overrides = {}) => ({
|
|
12
|
-
csp: "azure",
|
|
13
|
-
defaultLocation: "westeurope",
|
|
14
|
-
displayName: "Test Account",
|
|
15
|
-
id: "test-account-id",
|
|
16
|
-
...overrides,
|
|
17
|
-
});
|
|
18
|
-
const createMockEnvironment = (overrides = {}) => ({
|
|
19
|
-
cloudAccounts: [createMockCloudAccount()],
|
|
20
|
-
name: "dev",
|
|
21
|
-
prefix: "dx",
|
|
22
|
-
...overrides,
|
|
23
|
-
});
|
|
24
|
-
const createMockTerraformBackend = (overrides = {}) => ({
|
|
25
|
-
resourceGroupName: "dx-d-itn-tf-rg",
|
|
26
|
-
storageAccountName: "dxditntfst",
|
|
27
|
-
subscriptionId: "00000000-0000-0000-0000-000000000000",
|
|
28
|
-
type: "azurerm",
|
|
29
|
-
...overrides,
|
|
30
|
-
});
|
|
31
|
-
describe("getInitializationStatus", () => {
|
|
32
|
-
it("should return initialized true when all cloud accounts are initialized and backend exists", async () => {
|
|
33
|
-
const mockBackend = createMockTerraformBackend();
|
|
34
|
-
const mockService = createMockCloudAccountService({
|
|
35
|
-
getTerraformBackend: vi.fn().mockResolvedValue(mockBackend),
|
|
36
|
-
isInitialized: vi.fn().mockResolvedValue(true),
|
|
37
|
-
});
|
|
38
|
-
const environment = createMockEnvironment();
|
|
39
|
-
const result = await getInitializationStatus(mockService, environment);
|
|
40
|
-
expect(result).toEqual({ initialized: true });
|
|
41
|
-
});
|
|
42
|
-
it("should return CLOUD_ACCOUNT_NOT_INITIALIZED issue when cloud account is not initialized", async () => {
|
|
43
|
-
const cloudAccount = createMockCloudAccount({ id: "uninitialized-id" });
|
|
44
|
-
const mockBackend = createMockTerraformBackend();
|
|
45
|
-
const mockService = createMockCloudAccountService({
|
|
46
|
-
getTerraformBackend: vi.fn().mockResolvedValue(mockBackend),
|
|
47
|
-
isInitialized: vi.fn().mockResolvedValue(false),
|
|
48
|
-
});
|
|
49
|
-
const environment = createMockEnvironment({
|
|
50
|
-
cloudAccounts: [cloudAccount],
|
|
51
|
-
});
|
|
52
|
-
const result = await getInitializationStatus(mockService, environment);
|
|
53
|
-
expect(result).toEqual({
|
|
54
|
-
initialized: false,
|
|
55
|
-
issues: [
|
|
56
|
-
{
|
|
57
|
-
cloudAccount,
|
|
58
|
-
type: "CLOUD_ACCOUNT_NOT_INITIALIZED",
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
it("should return MISSING_REMOTE_BACKEND issue when terraform backend is not found", async () => {
|
|
64
|
-
const mockService = createMockCloudAccountService({
|
|
65
|
-
getTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
66
|
-
isInitialized: vi.fn().mockResolvedValue(true),
|
|
67
|
-
});
|
|
68
|
-
const environment = createMockEnvironment();
|
|
69
|
-
const result = await getInitializationStatus(mockService, environment);
|
|
70
|
-
expect(result).toEqual({
|
|
71
|
-
initialized: false,
|
|
72
|
-
issues: [
|
|
73
|
-
{
|
|
74
|
-
type: "MISSING_REMOTE_BACKEND",
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
it("should return multiple issues when both cloud account is not initialized and backend is missing", async () => {
|
|
80
|
-
const cloudAccount = createMockCloudAccount();
|
|
81
|
-
const mockService = createMockCloudAccountService({
|
|
82
|
-
getTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
83
|
-
isInitialized: vi.fn().mockResolvedValue(false),
|
|
84
|
-
});
|
|
85
|
-
const environment = createMockEnvironment({
|
|
86
|
-
cloudAccounts: [cloudAccount],
|
|
87
|
-
});
|
|
88
|
-
const result = await getInitializationStatus(mockService, environment);
|
|
89
|
-
expect(result).toEqual({
|
|
90
|
-
initialized: false,
|
|
91
|
-
issues: [
|
|
92
|
-
{
|
|
93
|
-
cloudAccount,
|
|
94
|
-
type: "CLOUD_ACCOUNT_NOT_INITIALIZED",
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
type: "MISSING_REMOTE_BACKEND",
|
|
98
|
-
},
|
|
99
|
-
],
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
it("should check initialization for each cloud account with correct parameters", async () => {
|
|
103
|
-
const cloudAccount1 = createMockCloudAccount({ id: "account-1" });
|
|
104
|
-
const cloudAccount2 = createMockCloudAccount({ id: "account-2" });
|
|
105
|
-
const mockBackend = createMockTerraformBackend();
|
|
106
|
-
const isInitializedMock = vi.fn().mockResolvedValue(true);
|
|
107
|
-
const mockService = createMockCloudAccountService({
|
|
108
|
-
getTerraformBackend: vi.fn().mockResolvedValue(mockBackend),
|
|
109
|
-
isInitialized: isInitializedMock,
|
|
110
|
-
});
|
|
111
|
-
const environment = createMockEnvironment({
|
|
112
|
-
cloudAccounts: [cloudAccount1, cloudAccount2],
|
|
113
|
-
name: "prod",
|
|
114
|
-
prefix: "io",
|
|
115
|
-
});
|
|
116
|
-
await getInitializationStatus(mockService, environment);
|
|
117
|
-
expect(isInitializedMock).toHaveBeenCalledTimes(2);
|
|
118
|
-
expect(isInitializedMock).toHaveBeenCalledWith("account-1", expect.objectContaining({
|
|
119
|
-
name: "prod",
|
|
120
|
-
prefix: "io",
|
|
121
|
-
}));
|
|
122
|
-
expect(isInitializedMock).toHaveBeenCalledWith("account-2", expect.objectContaining({
|
|
123
|
-
name: "prod",
|
|
124
|
-
prefix: "io",
|
|
125
|
-
}));
|
|
126
|
-
});
|
|
127
|
-
it("should return issues for multiple uninitialized cloud accounts", async () => {
|
|
128
|
-
const cloudAccount1 = createMockCloudAccount({ id: "account-1" });
|
|
129
|
-
const cloudAccount2 = createMockCloudAccount({ id: "account-2" });
|
|
130
|
-
const mockBackend = createMockTerraformBackend();
|
|
131
|
-
const mockService = createMockCloudAccountService({
|
|
132
|
-
getTerraformBackend: vi.fn().mockResolvedValue(mockBackend),
|
|
133
|
-
isInitialized: vi.fn().mockResolvedValue(false),
|
|
134
|
-
});
|
|
135
|
-
const environment = createMockEnvironment({
|
|
136
|
-
cloudAccounts: [cloudAccount1, cloudAccount2],
|
|
137
|
-
});
|
|
138
|
-
const result = await getInitializationStatus(mockService, environment);
|
|
139
|
-
expect(result).toEqual({
|
|
140
|
-
initialized: false,
|
|
141
|
-
issues: [
|
|
142
|
-
{
|
|
143
|
-
cloudAccount: cloudAccount1,
|
|
144
|
-
type: "CLOUD_ACCOUNT_NOT_INITIALIZED",
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
cloudAccount: cloudAccount2,
|
|
148
|
-
type: "CLOUD_ACCOUNT_NOT_INITIALIZED",
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
describe("getTerraformBackend", () => {
|
|
155
|
-
it("should return the terraform backend when it exists", async () => {
|
|
156
|
-
const mockBackend = createMockTerraformBackend();
|
|
157
|
-
const mockService = createMockCloudAccountService({
|
|
158
|
-
getTerraformBackend: vi.fn().mockResolvedValue(mockBackend),
|
|
159
|
-
});
|
|
160
|
-
const environment = createMockEnvironment();
|
|
161
|
-
const result = await getTerraformBackend(mockService, environment);
|
|
162
|
-
expect(result).toEqual(mockBackend);
|
|
163
|
-
});
|
|
164
|
-
it("should return undefined when no terraform backend exists", async () => {
|
|
165
|
-
const mockService = createMockCloudAccountService({
|
|
166
|
-
getTerraformBackend: vi.fn().mockResolvedValue(undefined),
|
|
167
|
-
});
|
|
168
|
-
const environment = createMockEnvironment();
|
|
169
|
-
const result = await getTerraformBackend(mockService, environment);
|
|
170
|
-
expect(result).toBeUndefined();
|
|
171
|
-
});
|
|
172
|
-
it("should call getTerraformBackend with correct parameters", async () => {
|
|
173
|
-
const getTerraformBackendMock = vi.fn().mockResolvedValue(undefined);
|
|
174
|
-
const mockService = createMockCloudAccountService({
|
|
175
|
-
getTerraformBackend: getTerraformBackendMock,
|
|
176
|
-
});
|
|
177
|
-
const environment = createMockEnvironment({
|
|
178
|
-
cloudAccounts: [createMockCloudAccount({ id: "my-account" })],
|
|
179
|
-
name: "uat",
|
|
180
|
-
prefix: "io",
|
|
181
|
-
});
|
|
182
|
-
await getTerraformBackend(mockService, environment);
|
|
183
|
-
expect(getTerraformBackendMock).toHaveBeenCalledWith("my-account", expect.objectContaining({
|
|
184
|
-
name: "uat",
|
|
185
|
-
prefix: "io",
|
|
186
|
-
}));
|
|
187
|
-
});
|
|
188
|
-
it("should return the first backend found when multiple cloud accounts exist", async () => {
|
|
189
|
-
const mockBackend = createMockTerraformBackend({
|
|
190
|
-
storageAccountName: "firstaccountst",
|
|
191
|
-
});
|
|
192
|
-
const getTerraformBackendMock = vi.fn().mockResolvedValue(mockBackend);
|
|
193
|
-
const mockService = createMockCloudAccountService({
|
|
194
|
-
getTerraformBackend: getTerraformBackendMock,
|
|
195
|
-
});
|
|
196
|
-
const environment = createMockEnvironment({
|
|
197
|
-
cloudAccounts: [
|
|
198
|
-
createMockCloudAccount({ id: "account-1" }),
|
|
199
|
-
createMockCloudAccount({ id: "account-2" }),
|
|
200
|
-
],
|
|
201
|
-
});
|
|
202
|
-
const result = await getTerraformBackend(mockService, environment);
|
|
203
|
-
expect(result).toEqual(mockBackend);
|
|
204
|
-
// Should only call for the first account due to early return
|
|
205
|
-
expect(getTerraformBackendMock).toHaveBeenCalledTimes(1);
|
|
206
|
-
});
|
|
207
|
-
it("should return undefined when environment has no cloud accounts", async () => {
|
|
208
|
-
const mockService = createMockCloudAccountService();
|
|
209
|
-
const environment = createMockEnvironment({
|
|
210
|
-
cloudAccounts: [],
|
|
211
|
-
});
|
|
212
|
-
const result = await getTerraformBackend(mockService, environment);
|
|
213
|
-
expect(result).toBeUndefined();
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
describe("hasUserPermissionToInitialize", () => {
|
|
217
|
-
it("should return true when all cloud accounts have permission", async () => {
|
|
218
|
-
const mockService = createMockCloudAccountService({
|
|
219
|
-
hasUserPermissionToInitialize: vi.fn().mockResolvedValue(true),
|
|
220
|
-
});
|
|
221
|
-
const environment = createMockEnvironment({
|
|
222
|
-
cloudAccounts: [
|
|
223
|
-
createMockCloudAccount({ id: "account-1" }),
|
|
224
|
-
createMockCloudAccount({ id: "account-2" }),
|
|
225
|
-
],
|
|
226
|
-
});
|
|
227
|
-
const result = await hasUserPermissionToInitialize(mockService, environment);
|
|
228
|
-
expect(result).toBe(true);
|
|
229
|
-
});
|
|
230
|
-
it("should return false when any cloud account lacks permission", async () => {
|
|
231
|
-
const hasUserPermissionMock = vi
|
|
232
|
-
.fn()
|
|
233
|
-
.mockResolvedValueOnce(true)
|
|
234
|
-
.mockResolvedValueOnce(false);
|
|
235
|
-
const mockService = createMockCloudAccountService({
|
|
236
|
-
hasUserPermissionToInitialize: hasUserPermissionMock,
|
|
237
|
-
});
|
|
238
|
-
const environment = createMockEnvironment({
|
|
239
|
-
cloudAccounts: [
|
|
240
|
-
createMockCloudAccount({ id: "account-1" }),
|
|
241
|
-
createMockCloudAccount({ id: "account-2" }),
|
|
242
|
-
],
|
|
243
|
-
});
|
|
244
|
-
const result = await hasUserPermissionToInitialize(mockService, environment);
|
|
245
|
-
expect(result).toBe(false);
|
|
246
|
-
expect(hasUserPermissionMock).toHaveBeenCalledTimes(2);
|
|
247
|
-
});
|
|
248
|
-
it("should short-circuit and return false on first unauthorized account", async () => {
|
|
249
|
-
const hasUserPermissionMock = vi.fn().mockResolvedValue(false);
|
|
250
|
-
const mockService = createMockCloudAccountService({
|
|
251
|
-
hasUserPermissionToInitialize: hasUserPermissionMock,
|
|
252
|
-
});
|
|
253
|
-
const environment = createMockEnvironment({
|
|
254
|
-
cloudAccounts: [
|
|
255
|
-
createMockCloudAccount({ id: "account-1" }),
|
|
256
|
-
createMockCloudAccount({ id: "account-2" }),
|
|
257
|
-
createMockCloudAccount({ id: "account-3" }),
|
|
258
|
-
],
|
|
259
|
-
});
|
|
260
|
-
const result = await hasUserPermissionToInitialize(mockService, environment);
|
|
261
|
-
expect(result).toBe(false);
|
|
262
|
-
// Should only check the first account before short-circuiting
|
|
263
|
-
expect(hasUserPermissionMock).toHaveBeenCalledTimes(1);
|
|
264
|
-
expect(hasUserPermissionMock).toHaveBeenCalledWith("account-1");
|
|
265
|
-
});
|
|
266
|
-
it("should call service with correct cloud account IDs", async () => {
|
|
267
|
-
const hasUserPermissionMock = vi.fn().mockResolvedValue(true);
|
|
268
|
-
const mockService = createMockCloudAccountService({
|
|
269
|
-
hasUserPermissionToInitialize: hasUserPermissionMock,
|
|
270
|
-
});
|
|
271
|
-
const environment = createMockEnvironment({
|
|
272
|
-
cloudAccounts: [
|
|
273
|
-
createMockCloudAccount({ id: "account-a" }),
|
|
274
|
-
createMockCloudAccount({ id: "account-b" }),
|
|
275
|
-
],
|
|
276
|
-
});
|
|
277
|
-
await hasUserPermissionToInitialize(mockService, environment);
|
|
278
|
-
expect(hasUserPermissionMock).toHaveBeenCalledTimes(2);
|
|
279
|
-
expect(hasUserPermissionMock).toHaveBeenNthCalledWith(1, "account-a");
|
|
280
|
-
expect(hasUserPermissionMock).toHaveBeenNthCalledWith(2, "account-b");
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
describe("environmentSchema — prefix transforms", () => {
|
|
284
|
-
const baseEnv = {
|
|
285
|
-
cloudAccounts: [
|
|
286
|
-
{
|
|
287
|
-
csp: "azure",
|
|
288
|
-
defaultLocation: "westeurope",
|
|
289
|
-
displayName: "Test Account",
|
|
290
|
-
id: "test-account-id",
|
|
291
|
-
},
|
|
292
|
-
],
|
|
293
|
-
name: "dev",
|
|
294
|
-
prefix: "dx",
|
|
295
|
-
};
|
|
296
|
-
it("lowercases an uppercase prefix", () => {
|
|
297
|
-
const result = environmentSchema.safeParse({ ...baseEnv, prefix: "ABC" });
|
|
298
|
-
expect(result.success).toBe(true);
|
|
299
|
-
expect(result.success && result.data.prefix).toBe("abc");
|
|
300
|
-
});
|
|
301
|
-
it("lowercases a mixed-case prefix", () => {
|
|
302
|
-
const result = environmentSchema.safeParse({ ...baseEnv, prefix: "AbCd" });
|
|
303
|
-
expect(result.success).toBe(true);
|
|
304
|
-
expect(result.success && result.data.prefix).toBe("abcd");
|
|
305
|
-
});
|
|
306
|
-
it("trims leading and trailing whitespace from prefix", () => {
|
|
307
|
-
const result = environmentSchema.safeParse({ ...baseEnv, prefix: " dx " });
|
|
308
|
-
expect(result.success).toBe(true);
|
|
309
|
-
expect(result.success && result.data.prefix).toBe("dx");
|
|
310
|
-
});
|
|
311
|
-
it("trims and lowercases prefix together", () => {
|
|
312
|
-
const result = environmentSchema.safeParse({ ...baseEnv, prefix: " DX " });
|
|
313
|
-
expect(result.success).toBe(true);
|
|
314
|
-
expect(result.success && result.data.prefix).toBe("dx");
|
|
315
|
-
});
|
|
316
|
-
it("still rejects a prefix shorter than 2 characters after trim", () => {
|
|
317
|
-
const result = environmentSchema.safeParse({ ...baseEnv, prefix: " a " });
|
|
318
|
-
expect(result.success).toBe(false);
|
|
319
|
-
});
|
|
320
|
-
it("still rejects a prefix longer than 4 characters after trim", () => {
|
|
321
|
-
const result = environmentSchema.safeParse({
|
|
322
|
-
...baseEnv,
|
|
323
|
-
prefix: "abcde",
|
|
324
|
-
});
|
|
325
|
-
expect(result.success).toBe(false);
|
|
326
|
-
});
|
|
327
|
-
it("accepts a valid lowercase prefix unchanged", () => {
|
|
328
|
-
const result = environmentSchema.safeParse({ ...baseEnv, prefix: "dx" });
|
|
329
|
-
expect(result.success).toBe(true);
|
|
330
|
-
expect(result.success && result.data.prefix).toBe("dx");
|
|
331
|
-
});
|
|
332
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { errAsync, okAsync } from "neverthrow";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { getInfo } from "../info.js";
|
|
4
|
-
import { makeMockDependencies, makeMockPackageJson, makeMockRepositoryRoot, } from "./data.js";
|
|
5
|
-
describe("getInfo", () => {
|
|
6
|
-
describe("packageManager", () => {
|
|
7
|
-
it("should return default packageManager (npm) when packageManager is not detected", async () => {
|
|
8
|
-
const mockPackageJson = makeMockPackageJson({
|
|
9
|
-
packageManager: undefined,
|
|
10
|
-
});
|
|
11
|
-
const mockDependencies = makeMockDependencies();
|
|
12
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
13
|
-
mockDependencies.repositoryReader.findRepositoryRoot.mockReturnValue(okAsync(repositoryRoot));
|
|
14
|
-
mockDependencies.packageJsonReader.readPackageJson.mockReturnValue(okAsync(mockPackageJson));
|
|
15
|
-
mockDependencies.repositoryReader.fileExists.mockReturnValue(okAsync(false));
|
|
16
|
-
mockDependencies.repositoryReader.readFile.mockReturnValue(okAsync("22.0.0"));
|
|
17
|
-
const result = await getInfo(mockDependencies)();
|
|
18
|
-
expect(result.packageManager).toStrictEqual("npm");
|
|
19
|
-
expect(mockDependencies.repositoryReader.fileExists).nthCalledWith(1, "a/repo/root/pnpm-lock.yaml");
|
|
20
|
-
expect(mockDependencies.repositoryReader.fileExists).nthCalledWith(2, "a/repo/root/yarn.lock");
|
|
21
|
-
expect(mockDependencies.repositoryReader.fileExists).nthCalledWith(3, "a/repo/root/package-lock.json");
|
|
22
|
-
});
|
|
23
|
-
it("should return yarn when yarn.lock is present", async () => {
|
|
24
|
-
const mockPackageJson = makeMockPackageJson({
|
|
25
|
-
packageManager: undefined,
|
|
26
|
-
});
|
|
27
|
-
const mockDependencies = makeMockDependencies();
|
|
28
|
-
mockDependencies.packageJsonReader.readPackageJson.mockReturnValue(okAsync(mockPackageJson));
|
|
29
|
-
mockDependencies.repositoryReader.fileExists
|
|
30
|
-
.mockReturnValueOnce(okAsync(false))
|
|
31
|
-
.mockReturnValueOnce(okAsync(true)); // yarn lock file exists
|
|
32
|
-
mockDependencies.repositoryReader.readFile.mockReturnValue(okAsync("22.0.0"));
|
|
33
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
34
|
-
mockDependencies.repositoryReader.findRepositoryRoot.mockReturnValue(okAsync(repositoryRoot));
|
|
35
|
-
const result = await getInfo(mockDependencies)();
|
|
36
|
-
expect(result.packageManager).toStrictEqual("yarn");
|
|
37
|
-
expect(mockDependencies.repositoryReader.fileExists).nthCalledWith(1, "a/repo/root/pnpm-lock.yaml");
|
|
38
|
-
expect(mockDependencies.repositoryReader.fileExists).nthCalledWith(2, "a/repo/root/yarn.lock");
|
|
39
|
-
expect(mockDependencies.repositoryReader.fileExists).not.toHaveBeenCalledWith("a/repo/root/package-lock.json");
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
it("should return all info", async () => {
|
|
43
|
-
const mockPackageJson = makeMockPackageJson({
|
|
44
|
-
devDependencies: new Map([["turbo", "^2.5.0"]]),
|
|
45
|
-
});
|
|
46
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
47
|
-
const mockDependencies = makeMockDependencies();
|
|
48
|
-
mockDependencies.repositoryReader.findRepositoryRoot.mockReturnValue(okAsync(repositoryRoot));
|
|
49
|
-
mockDependencies.packageJsonReader.readPackageJson.mockReturnValue(okAsync(mockPackageJson));
|
|
50
|
-
mockDependencies.repositoryReader.readFile
|
|
51
|
-
.mockReturnValueOnce(okAsync("\n22.0.0\n"))
|
|
52
|
-
.mockReturnValueOnce(okAsync("1.0.0\n"));
|
|
53
|
-
const result = await getInfo(mockDependencies)();
|
|
54
|
-
expect(result).toStrictEqual({
|
|
55
|
-
node: "22.0.0",
|
|
56
|
-
packageManager: "pnpm",
|
|
57
|
-
terraform: "1.0.0",
|
|
58
|
-
turbo: "^2.5.0",
|
|
59
|
-
});
|
|
60
|
-
expect(mockDependencies.repositoryReader.readFile).toHaveBeenCalledWith("a/repo/root/.node-version");
|
|
61
|
-
expect(mockDependencies.repositoryReader.readFile).toHaveBeenCalledWith("a/repo/root/.terraform-version");
|
|
62
|
-
});
|
|
63
|
-
it("should only required information", async () => {
|
|
64
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
65
|
-
const mockDependencies = makeMockDependencies();
|
|
66
|
-
mockDependencies.repositoryReader.findRepositoryRoot.mockReturnValue(okAsync(repositoryRoot));
|
|
67
|
-
mockDependencies.packageJsonReader.readPackageJson.mockReturnValue(okAsync(makeMockPackageJson()));
|
|
68
|
-
mockDependencies.repositoryReader.readFile.mockReturnValue(errAsync(new Error("File not found")));
|
|
69
|
-
const result = await getInfo(mockDependencies)();
|
|
70
|
-
expect(result).toStrictEqual({
|
|
71
|
-
node: undefined,
|
|
72
|
-
packageManager: "pnpm",
|
|
73
|
-
terraform: undefined,
|
|
74
|
-
turbo: undefined,
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { errAsync, ok, okAsync } from "neverthrow";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { checkMonorepoScripts } from "../package-json.js";
|
|
4
|
-
import { makeMockDependencies, makeMockRepositoryRoot } from "./data.js";
|
|
5
|
-
describe("checkMonorepoScripts", () => {
|
|
6
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
7
|
-
it("should return error result when getScripts fails", async () => {
|
|
8
|
-
const deps = makeMockDependencies();
|
|
9
|
-
const errorMessage = "Oh No!";
|
|
10
|
-
deps.packageJsonReader.getScripts.mockReturnValueOnce(errAsync(new Error(errorMessage)));
|
|
11
|
-
const result = await checkMonorepoScripts(deps, repositoryRoot);
|
|
12
|
-
expect(result.isErr()).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
it("should return ok result with successful validation when all required scripts are present", async () => {
|
|
15
|
-
const deps = makeMockDependencies();
|
|
16
|
-
const scripts = new Map()
|
|
17
|
-
.set("build", "eslint .")
|
|
18
|
-
.set("code-review", "eslint .");
|
|
19
|
-
deps.packageJsonReader.getScripts.mockReturnValueOnce(okAsync(scripts));
|
|
20
|
-
deps.packageJsonReader.getRootRequiredScripts.mockReturnValueOnce(new Map().set("code-review", "eslint ."));
|
|
21
|
-
const result = await checkMonorepoScripts(deps, repositoryRoot);
|
|
22
|
-
expect(result).toStrictEqual(ok({
|
|
23
|
-
checkName: "Monorepo Scripts",
|
|
24
|
-
isValid: true,
|
|
25
|
-
successMessage: "Monorepo scripts are correctly set up",
|
|
26
|
-
}));
|
|
27
|
-
});
|
|
28
|
-
it("should return ok result with failed validation when required scripts are missing", async () => {
|
|
29
|
-
const deps = makeMockDependencies();
|
|
30
|
-
deps.packageJsonReader.getScripts.mockReturnValueOnce(okAsync(new Map()));
|
|
31
|
-
deps.packageJsonReader.getRootRequiredScripts.mockReturnValueOnce(new Map().set("code-review", "eslint ."));
|
|
32
|
-
const result = await checkMonorepoScripts(deps, repositoryRoot);
|
|
33
|
-
expect(result).toStrictEqual(ok({
|
|
34
|
-
checkName: "Monorepo Scripts",
|
|
35
|
-
errorMessage: "Missing required scripts: code-review",
|
|
36
|
-
isValid: false,
|
|
37
|
-
}));
|
|
38
|
-
});
|
|
39
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { errAsync, ok, okAsync } from "neverthrow";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { checkNxConfig, checkPreCommitConfig } from "../repository.js";
|
|
4
|
-
import { makeMockConfig, makeMockDependencies, makeMockRepositoryRoot, } from "./data.js";
|
|
5
|
-
describe("checkPreCommitConfig", () => {
|
|
6
|
-
it("should return ok result with successful validation when .pre-commit-config.yaml exists", async () => {
|
|
7
|
-
const deps = makeMockDependencies();
|
|
8
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
9
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(okAsync(true));
|
|
10
|
-
const result = await checkPreCommitConfig(deps, repositoryRoot);
|
|
11
|
-
expect(result).toStrictEqual(ok({
|
|
12
|
-
checkName: "Pre-commit Configuration",
|
|
13
|
-
isValid: true,
|
|
14
|
-
successMessage: "Pre-commit configuration is present in the repository root",
|
|
15
|
-
}));
|
|
16
|
-
expect(deps.repositoryReader.fileExists).toHaveBeenCalledWith("a/repo/root/.pre-commit-config.yaml");
|
|
17
|
-
});
|
|
18
|
-
it("should return ok result with failed validation when .pre-commit-config.yaml does not exist", async () => {
|
|
19
|
-
const deps = makeMockDependencies();
|
|
20
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
21
|
-
const errorMessage = ".pre-commit-config.yaml not found in repository root. Make sure to have pre-commit configured for the repository.";
|
|
22
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(errAsync(new Error(errorMessage)));
|
|
23
|
-
const result = await checkPreCommitConfig(deps, repositoryRoot);
|
|
24
|
-
expect(result).toStrictEqual(ok({
|
|
25
|
-
checkName: "Pre-commit Configuration",
|
|
26
|
-
errorMessage,
|
|
27
|
-
isValid: false,
|
|
28
|
-
}));
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe("checkNxConfig", () => {
|
|
32
|
-
const config = makeMockConfig();
|
|
33
|
-
const repositoryRoot = makeMockRepositoryRoot();
|
|
34
|
-
it("should return ok result with successful validation when nx.json exists and nx dependency is present", async () => {
|
|
35
|
-
const deps = makeMockDependencies();
|
|
36
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(okAsync(true));
|
|
37
|
-
deps.packageJsonReader.getDependencies.mockReturnValueOnce(okAsync(new Map().set("nx", "^22.6.1")));
|
|
38
|
-
const result = await checkNxConfig(deps, repositoryRoot, config);
|
|
39
|
-
expect(result).toStrictEqual(ok({
|
|
40
|
-
checkName: "Nx Configuration",
|
|
41
|
-
isValid: true,
|
|
42
|
-
successMessage: "Nx configuration is present in the monorepo root and Nx dependency is installed",
|
|
43
|
-
}));
|
|
44
|
-
expect(deps.repositoryReader.fileExists).toHaveBeenCalledWith("a/repo/root/nx.json");
|
|
45
|
-
});
|
|
46
|
-
it("should return ok result with failed validation when nx.json exists but Nx dependency is missing", async () => {
|
|
47
|
-
const deps = makeMockDependencies();
|
|
48
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(okAsync(true));
|
|
49
|
-
deps.packageJsonReader.getDependencies.mockReturnValueOnce(okAsync(new Map().set("eslint", "^8.0.0")));
|
|
50
|
-
const result = await checkNxConfig(deps, repositoryRoot, config);
|
|
51
|
-
expect(result).toStrictEqual(ok({
|
|
52
|
-
checkName: "Nx Configuration",
|
|
53
|
-
errorMessage: "Nx dependency not found in devDependencies. Please add 'nx' to your devDependencies.",
|
|
54
|
-
isValid: false,
|
|
55
|
-
}));
|
|
56
|
-
});
|
|
57
|
-
it("should return ok result with failed validation when nx.json does not exist", async () => {
|
|
58
|
-
const deps = makeMockDependencies();
|
|
59
|
-
const errorMessage = "nx.json not found in repository root. Make sure to have Nx configured for the monorepo.";
|
|
60
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(errAsync(new Error(errorMessage)));
|
|
61
|
-
const result = await checkNxConfig(deps, repositoryRoot, config);
|
|
62
|
-
expect(result).toStrictEqual(ok({
|
|
63
|
-
checkName: "Nx Configuration",
|
|
64
|
-
errorMessage,
|
|
65
|
-
isValid: false,
|
|
66
|
-
}));
|
|
67
|
-
});
|
|
68
|
-
it("should return ok result with failed validation when fileExists returns ok(false)", async () => {
|
|
69
|
-
const deps = makeMockDependencies();
|
|
70
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(okAsync(false));
|
|
71
|
-
const result = await checkNxConfig(deps, repositoryRoot, config);
|
|
72
|
-
expect(result).toStrictEqual(ok({
|
|
73
|
-
checkName: "Nx Configuration",
|
|
74
|
-
errorMessage: "nx.json not found in repository root. Make sure to have Nx configured for the monorepo.",
|
|
75
|
-
isValid: false,
|
|
76
|
-
}));
|
|
77
|
-
});
|
|
78
|
-
it("should return the error message when nx is not listed in devDependencies", async () => {
|
|
79
|
-
const deps = makeMockDependencies();
|
|
80
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(okAsync(true));
|
|
81
|
-
deps.packageJsonReader.getDependencies.mockReturnValueOnce(okAsync(new Map().set("eslint", "^8.0.0")));
|
|
82
|
-
const result = await checkNxConfig(deps, repositoryRoot, config);
|
|
83
|
-
expect(result).toStrictEqual(ok({
|
|
84
|
-
checkName: "Nx Configuration",
|
|
85
|
-
errorMessage: "Nx dependency not found in devDependencies. Please add 'nx' to your devDependencies.",
|
|
86
|
-
isValid: false,
|
|
87
|
-
}));
|
|
88
|
-
});
|
|
89
|
-
it("should return the error message when nx version is less than minimum", async () => {
|
|
90
|
-
const deps = makeMockDependencies();
|
|
91
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(okAsync(true));
|
|
92
|
-
deps.packageJsonReader.getDependencies.mockReturnValueOnce(okAsync(new Map().set("nx", "1.0.0")));
|
|
93
|
-
const result = await checkNxConfig(deps, repositoryRoot, config);
|
|
94
|
-
expect(result).toStrictEqual(ok({
|
|
95
|
-
checkName: "Nx Configuration",
|
|
96
|
-
errorMessage: `Nx version (1.0.0) is too low. Minimum required version is ${config.minVersions.nx}.`,
|
|
97
|
-
isValid: false,
|
|
98
|
-
}));
|
|
99
|
-
});
|
|
100
|
-
it("should return the success message when nx version is ok", async () => {
|
|
101
|
-
const deps = makeMockDependencies();
|
|
102
|
-
deps.repositoryReader.fileExists.mockReturnValueOnce(okAsync(true));
|
|
103
|
-
deps.packageJsonReader.getDependencies.mockReturnValueOnce(okAsync(new Map().set("nx", config.minVersions.nx)));
|
|
104
|
-
const result = await checkNxConfig(deps, repositoryRoot, config);
|
|
105
|
-
expect(result).toStrictEqual(ok({
|
|
106
|
-
checkName: "Nx Configuration",
|
|
107
|
-
isValid: true,
|
|
108
|
-
successMessage: "Nx configuration is present in the monorepo root and Nx dependency is installed",
|
|
109
|
-
}));
|
|
110
|
-
});
|
|
111
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { err, ok } from "neverthrow";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { checkWorkspaces, workspaceSchema } from "../workspace.js";
|
|
4
|
-
import { makeMockDependencies } from "./data.js";
|
|
5
|
-
describe("checkWorkspaces", () => {
|
|
6
|
-
const monorepoDir = "/path/to/monorepo";
|
|
7
|
-
const validWorkspaces = [
|
|
8
|
-
workspaceSchema.parse({
|
|
9
|
-
name: "workspace1",
|
|
10
|
-
path: "/path/to/workspace1",
|
|
11
|
-
}),
|
|
12
|
-
workspaceSchema.parse({
|
|
13
|
-
name: "workspace2",
|
|
14
|
-
path: "/path/to/workspace2",
|
|
15
|
-
}),
|
|
16
|
-
];
|
|
17
|
-
it("should return the list of workspaces", async () => {
|
|
18
|
-
const deps = makeMockDependencies();
|
|
19
|
-
deps.repositoryReader.getWorkspaces.mockResolvedValueOnce(ok(validWorkspaces));
|
|
20
|
-
const result = await checkWorkspaces(deps, monorepoDir);
|
|
21
|
-
expect(result).toStrictEqual(ok({
|
|
22
|
-
checkName: "Workspaces",
|
|
23
|
-
isValid: true,
|
|
24
|
-
successMessage: "Found 2 workspaces",
|
|
25
|
-
}));
|
|
26
|
-
});
|
|
27
|
-
it("should return error when getWorkspaces fails", async () => {
|
|
28
|
-
const deps = makeMockDependencies();
|
|
29
|
-
deps.repositoryReader.getWorkspaces.mockResolvedValueOnce(err(new Error("Failed to get workspaces")));
|
|
30
|
-
const result = await checkWorkspaces(deps, monorepoDir);
|
|
31
|
-
expect(result).toStrictEqual(ok({
|
|
32
|
-
checkName: "Workspaces",
|
|
33
|
-
errorMessage: "Something is wrong with the workspaces configuration. If you need help, please contact the DevEx team.",
|
|
34
|
-
isValid: false,
|
|
35
|
-
}));
|
|
36
|
-
});
|
|
37
|
-
it("should return success when workspaces are found", async () => {
|
|
38
|
-
const deps = makeMockDependencies();
|
|
39
|
-
deps.repositoryReader.getWorkspaces.mockResolvedValueOnce(ok([validWorkspaces[0]]));
|
|
40
|
-
const result = await checkWorkspaces(deps, monorepoDir);
|
|
41
|
-
expect(result).toStrictEqual(ok({
|
|
42
|
-
checkName: "Workspaces",
|
|
43
|
-
isValid: true,
|
|
44
|
-
successMessage: "Found 1 workspace",
|
|
45
|
-
}));
|
|
46
|
-
});
|
|
47
|
-
it("should return error when no workspace configuration is found", async () => {
|
|
48
|
-
const deps = makeMockDependencies();
|
|
49
|
-
deps.repositoryReader.getWorkspaces.mockResolvedValueOnce(ok([]));
|
|
50
|
-
const result = await checkWorkspaces(deps, monorepoDir);
|
|
51
|
-
expect(result).toStrictEqual(ok({
|
|
52
|
-
checkName: "Workspaces",
|
|
53
|
-
errorMessage: "No workspace configuration found. Make sure to configure workspaces in pnpm-workspace.yaml.",
|
|
54
|
-
isValid: false,
|
|
55
|
-
}));
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|