@lightdash/cli 2.228.1 → 2.229.0
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/dist/.tsbuildinfo.build +1 -0
- package/package.json +4 -4
- package/dist/.tsbuildinfo +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -72
- package/dist/dbt/context.test.d.ts +0 -2
- package/dist/dbt/context.test.d.ts.map +0 -1
- package/dist/dbt/context.test.js +0 -152
- package/dist/dbt/manifest.test.d.ts +0 -2
- package/dist/dbt/manifest.test.d.ts.map +0 -1
- package/dist/dbt/manifest.test.js +0 -170
- package/dist/dbt/models.test.d.ts +0 -2
- package/dist/dbt/models.test.d.ts.map +0 -1
- package/dist/dbt/models.test.js +0 -139
- package/dist/dbt/profiles.test.d.ts +0 -2
- package/dist/dbt/profiles.test.d.ts.map +0 -1
- package/dist/dbt/profiles.test.js +0 -50
- package/dist/dbt/targets/athena.test.d.ts +0 -2
- package/dist/dbt/targets/athena.test.d.ts.map +0 -1
- package/dist/dbt/targets/athena.test.js +0 -78
- package/dist/dbt/targets/duckdb.test.d.ts +0 -2
- package/dist/dbt/targets/duckdb.test.d.ts.map +0 -1
- package/dist/dbt/targets/duckdb.test.js +0 -149
- package/dist/dbt/targets/redshift.test.d.ts +0 -2
- package/dist/dbt/targets/redshift.test.d.ts.map +0 -1
- package/dist/dbt/targets/redshift.test.js +0 -198
- package/dist/dbt/templating.test.d.ts +0 -2
- package/dist/dbt/templating.test.d.ts.map +0 -1
- package/dist/dbt/templating.test.js +0 -51
- package/dist/dbt/validation.test.d.ts +0 -2
- package/dist/dbt/validation.test.d.ts.map +0 -1
- package/dist/dbt/validation.test.js +0 -67
- package/dist/handlers/apps/appCodeFiles.test.d.ts +0 -2
- package/dist/handlers/apps/appCodeFiles.test.d.ts.map +0 -1
- package/dist/handlers/apps/appCodeFiles.test.js +0 -391
- package/dist/handlers/apps/appsDownload.test.d.ts +0 -2
- package/dist/handlers/apps/appsDownload.test.d.ts.map +0 -1
- package/dist/handlers/apps/appsDownload.test.js +0 -639
- package/dist/handlers/apps/build.test.d.ts +0 -2
- package/dist/handlers/apps/build.test.d.ts.map +0 -1
- package/dist/handlers/apps/build.test.js +0 -252
- package/dist/handlers/apps/createApp.test.d.ts +0 -2
- package/dist/handlers/apps/createApp.test.d.ts.map +0 -1
- package/dist/handlers/apps/createApp.test.js +0 -340
- package/dist/handlers/apps/pathContainment.test.d.ts +0 -2
- package/dist/handlers/apps/pathContainment.test.d.ts.map +0 -1
- package/dist/handlers/apps/pathContainment.test.js +0 -66
- package/dist/handlers/apps/preview.test.d.ts +0 -2
- package/dist/handlers/apps/preview.test.d.ts.map +0 -1
- package/dist/handlers/apps/preview.test.js +0 -353
- package/dist/handlers/apps/previewProxy.test.d.ts +0 -2
- package/dist/handlers/apps/previewProxy.test.d.ts.map +0 -1
- package/dist/handlers/apps/previewProxy.test.js +0 -178
- package/dist/handlers/apps/scaffolding.test.d.ts +0 -2
- package/dist/handlers/apps/scaffolding.test.d.ts.map +0 -1
- package/dist/handlers/apps/scaffolding.test.js +0 -151
- package/dist/handlers/apps/uploadRetry.test.d.ts +0 -2
- package/dist/handlers/apps/uploadRetry.test.d.ts.map +0 -1
- package/dist/handlers/apps/uploadRetry.test.js +0 -103
- package/dist/handlers/apps/validate.test.d.ts +0 -2
- package/dist/handlers/apps/validate.test.d.ts.map +0 -1
- package/dist/handlers/apps/validate.test.js +0 -526
- package/dist/handlers/compile.test.d.ts +0 -2
- package/dist/handlers/compile.test.d.ts.map +0 -1
- package/dist/handlers/compile.test.js +0 -151
- package/dist/handlers/compileProject.test.d.ts +0 -2
- package/dist/handlers/compileProject.test.d.ts.map +0 -1
- package/dist/handlers/compileProject.test.js +0 -637
- package/dist/handlers/compileWarehouseColumnValidation.test.d.ts +0 -2
- package/dist/handlers/compileWarehouseColumnValidation.test.d.ts.map +0 -1
- package/dist/handlers/compileWarehouseColumnValidation.test.js +0 -493
- package/dist/handlers/connectSnowflake.test.d.ts +0 -2
- package/dist/handlers/connectSnowflake.test.d.ts.map +0 -1
- package/dist/handlers/connectSnowflake.test.js +0 -320
- package/dist/handlers/contentAsCode/fileNames.test.d.ts +0 -2
- package/dist/handlers/contentAsCode/fileNames.test.d.ts.map +0 -1
- package/dist/handlers/contentAsCode/fileNames.test.js +0 -79
- package/dist/handlers/contentAsCode/registry.test.d.ts +0 -2
- package/dist/handlers/contentAsCode/registry.test.d.ts.map +0 -1
- package/dist/handlers/contentAsCode/registry.test.js +0 -18
- package/dist/handlers/contentAsCode/resource.test.d.ts +0 -2
- package/dist/handlers/contentAsCode/resource.test.d.ts.map +0 -1
- package/dist/handlers/contentAsCode/resource.test.js +0 -121
- package/dist/handlers/dbt/getDbtVersion.test.d.ts +0 -2
- package/dist/handlers/dbt/getDbtVersion.test.d.ts.map +0 -1
- package/dist/handlers/dbt/getDbtVersion.test.js +0 -106
- package/dist/handlers/deploy.test.d.ts +0 -2
- package/dist/handlers/deploy.test.d.ts.map +0 -1
- package/dist/handlers/deploy.test.js +0 -244
- package/dist/handlers/download.test.d.ts +0 -2
- package/dist/handlers/download.test.d.ts.map +0 -1
- package/dist/handlers/download.test.js +0 -1739
- package/dist/handlers/exportChartImage.test.d.ts +0 -2
- package/dist/handlers/exportChartImage.test.d.ts.map +0 -1
- package/dist/handlers/exportChartImage.test.js +0 -55
- package/dist/handlers/homepagesAsCode.test.d.ts +0 -2
- package/dist/handlers/homepagesAsCode.test.d.ts.map +0 -1
- package/dist/handlers/homepagesAsCode.test.js +0 -72
- package/dist/handlers/lint.test.d.ts +0 -2
- package/dist/handlers/lint.test.d.ts.map +0 -1
- package/dist/handlers/lint.test.js +0 -221
- package/dist/handlers/login.test.d.ts +0 -2
- package/dist/handlers/login.test.d.ts.map +0 -1
- package/dist/handlers/login.test.js +0 -24
- package/dist/handlers/metadataFile.test.d.ts +0 -2
- package/dist/handlers/metadataFile.test.d.ts.map +0 -1
- package/dist/handlers/metadataFile.test.js +0 -46
- package/dist/handlers/organizationContent/customRoles.test.d.ts +0 -2
- package/dist/handlers/organizationContent/customRoles.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/customRoles.test.js +0 -179
- package/dist/handlers/organizationContent/groups.test.d.ts +0 -2
- package/dist/handlers/organizationContent/groups.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/groups.test.js +0 -135
- package/dist/handlers/organizationContent/index.test.d.ts +0 -2
- package/dist/handlers/organizationContent/index.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/index.test.js +0 -212
- package/dist/handlers/organizationContent/themes.test.d.ts +0 -2
- package/dist/handlers/organizationContent/themes.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/themes.test.js +0 -180
- package/dist/handlers/organizationContent/users.test.d.ts +0 -2
- package/dist/handlers/organizationContent/users.test.d.ts.map +0 -1
- package/dist/handlers/organizationContent/users.test.js +0 -155
- package/dist/handlers/preAggregateAudit.test.d.ts +0 -2
- package/dist/handlers/preAggregateAudit.test.d.ts.map +0 -1
- package/dist/handlers/preAggregateAudit.test.js +0 -102
- package/dist/handlers/renameHandler.test.d.ts +0 -2
- package/dist/handlers/renameHandler.test.d.ts.map +0 -1
- package/dist/handlers/renameHandler.test.js +0 -189
- package/dist/handlers/resolveProjectFlag.test.d.ts +0 -2
- package/dist/handlers/resolveProjectFlag.test.d.ts.map +0 -1
- package/dist/handlers/resolveProjectFlag.test.js +0 -48
- package/dist/handlers/selectProject.test.d.ts +0 -2
- package/dist/handlers/selectProject.test.d.ts.map +0 -1
- package/dist/handlers/selectProject.test.js +0 -224
- package/dist/handlers/setProject.test.d.ts +0 -2
- package/dist/handlers/setProject.test.d.ts.map +0 -1
- package/dist/handlers/setProject.test.js +0 -63
- package/dist/handlers/slugUpdate.test.d.ts +0 -2
- package/dist/handlers/slugUpdate.test.d.ts.map +0 -1
- package/dist/handlers/slugUpdate.test.js +0 -265
- package/dist/handlers/timestampConversion.test.d.ts +0 -2
- package/dist/handlers/timestampConversion.test.d.ts.map +0 -1
- package/dist/handlers/timestampConversion.test.js +0 -87
- package/dist/handlers/upgradeCheck.test.d.ts +0 -2
- package/dist/handlers/upgradeCheck.test.d.ts.map +0 -1
- package/dist/handlers/upgradeCheck.test.js +0 -227
- package/dist/handlers/validate.test.d.ts +0 -2
- package/dist/handlers/validate.test.d.ts.map +0 -1
- package/dist/handlers/validate.test.js +0 -318
- package/dist/handlers/validateSpaceFilter.test.d.ts +0 -2
- package/dist/handlers/validateSpaceFilter.test.d.ts.map +0 -1
- package/dist/handlers/validateSpaceFilter.test.js +0 -79
- package/dist/handlers/warehouseConnection.test.d.ts +0 -2
- package/dist/handlers/warehouseConnection.test.d.ts.map +0 -1
- package/dist/handlers/warehouseConnection.test.js +0 -75
- package/dist/lightdash-config/lightdash-config.test.d.ts +0 -2
- package/dist/lightdash-config/lightdash-config.test.d.ts.map +0 -1
- package/dist/lightdash-config/lightdash-config.test.js +0 -69
- package/dist/prompts/fuzzySearchPrompt.test.d.ts +0 -2
- package/dist/prompts/fuzzySearchPrompt.test.d.ts.map +0 -1
- package/dist/prompts/fuzzySearchPrompt.test.js +0 -53
- package/dist/refsArgument.test.d.ts +0 -2
- package/dist/refsArgument.test.d.ts.map +0 -1
- package/dist/refsArgument.test.js +0 -42
- package/dist/releaseSafety/index.test.d.ts +0 -2
- package/dist/releaseSafety/index.test.d.ts.map +0 -1
- package/dist/releaseSafety/index.test.js +0 -154
- package/dist/releaseSafety/version.test.d.ts +0 -2
- package/dist/releaseSafety/version.test.d.ts.map +0 -1
- package/dist/releaseSafety/version.test.js +0 -22
- package/dist/skills.test.d.ts +0 -2
- package/dist/skills.test.d.ts.map +0 -1
- package/dist/skills.test.js +0 -112
- package/dist/terminal/contentAsCodeOutput.test.d.ts +0 -2
- package/dist/terminal/contentAsCodeOutput.test.d.ts.map +0 -1
- package/dist/terminal/contentAsCodeOutput.test.js +0 -231
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const common_1 = require("@lightdash/common");
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
const yaml = tslib_1.__importStar(require("js-yaml"));
|
|
7
|
-
const os = tslib_1.__importStar(require("os"));
|
|
8
|
-
const path = tslib_1.__importStar(require("path"));
|
|
9
|
-
const vitest_1 = require("vitest");
|
|
10
|
-
const apiClient_1 = require("../dbt/apiClient");
|
|
11
|
-
const users_1 = require("./users");
|
|
12
|
-
vitest_1.vi.mock('../dbt/apiClient', async (importOriginal) => ({
|
|
13
|
-
...(await importOriginal()),
|
|
14
|
-
lightdashApi: vitest_1.vi.fn(),
|
|
15
|
-
}));
|
|
16
|
-
describe('users as code', () => {
|
|
17
|
-
let tmpDir;
|
|
18
|
-
const user = (email, overrides = {}) => ({
|
|
19
|
-
version: 1,
|
|
20
|
-
email,
|
|
21
|
-
disabled: false,
|
|
22
|
-
role: { type: 'system', name: common_1.OrganizationMemberRole.EDITOR },
|
|
23
|
-
...overrides,
|
|
24
|
-
});
|
|
25
|
-
const writeUser = async (filename, value) => {
|
|
26
|
-
const folder = (0, users_1.getUsersFolder)(tmpDir);
|
|
27
|
-
await fs_1.promises.mkdir(folder, { recursive: true });
|
|
28
|
-
await fs_1.promises.writeFile(path.join(folder, filename), yaml.dump(value, { sortKeys: true }));
|
|
29
|
-
};
|
|
30
|
-
beforeEach(async () => {
|
|
31
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockReset();
|
|
32
|
-
tmpDir = await fs_1.promises.mkdtemp(path.join(os.tmpdir(), 'users-test-'));
|
|
33
|
-
});
|
|
34
|
-
afterEach(async () => {
|
|
35
|
-
vitest_1.vi.restoreAllMocks();
|
|
36
|
-
await fs_1.promises.rm(tmpDir, { recursive: true, force: true });
|
|
37
|
-
});
|
|
38
|
-
it('formats upload results using CRUD statuses only', () => {
|
|
39
|
-
expect((0, users_1.formatUserUploadSummary)({
|
|
40
|
-
created: 1,
|
|
41
|
-
updated: 2,
|
|
42
|
-
unchanged: 3,
|
|
43
|
-
awaitingAuthentication: 4,
|
|
44
|
-
invited: 5,
|
|
45
|
-
skippedAuthenticated: 6,
|
|
46
|
-
skippedDisabled: 7,
|
|
47
|
-
skippedValidInvite: 8,
|
|
48
|
-
failed: 9,
|
|
49
|
-
failures: [],
|
|
50
|
-
})).toBe('1 created, 2 updated, 3 unchanged, 9 failed');
|
|
51
|
-
});
|
|
52
|
-
it('downloads deterministic portable users and preserves unknown YAML', async () => {
|
|
53
|
-
const folder = (0, users_1.getUsersFolder)(tmpDir);
|
|
54
|
-
await fs_1.promises.mkdir(folder, { recursive: true });
|
|
55
|
-
await fs_1.promises.writeFile(path.join(folder, 'stale.yml'), 'stale: true');
|
|
56
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockResolvedValueOnce({
|
|
57
|
-
users: [
|
|
58
|
-
user('z@example.com', {
|
|
59
|
-
disabled: true,
|
|
60
|
-
role: { type: 'custom', name: 'Data steward' },
|
|
61
|
-
}),
|
|
62
|
-
user('a@example.com'),
|
|
63
|
-
],
|
|
64
|
-
});
|
|
65
|
-
await expect((0, users_1.downloadUsers)('organization-uuid', tmpDir)).resolves.toBe(2);
|
|
66
|
-
expect((await fs_1.promises.readdir(folder)).sort()).toStrictEqual([
|
|
67
|
-
'a-example-com.yml',
|
|
68
|
-
'stale.yml',
|
|
69
|
-
'z-example-com.yml',
|
|
70
|
-
]);
|
|
71
|
-
expect(yaml.load(await fs_1.promises.readFile(path.join(folder, 'z-example-com.yml'), 'utf8'))).toStrictEqual({
|
|
72
|
-
disabled: true,
|
|
73
|
-
email: 'z@example.com',
|
|
74
|
-
role: { name: 'Data steward', type: 'custom' },
|
|
75
|
-
version: 1,
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
it('rejects case-insensitive duplicate emails before upload', async () => {
|
|
79
|
-
await writeUser('one.yml', user('duplicate@example.com'));
|
|
80
|
-
await writeUser('two.yml', user('DUPLICATE@example.com'));
|
|
81
|
-
await expect((0, users_1.readUserFiles)(tmpDir)).rejects.toThrow('Duplicate user email "duplicate@example.com"');
|
|
82
|
-
expect(apiClient_1.lightdashApi).not.toHaveBeenCalled();
|
|
83
|
-
});
|
|
84
|
-
it('is a backward-compatible no-op when the users folder is missing', async () => {
|
|
85
|
-
await expect((0, users_1.uploadUsers)('organization-uuid', tmpDir)).resolves.toMatchObject({
|
|
86
|
-
created: 0,
|
|
87
|
-
updated: 0,
|
|
88
|
-
unchanged: 0,
|
|
89
|
-
failed: 0,
|
|
90
|
-
});
|
|
91
|
-
expect(apiClient_1.lightdashApi).not.toHaveBeenCalled();
|
|
92
|
-
});
|
|
93
|
-
it('does not request invitations by default', async () => {
|
|
94
|
-
await writeUser('user.yml', user('person@example.com'));
|
|
95
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi)
|
|
96
|
-
.mockResolvedValueOnce({ users: [] })
|
|
97
|
-
.mockResolvedValueOnce({
|
|
98
|
-
action: common_1.PromotionAction.CREATE,
|
|
99
|
-
lifecycle: common_1.UserAsCodeLifecycleStatus.AWAITING_AUTHENTICATION,
|
|
100
|
-
invitation: common_1.UserAsCodeInvitationStatus.NOT_REQUESTED,
|
|
101
|
-
});
|
|
102
|
-
const summary = await (0, users_1.uploadUsers)('organization-uuid', tmpDir);
|
|
103
|
-
expect(summary).toMatchObject({
|
|
104
|
-
created: 1,
|
|
105
|
-
awaitingAuthentication: 1,
|
|
106
|
-
invited: 0,
|
|
107
|
-
failed: 0,
|
|
108
|
-
});
|
|
109
|
-
expect(apiClient_1.lightdashApi).toHaveBeenNthCalledWith(2, {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
url: '/api/v2/orgs/organization-uuid/code/users',
|
|
112
|
-
body: expect.any(String),
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
it('passes sendInvite and reports invitation outcomes', async () => {
|
|
116
|
-
await writeUser('user.yml', user('person@example.com'));
|
|
117
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi)
|
|
118
|
-
.mockResolvedValueOnce({ users: [] })
|
|
119
|
-
.mockResolvedValueOnce({
|
|
120
|
-
action: common_1.PromotionAction.CREATE,
|
|
121
|
-
lifecycle: common_1.UserAsCodeLifecycleStatus.AWAITING_AUTHENTICATION,
|
|
122
|
-
invitation: common_1.UserAsCodeInvitationStatus.SENT,
|
|
123
|
-
});
|
|
124
|
-
const summary = await (0, users_1.uploadUsers)('organization-uuid', tmpDir, true);
|
|
125
|
-
expect(summary.invited).toBe(1);
|
|
126
|
-
expect(apiClient_1.lightdashApi).toHaveBeenNthCalledWith(2, {
|
|
127
|
-
method: 'POST',
|
|
128
|
-
url: '/api/v2/orgs/organization-uuid/code/users?sendInvite=true',
|
|
129
|
-
body: expect.any(String),
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
it('promotes an enabled admin before demoting the current admin', async () => {
|
|
133
|
-
const currentAdmin = user('current@example.com', {
|
|
134
|
-
role: { type: 'system', name: common_1.OrganizationMemberRole.ADMIN },
|
|
135
|
-
});
|
|
136
|
-
const desiredAdmin = user('new@example.com', {
|
|
137
|
-
role: { type: 'system', name: common_1.OrganizationMemberRole.ADMIN },
|
|
138
|
-
});
|
|
139
|
-
const desiredDemotion = user('current@example.com');
|
|
140
|
-
await writeUser('a-demote.yml', desiredDemotion);
|
|
141
|
-
await writeUser('z-promote.yml', desiredAdmin);
|
|
142
|
-
vitest_1.vi.mocked(apiClient_1.lightdashApi)
|
|
143
|
-
.mockResolvedValueOnce({ users: [currentAdmin] })
|
|
144
|
-
.mockResolvedValue({
|
|
145
|
-
action: common_1.PromotionAction.UPDATE,
|
|
146
|
-
lifecycle: common_1.UserAsCodeLifecycleStatus.READY,
|
|
147
|
-
invitation: common_1.UserAsCodeInvitationStatus.NOT_REQUESTED,
|
|
148
|
-
});
|
|
149
|
-
await (0, users_1.uploadUsers)('organization-uuid', tmpDir);
|
|
150
|
-
const firstBody = JSON.parse(vitest_1.vi.mocked(apiClient_1.lightdashApi).mock.calls[1][0].body);
|
|
151
|
-
const secondBody = JSON.parse(vitest_1.vi.mocked(apiClient_1.lightdashApi).mock.calls[2][0].body);
|
|
152
|
-
expect(firstBody.email).toBe('new@example.com');
|
|
153
|
-
expect(secondBody.email).toBe('current@example.com');
|
|
154
|
-
});
|
|
155
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"preAggregateAudit.test.d.ts","sourceRoot":"","sources":["../../src/handlers/preAggregateAudit.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const common_1 = require("@lightdash/common");
|
|
4
|
-
const preAggregateAudit_1 = require("./preAggregateAudit");
|
|
5
|
-
vi.mock('./dbt/apiClient', () => ({
|
|
6
|
-
lightdashApi: vi.fn(),
|
|
7
|
-
checkLightdashVersion: vi.fn(),
|
|
8
|
-
}));
|
|
9
|
-
const { renderSingle, exitIfFailOnMiss } = preAggregateAudit_1.testHelpers;
|
|
10
|
-
const makeAudit = (partial = {}) => ({
|
|
11
|
-
dashboardUuid: 'd-1',
|
|
12
|
-
dashboardSlug: 'd1',
|
|
13
|
-
dashboardName: 'D1',
|
|
14
|
-
tabs: [{ tabUuid: null, tabName: null, tiles: [] }],
|
|
15
|
-
summary: { hitCount: 0, missCount: 0, ineligibleCount: 0 },
|
|
16
|
-
...partial,
|
|
17
|
-
});
|
|
18
|
-
describe('renderSingle JSON mode', () => {
|
|
19
|
-
it('prints DashboardPreAggregateAudit as JSON', () => {
|
|
20
|
-
const audit = makeAudit({
|
|
21
|
-
summary: { hitCount: 2, missCount: 1, ineligibleCount: 0 },
|
|
22
|
-
});
|
|
23
|
-
const spy = vi
|
|
24
|
-
.spyOn(process.stdout, 'write')
|
|
25
|
-
.mockImplementation(() => true);
|
|
26
|
-
renderSingle(audit, { json: true, verbose: false });
|
|
27
|
-
expect(spy).toHaveBeenCalledWith(`${JSON.stringify(audit, null, 2)}\n`);
|
|
28
|
-
spy.mockRestore();
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe('renderSingle human output', () => {
|
|
32
|
-
const missTileWithoutChartName = {
|
|
33
|
-
status: 'miss',
|
|
34
|
-
tileUuid: 't-1',
|
|
35
|
-
tileName: 'Orders over time',
|
|
36
|
-
tileType: common_1.DashboardTileTypes.SAVED_CHART,
|
|
37
|
-
savedChartUuid: 'sc-1',
|
|
38
|
-
exploreName: 'orders',
|
|
39
|
-
miss: { reason: common_1.PreAggregateMissReason.NO_PRE_AGGREGATES_DEFINED },
|
|
40
|
-
missFieldLabel: null,
|
|
41
|
-
};
|
|
42
|
-
it('does not crash when tiles lack chartName/exploreLabel', () => {
|
|
43
|
-
const audit = makeAudit({
|
|
44
|
-
summary: { hitCount: 0, missCount: 1, ineligibleCount: 0 },
|
|
45
|
-
tabs: [
|
|
46
|
-
{
|
|
47
|
-
tabUuid: null,
|
|
48
|
-
tabName: null,
|
|
49
|
-
tiles: [missTileWithoutChartName],
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
});
|
|
53
|
-
const writes = [];
|
|
54
|
-
const spy = vi
|
|
55
|
-
.spyOn(process.stdout, 'write')
|
|
56
|
-
.mockImplementation((chunk) => {
|
|
57
|
-
writes.push(String(chunk));
|
|
58
|
-
return true;
|
|
59
|
-
});
|
|
60
|
-
expect(() => renderSingle(audit, { json: false, verbose: false })).not.toThrow();
|
|
61
|
-
expect(writes.join('')).toContain('Orders over time');
|
|
62
|
-
spy.mockRestore();
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe('exitIfFailOnMiss', () => {
|
|
66
|
-
it('does nothing if flag unset', () => {
|
|
67
|
-
const spy = vi
|
|
68
|
-
.spyOn(process, 'exit')
|
|
69
|
-
.mockImplementation(() => undefined);
|
|
70
|
-
exitIfFailOnMiss([
|
|
71
|
-
makeAudit({
|
|
72
|
-
summary: { hitCount: 0, missCount: 5, ineligibleCount: 0 },
|
|
73
|
-
}),
|
|
74
|
-
], false);
|
|
75
|
-
expect(spy).not.toHaveBeenCalled();
|
|
76
|
-
spy.mockRestore();
|
|
77
|
-
});
|
|
78
|
-
it('exits 1 when flag set and any miss present', () => {
|
|
79
|
-
const spy = vi
|
|
80
|
-
.spyOn(process, 'exit')
|
|
81
|
-
.mockImplementation(() => undefined);
|
|
82
|
-
exitIfFailOnMiss([
|
|
83
|
-
makeAudit({
|
|
84
|
-
summary: { hitCount: 1, missCount: 2, ineligibleCount: 0 },
|
|
85
|
-
}),
|
|
86
|
-
], true);
|
|
87
|
-
expect(spy).toHaveBeenCalledWith(1);
|
|
88
|
-
spy.mockRestore();
|
|
89
|
-
});
|
|
90
|
-
it('does not exit when flag set and no misses', () => {
|
|
91
|
-
const spy = vi
|
|
92
|
-
.spyOn(process, 'exit')
|
|
93
|
-
.mockImplementation(() => undefined);
|
|
94
|
-
exitIfFailOnMiss([
|
|
95
|
-
makeAudit({
|
|
96
|
-
summary: { hitCount: 3, missCount: 0, ineligibleCount: 2 },
|
|
97
|
-
}),
|
|
98
|
-
], true);
|
|
99
|
-
expect(spy).not.toHaveBeenCalled();
|
|
100
|
-
spy.mockRestore();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"renameHandler.test.d.ts","sourceRoot":"","sources":["../../src/handlers/renameHandler.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const common_1 = require("@lightdash/common");
|
|
5
|
-
const analytics_1 = require("../analytics/analytics");
|
|
6
|
-
const config_1 = require("../config");
|
|
7
|
-
const globalState_1 = tslib_1.__importDefault(require("../globalState"));
|
|
8
|
-
const apiClient_1 = require("./dbt/apiClient");
|
|
9
|
-
const refresh_1 = require("./dbt/refresh");
|
|
10
|
-
const renameHandler_1 = require("./renameHandler");
|
|
11
|
-
vi.mock('../analytics/analytics');
|
|
12
|
-
vi.mock('../config');
|
|
13
|
-
vi.mock('./dbt/apiClient');
|
|
14
|
-
vi.mock('./dbt/refresh');
|
|
15
|
-
const baseOptions = {
|
|
16
|
-
verbose: false,
|
|
17
|
-
type: common_1.RenameType.FIELD,
|
|
18
|
-
project: '11111111-1111-4111-8111-111111111111',
|
|
19
|
-
from: 'old_name',
|
|
20
|
-
to: 'new_name',
|
|
21
|
-
dryRun: false,
|
|
22
|
-
assumeYes: true,
|
|
23
|
-
list: false,
|
|
24
|
-
validate: true,
|
|
25
|
-
};
|
|
26
|
-
const emptyResults = {
|
|
27
|
-
charts: [],
|
|
28
|
-
dashboards: [],
|
|
29
|
-
alerts: [],
|
|
30
|
-
dashboardSchedulers: [],
|
|
31
|
-
};
|
|
32
|
-
const RENAME_JOB_ID = 'rename-job-id';
|
|
33
|
-
const VALIDATION_JOB_ID = 'validation-job-id';
|
|
34
|
-
const PREVIEW_PROJECT = '33333333-3333-4333-8333-333333333333';
|
|
35
|
-
describe('renameHandler follow-up validation', () => {
|
|
36
|
-
let errorOutput;
|
|
37
|
-
const mockApi = (failingJobId) => {
|
|
38
|
-
vi.mocked(apiClient_1.lightdashApi).mockImplementation(async ({ method, url }) => {
|
|
39
|
-
if (method === 'POST' && url.endsWith('/rename')) {
|
|
40
|
-
return { jobId: RENAME_JOB_ID };
|
|
41
|
-
}
|
|
42
|
-
if (method === 'POST' && url.endsWith('/validate')) {
|
|
43
|
-
return { jobId: VALIDATION_JOB_ID };
|
|
44
|
-
}
|
|
45
|
-
if (url.includes(`/schedulers/job/${failingJobId}/status`)) {
|
|
46
|
-
return {
|
|
47
|
-
status: common_1.SchedulerJobStatus.ERROR,
|
|
48
|
-
details: { error: 'job blew up' },
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
if (url.includes('/schedulers/job/')) {
|
|
52
|
-
return {
|
|
53
|
-
status: common_1.SchedulerJobStatus.COMPLETED,
|
|
54
|
-
details: { results: emptyResults },
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
if (url.includes('/validate?jobId=')) {
|
|
58
|
-
return [];
|
|
59
|
-
}
|
|
60
|
-
if (method === 'GET' &&
|
|
61
|
-
url === `/api/v1/projects/${PREVIEW_PROJECT}`) {
|
|
62
|
-
return {
|
|
63
|
-
projectUuid: PREVIEW_PROJECT,
|
|
64
|
-
type: common_1.ProjectType.PREVIEW,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
throw new Error(`Unexpected API call: ${method} ${url}`);
|
|
68
|
-
});
|
|
69
|
-
};
|
|
70
|
-
beforeEach(() => {
|
|
71
|
-
vi.clearAllMocks();
|
|
72
|
-
vi.stubEnv('LIGHTDASH_PROJECT', undefined);
|
|
73
|
-
vi.mocked(apiClient_1.checkLightdashVersion).mockResolvedValue(undefined);
|
|
74
|
-
vi.mocked(config_1.getConfig).mockResolvedValue({
|
|
75
|
-
context: {
|
|
76
|
-
apiKey: 'test-key',
|
|
77
|
-
serverUrl: 'http://localhost',
|
|
78
|
-
project: '11111111-1111-4111-8111-111111111111',
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
vi.mocked(refresh_1.getProject).mockResolvedValue({
|
|
82
|
-
name: 'test project',
|
|
83
|
-
});
|
|
84
|
-
errorOutput = [];
|
|
85
|
-
vi.spyOn(console, 'error').mockImplementation((...args) => {
|
|
86
|
-
errorOutput.push(args.map(String).join(' '));
|
|
87
|
-
});
|
|
88
|
-
vi.spyOn(console, 'info').mockImplementation(() => { });
|
|
89
|
-
});
|
|
90
|
-
afterEach(() => {
|
|
91
|
-
vi.restoreAllMocks();
|
|
92
|
-
vi.unstubAllEnvs();
|
|
93
|
-
});
|
|
94
|
-
const trackedEvents = () => vi.mocked(analytics_1.LightdashAnalytics.track).mock.calls.map(([payload]) => ({
|
|
95
|
-
event: payload.event,
|
|
96
|
-
properties: payload.properties,
|
|
97
|
-
}));
|
|
98
|
-
test.each([undefined, baseOptions.project])('targets the environment project unless --project overrides it (%s)', async (explicitProject) => {
|
|
99
|
-
const envProject = '22222222-2222-4222-8222-222222222222';
|
|
100
|
-
vi.stubEnv('LIGHTDASH_PROJECT', envProject);
|
|
101
|
-
vi.spyOn(globalState_1.default, 'isNonInteractive').mockReturnValue(true);
|
|
102
|
-
vi.mocked(config_1.getConfig).mockResolvedValue({
|
|
103
|
-
context: {
|
|
104
|
-
apiKey: 'test-key',
|
|
105
|
-
serverUrl: 'http://localhost',
|
|
106
|
-
project: envProject,
|
|
107
|
-
previewProject: PREVIEW_PROJECT,
|
|
108
|
-
previewName: 'Stale preview',
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
mockApi(null);
|
|
112
|
-
await (0, renameHandler_1.renameHandler)({ ...baseOptions, project: explicitProject });
|
|
113
|
-
const expectedProject = explicitProject || envProject;
|
|
114
|
-
expect(apiClient_1.lightdashApi).toHaveBeenCalledWith(expect.objectContaining({
|
|
115
|
-
method: 'POST',
|
|
116
|
-
url: `/api/v1/projects/${expectedProject}/rename`,
|
|
117
|
-
}));
|
|
118
|
-
const output = errorOutput.join('\n');
|
|
119
|
-
expect(output).toContain(`Renaming in project: ${expectedProject}`);
|
|
120
|
-
if (explicitProject) {
|
|
121
|
-
expect(output).not.toContain('Stale preview');
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
expect(output).toContain('active preview "Stale preview" ignored');
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
test('reports a failed validation job as a validation failure, not a rename failure', async () => {
|
|
128
|
-
mockApi(VALIDATION_JOB_ID);
|
|
129
|
-
await (0, renameHandler_1.renameHandler)({ ...baseOptions });
|
|
130
|
-
const output = errorOutput.join('\n');
|
|
131
|
-
expect(output).toContain('Rename completed');
|
|
132
|
-
expect(output).toContain('Validation failed: job blew up');
|
|
133
|
-
expect(output).not.toContain('Rename failed');
|
|
134
|
-
expect(output).not.toContain('unexpected error');
|
|
135
|
-
});
|
|
136
|
-
test('counts a rename whose follow-up validation failed as completed', async () => {
|
|
137
|
-
mockApi(VALIDATION_JOB_ID);
|
|
138
|
-
await (0, renameHandler_1.renameHandler)({ ...baseOptions });
|
|
139
|
-
expect(trackedEvents()).toEqual([
|
|
140
|
-
{
|
|
141
|
-
event: 'rename.completed',
|
|
142
|
-
properties: expect.objectContaining({
|
|
143
|
-
validationStatus: 'failed',
|
|
144
|
-
}),
|
|
145
|
-
},
|
|
146
|
-
]);
|
|
147
|
-
});
|
|
148
|
-
test('still reports a failed rename job as a rename failure', async () => {
|
|
149
|
-
mockApi(RENAME_JOB_ID);
|
|
150
|
-
await (0, renameHandler_1.renameHandler)({ ...baseOptions });
|
|
151
|
-
const output = errorOutput.join('\n');
|
|
152
|
-
expect(output).toContain('Rename failed: job blew up');
|
|
153
|
-
expect(output).not.toContain('Validation failed');
|
|
154
|
-
expect(trackedEvents()).toEqual([
|
|
155
|
-
{ event: 'rename.error', properties: expect.anything() },
|
|
156
|
-
]);
|
|
157
|
-
});
|
|
158
|
-
test('records a passing validation on the completed event', async () => {
|
|
159
|
-
mockApi(null);
|
|
160
|
-
await (0, renameHandler_1.renameHandler)({ ...baseOptions });
|
|
161
|
-
expect(trackedEvents()).toEqual([
|
|
162
|
-
{
|
|
163
|
-
event: 'rename.completed',
|
|
164
|
-
properties: expect.objectContaining({
|
|
165
|
-
validationStatus: 'passed',
|
|
166
|
-
}),
|
|
167
|
-
},
|
|
168
|
-
]);
|
|
169
|
-
});
|
|
170
|
-
test('does not run the validation job when --validate is not set', async () => {
|
|
171
|
-
mockApi(null);
|
|
172
|
-
await (0, renameHandler_1.renameHandler)({ ...baseOptions, validate: false });
|
|
173
|
-
const validationCalls = vi
|
|
174
|
-
.mocked(apiClient_1.lightdashApi)
|
|
175
|
-
.mock.calls.filter(([{ url }]) => url.endsWith('/validate'));
|
|
176
|
-
expect(validationCalls).toEqual([]);
|
|
177
|
-
const output = errorOutput.join('\n');
|
|
178
|
-
expect(output).not.toContain('failed');
|
|
179
|
-
expect(output).not.toContain('unexpected error');
|
|
180
|
-
expect(trackedEvents()).toEqual([
|
|
181
|
-
{
|
|
182
|
-
event: 'rename.completed',
|
|
183
|
-
properties: expect.objectContaining({
|
|
184
|
-
validationStatus: 'skipped',
|
|
185
|
-
}),
|
|
186
|
-
},
|
|
187
|
-
]);
|
|
188
|
-
});
|
|
189
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolveProjectFlag.test.d.ts","sourceRoot":"","sources":["../../src/handlers/resolveProjectFlag.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const common_1 = require("@lightdash/common");
|
|
4
|
-
const apiClient_1 = require("./dbt/apiClient");
|
|
5
|
-
const resolveProjectFlag_1 = require("./resolveProjectFlag");
|
|
6
|
-
vi.mock('./dbt/apiClient', () => ({
|
|
7
|
-
lightdashApi: vi.fn(),
|
|
8
|
-
}));
|
|
9
|
-
const mockLightdashApi = apiClient_1.lightdashApi;
|
|
10
|
-
const PROJECT_UUID = '00000000-0000-0000-0000-000000000001';
|
|
11
|
-
const OTHER_UUID = '00000000-0000-0000-0000-000000000002';
|
|
12
|
-
const mockOrgProjects = (projects) => {
|
|
13
|
-
mockLightdashApi.mockResolvedValueOnce(projects.map((project) => ({
|
|
14
|
-
...project,
|
|
15
|
-
type: common_1.ProjectType.DEFAULT,
|
|
16
|
-
})));
|
|
17
|
-
};
|
|
18
|
-
describe('resolveProjectFlag', () => {
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
vi.clearAllMocks();
|
|
21
|
-
});
|
|
22
|
-
it('returns a UUID as-is without calling the API', async () => {
|
|
23
|
-
await expect((0, resolveProjectFlag_1.resolveProjectFlag)(PROJECT_UUID)).resolves.toBe(PROJECT_UUID);
|
|
24
|
-
expect(mockLightdashApi).not.toHaveBeenCalled();
|
|
25
|
-
});
|
|
26
|
-
it('resolves a slug to the matching project UUID', async () => {
|
|
27
|
-
mockOrgProjects([
|
|
28
|
-
{ projectUuid: OTHER_UUID, slug: 'other', name: 'Other' },
|
|
29
|
-
{ projectUuid: PROJECT_UUID, slug: 'jaffle-shop', name: 'Jaffle' },
|
|
30
|
-
]);
|
|
31
|
-
await expect((0, resolveProjectFlag_1.resolveProjectFlag)('jaffle-shop')).resolves.toBe(PROJECT_UUID);
|
|
32
|
-
expect(mockLightdashApi).toHaveBeenCalledWith(expect.objectContaining({ url: '/api/v1/org/projects' }));
|
|
33
|
-
});
|
|
34
|
-
it('fails with an actionable error for an unknown slug', async () => {
|
|
35
|
-
mockOrgProjects([
|
|
36
|
-
{ projectUuid: PROJECT_UUID, slug: 'jaffle-shop', name: 'Jaffle' },
|
|
37
|
-
{ projectUuid: OTHER_UUID, name: 'No slug' },
|
|
38
|
-
]);
|
|
39
|
-
await expect((0, resolveProjectFlag_1.resolveProjectFlag)('missing')).rejects.toThrow(common_1.ParameterError);
|
|
40
|
-
});
|
|
41
|
-
it('fails when a slug matches more than one project', async () => {
|
|
42
|
-
mockOrgProjects([
|
|
43
|
-
{ projectUuid: PROJECT_UUID, slug: 'dup', name: 'A' },
|
|
44
|
-
{ projectUuid: OTHER_UUID, slug: 'dup', name: 'B' },
|
|
45
|
-
]);
|
|
46
|
-
await expect((0, resolveProjectFlag_1.resolveProjectFlag)('dup')).rejects.toThrow(/more than one project/);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"selectProject.test.d.ts","sourceRoot":"","sources":["../../src/handlers/selectProject.test.ts"],"names":[],"mappings":""}
|