@datalayer/core 0.0.10 → 0.0.11
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/lib/__tests__/shared/cleanup-shared.d.ts +4 -0
- package/lib/__tests__/shared/cleanup-shared.js +228 -0
- package/lib/__tests__/shared/test-config.d.ts +51 -0
- package/lib/__tests__/shared/test-config.js +110 -0
- package/lib/__tests__/shared/test-constants.d.ts +66 -0
- package/lib/__tests__/shared/test-constants.js +79 -0
- package/lib/api/DatalayerApi.d.ts +1 -1
- package/lib/api/DatalayerApi.js +73 -42
- package/lib/api/__tests__/iam.authentication.integration.test.d.ts +1 -0
- package/lib/api/__tests__/iam.authentication.integration.test.js +247 -0
- package/lib/api/__tests__/iam.healthz.integration.test.d.ts +1 -0
- package/lib/api/__tests__/iam.healthz.integration.test.js +63 -0
- package/lib/api/__tests__/iam.profile.integration.test.d.ts +1 -0
- package/lib/api/__tests__/iam.profile.integration.test.js +252 -0
- package/lib/api/__tests__/runtimes.environments.integration.test.d.ts +1 -0
- package/lib/api/__tests__/runtimes.environments.integration.test.js +122 -0
- package/lib/api/__tests__/runtimes.healthz.integration.test.d.ts +1 -0
- package/lib/api/__tests__/runtimes.healthz.integration.test.js +50 -0
- package/lib/api/__tests__/runtimes.integration.test.d.ts +1 -0
- package/lib/api/__tests__/runtimes.integration.test.js +369 -0
- package/lib/api/__tests__/spacer.healthz.integration.test.d.ts +1 -0
- package/lib/api/__tests__/spacer.healthz.integration.test.js +50 -0
- package/lib/api/__tests__/spacer.integration.test.d.ts +1 -0
- package/lib/api/__tests__/spacer.integration.test.js +519 -0
- package/lib/api/constants.d.ts +19 -0
- package/lib/api/constants.js +23 -0
- package/lib/api/iam/__tests__/authentication.unit.test.d.ts +1 -0
- package/lib/api/iam/__tests__/authentication.unit.test.js +63 -0
- package/lib/api/iam/__tests__/healthz.unit.test.d.ts +1 -0
- package/lib/api/iam/__tests__/healthz.unit.test.js +60 -0
- package/lib/api/iam/__tests__/profile.unit.test.d.ts +1 -0
- package/lib/api/iam/__tests__/profile.unit.test.js +57 -0
- package/lib/api/iam/authentication.d.ts +40 -0
- package/lib/api/iam/authentication.js +128 -0
- package/lib/api/iam/healthz.d.ts +15 -0
- package/lib/api/iam/healthz.js +43 -0
- package/lib/api/iam/index.d.ts +12 -0
- package/lib/api/iam/index.js +17 -0
- package/lib/api/iam/profile.d.ts +15 -0
- package/lib/api/iam/profile.js +41 -0
- package/lib/api/index.d.ts +20 -3
- package/lib/api/index.js +22 -3
- package/lib/api/runtimes/__tests__/environments.unit.test.d.ts +1 -0
- package/lib/api/runtimes/__tests__/environments.unit.test.js +77 -0
- package/lib/api/runtimes/__tests__/healthz.unit.test.d.ts +1 -0
- package/lib/api/runtimes/__tests__/healthz.unit.test.js +57 -0
- package/lib/api/runtimes/__tests__/runtimes.unit.test.d.ts +1 -0
- package/lib/api/runtimes/__tests__/runtimes.unit.test.js +139 -0
- package/lib/api/runtimes/__tests__/snapshots.unit.test.d.ts +1 -0
- package/lib/api/runtimes/__tests__/snapshots.unit.test.js +96 -0
- package/lib/api/runtimes/environments.d.ts +9 -0
- package/lib/api/runtimes/environments.js +28 -0
- package/lib/api/runtimes/healthz.d.ts +25 -0
- package/lib/api/runtimes/healthz.js +43 -0
- package/lib/api/runtimes/index.d.ts +10 -5
- package/lib/api/runtimes/index.js +10 -5
- package/lib/api/runtimes/runtimes.d.ts +54 -0
- package/lib/api/runtimes/runtimes.js +169 -0
- package/lib/api/runtimes/snapshots.d.ts +34 -21
- package/lib/api/runtimes/snapshots.js +69 -138
- package/lib/api/spacer/__tests__/healthz.unit.test.d.ts +1 -0
- package/lib/api/spacer/__tests__/healthz.unit.test.js +57 -0
- package/lib/api/spacer/__tests__/items.unit.test.d.ts +1 -0
- package/lib/api/spacer/__tests__/items.unit.test.js +165 -0
- package/lib/api/spacer/__tests__/lexicals.unit.test.d.ts +1 -0
- package/lib/api/spacer/__tests__/lexicals.unit.test.js +323 -0
- package/lib/api/spacer/__tests__/notebooks.unit.test.d.ts +1 -0
- package/lib/api/spacer/__tests__/notebooks.unit.test.js +224 -0
- package/lib/api/spacer/__tests__/users.unit.test.d.ts +1 -0
- package/lib/api/spacer/__tests__/users.unit.test.js +132 -0
- package/lib/api/spacer/healthz.d.ts +25 -0
- package/lib/api/spacer/healthz.js +43 -0
- package/lib/api/spacer/index.d.ts +13 -0
- package/lib/api/spacer/index.js +17 -0
- package/lib/api/spacer/items.d.ts +17 -0
- package/lib/api/spacer/items.js +40 -0
- package/lib/api/spacer/lexicals.d.ts +26 -0
- package/lib/api/spacer/lexicals.js +74 -0
- package/lib/api/spacer/notebooks.d.ts +26 -0
- package/lib/api/spacer/notebooks.js +74 -0
- package/lib/api/spacer/spaces.d.ts +9 -0
- package/lib/api/spacer/spaces.js +29 -0
- package/lib/api/spacer/users.d.ts +9 -0
- package/lib/api/spacer/users.js +28 -0
- package/lib/api/types/iam.d.ts +180 -0
- package/lib/api/types/index.d.ts +32 -0
- package/lib/api/types/index.js +36 -0
- package/lib/api/types/runtimes.d.ts +235 -0
- package/lib/api/types/runtimes.js +5 -0
- package/lib/api/types/spacer.d.ts +271 -0
- package/lib/api/types/spacer.js +5 -0
- package/lib/api/utils/__tests__/validation.test.d.ts +1 -0
- package/lib/api/utils/__tests__/validation.test.js +109 -0
- package/lib/api/utils/validation.d.ts +24 -0
- package/lib/api/utils/validation.js +133 -0
- package/lib/components/progress/CreditsIndicator.d.ts +1 -1
- package/lib/components/runtimes/RuntimeCellVariablesDialog.js +1 -1
- package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +1 -1
- package/lib/components/runtimes/RuntimeLauncherDialog.js +2 -1
- package/lib/components/runtimes/RuntimePickerBase.d.ts +1 -1
- package/lib/components/runtimes/RuntimePickerBase.js +1 -1
- package/lib/components/runtimes/RuntimePickerCell.js +2 -1
- package/lib/components/runtimes/RuntimePickerNotebook.d.ts +1 -1
- package/lib/components/runtimes/RuntimePickerNotebook.js +1 -1
- package/lib/components/runtimes/RuntimeSimplePicker.js +2 -1
- package/lib/components/runtimes/RuntimeTransfer.d.ts +1 -1
- package/lib/components/runtimes/RuntimeUtils.d.ts +1 -1
- package/lib/components/snapshots/RuntimeSnapshotMenu.d.ts +1 -1
- package/lib/components/snapshots/RuntimeSnapshotMenu.js +1 -1
- package/lib/hooks/useDatalayer.d.ts +1 -1
- package/lib/hooks/useDatalayer.js +1 -1
- package/lib/hooks/useIAM.js +1 -1
- package/lib/hooks/useRuntimes.js +1 -1
- package/lib/index.d.ts +9 -0
- package/lib/index.js +10 -0
- package/lib/sdk/client/__tests__/sdk.health.integration.test.d.ts +1 -0
- package/lib/sdk/client/__tests__/sdk.health.integration.test.js +110 -0
- package/lib/sdk/client/__tests__/sdk.iam.integration.test.d.ts +1 -0
- package/lib/sdk/client/__tests__/sdk.iam.integration.test.js +179 -0
- package/lib/sdk/client/__tests__/sdk.models.integration.test.d.ts +1 -0
- package/lib/sdk/client/__tests__/sdk.models.integration.test.js +376 -0
- package/lib/sdk/client/__tests__/sdk.runtimes.integration.test.d.ts +1 -0
- package/lib/sdk/client/__tests__/sdk.runtimes.integration.test.js +276 -0
- package/lib/sdk/client/__tests__/sdk.spacer.integration.test.d.ts +1 -0
- package/lib/sdk/client/__tests__/sdk.spacer.integration.test.js +361 -0
- package/lib/sdk/client/base.d.ts +88 -0
- package/lib/sdk/client/base.js +112 -0
- package/lib/sdk/client/index.d.ts +192 -0
- package/lib/sdk/client/index.js +128 -0
- package/lib/sdk/client/mixins/HealthMixin.d.ts +100 -0
- package/lib/sdk/client/mixins/HealthMixin.js +133 -0
- package/lib/sdk/client/mixins/IAMMixin.d.ts +59 -0
- package/lib/sdk/client/mixins/IAMMixin.js +83 -0
- package/lib/sdk/client/mixins/RuntimesMixin.d.ts +134 -0
- package/lib/sdk/client/mixins/RuntimesMixin.js +221 -0
- package/lib/sdk/client/mixins/SpacerMixin.d.ts +184 -0
- package/lib/sdk/client/mixins/SpacerMixin.js +278 -0
- package/lib/sdk/client/models/Lexical.d.ts +156 -0
- package/lib/sdk/client/models/Lexical.js +275 -0
- package/lib/sdk/client/models/Notebook.d.ts +174 -0
- package/lib/sdk/client/models/Notebook.js +311 -0
- package/lib/sdk/client/models/Runtime.d.ts +221 -0
- package/lib/sdk/client/models/Runtime.js +341 -0
- package/lib/sdk/client/models/Snapshot.d.ts +156 -0
- package/lib/sdk/client/models/Snapshot.js +244 -0
- package/lib/sdk/client/models/Space.d.ts +182 -0
- package/lib/sdk/client/models/Space.js +276 -0
- package/lib/sdk/client/models/__tests__/Lexical.test.d.ts +1 -0
- package/lib/sdk/client/models/__tests__/Lexical.test.js +288 -0
- package/lib/sdk/client/models/__tests__/Notebook.test.d.ts +1 -0
- package/lib/sdk/client/models/__tests__/Notebook.test.js +206 -0
- package/lib/sdk/client/models/__tests__/Runtime.test.d.ts +1 -0
- package/lib/sdk/client/models/__tests__/Runtime.test.js +133 -0
- package/lib/sdk/client/models/__tests__/Snapshot.test.d.ts +1 -0
- package/lib/sdk/client/models/__tests__/Snapshot.test.js +244 -0
- package/lib/sdk/client/models/__tests__/Space.test.d.ts +1 -0
- package/lib/sdk/client/models/__tests__/Space.test.js +334 -0
- package/lib/sdk/client/models/index.d.ts +30 -0
- package/lib/sdk/client/models/index.js +30 -0
- package/lib/sdk/client/utils/mixins.d.ts +42 -0
- package/lib/sdk/client/utils/mixins.js +47 -0
- package/lib/sdk/index.d.ts +26 -0
- package/lib/sdk/index.js +32 -0
- package/lib/sdk/stateful/index.d.ts +3 -0
- package/lib/sdk/stateful/index.js +7 -0
- package/lib/{api → sdk/stateful}/runtimes/actions.d.ts +1 -1
- package/lib/{api → sdk/stateful}/runtimes/actions.js +3 -3
- package/lib/{api → sdk/stateful}/runtimes/apis.d.ts +1 -1
- package/lib/sdk/stateful/runtimes/apis.js +5 -0
- package/lib/sdk/stateful/runtimes/index.d.ts +5 -0
- package/lib/sdk/stateful/runtimes/index.js +9 -0
- package/lib/sdk/stateful/runtimes/snapshots.d.ts +25 -0
- package/lib/sdk/stateful/runtimes/snapshots.js +150 -0
- package/lib/services/DatalayerServiceManager.js +1 -1
- package/lib/state/substates/IAMState.js +1 -1
- package/lib/state/substates/RuntimesState.d.ts +1 -1
- package/lib/state/substates/RuntimesState.js +1 -1
- package/lib/state/substates/SurveysState.js +1 -1
- package/lib/test-setup.js +1 -0
- package/package.json +19 -9
- /package/lib/api/{runtimes/apis.js → types/iam.js} +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/exec/Python.d.ts +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/exec/Python.js +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/exec/Snippets.d.ts +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/exec/Snippets.js +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/exec/index.d.ts +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/exec/index.js +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/index.d.ts +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/index.js +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/kernelsHandler.d.ts +0 -0
- /package/lib/{api → sdk/stateful}/jupyter/kernelsHandler.js +0 -0
- /package/lib/{api → sdk/stateful}/runtimes/settings.d.ts +0 -0
- /package/lib/{api → sdk/stateful}/runtimes/settings.js +0 -0
- /package/lib/{api → sdk/stateful}/runtimes/utils.d.ts +0 -0
- /package/lib/{api → sdk/stateful}/runtimes/utils.js +0 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import { snapshots, runtimes } from '../../api/runtimes';
|
|
6
|
+
import { users, items } from '../../api/spacer';
|
|
7
|
+
import { testConfig } from './test-config';
|
|
8
|
+
/**
|
|
9
|
+
* Shared cleanup logic for both setup and teardown
|
|
10
|
+
*/
|
|
11
|
+
export async function performCleanup(phase) {
|
|
12
|
+
const phaseLabel = phase === 'setup' ? 'PRE-TEST' : 'POST-TEST';
|
|
13
|
+
console.log('='.repeat(60));
|
|
14
|
+
console.log(`${phaseLabel} CLEANUP: Starting ${phase === 'setup' ? 'pre' : 'post'}-test cleanup...`);
|
|
15
|
+
console.log('='.repeat(60));
|
|
16
|
+
// Check if we have a token
|
|
17
|
+
if (!testConfig.hasToken()) {
|
|
18
|
+
console.log('WARNING: No Datalayer API token configured - skipping cleanup');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const DATALAYER_TOKEN = testConfig.getToken();
|
|
22
|
+
const RUNTIMES_BASE_URL = testConfig.getBaseUrl('RUNTIMES');
|
|
23
|
+
const SPACER_BASE_URL = testConfig.getBaseUrl('SPACER');
|
|
24
|
+
// Clean up runtimes
|
|
25
|
+
await cleanupRuntimes(DATALAYER_TOKEN, RUNTIMES_BASE_URL, phaseLabel);
|
|
26
|
+
// Clean up test snapshots
|
|
27
|
+
await cleanupTestSnapshots(DATALAYER_TOKEN, RUNTIMES_BASE_URL, phaseLabel);
|
|
28
|
+
// Clean up ALL spacer items (notebooks, lexicals, etc.)
|
|
29
|
+
await cleanupSpacerItems(DATALAYER_TOKEN, SPACER_BASE_URL, phaseLabel);
|
|
30
|
+
// Final verification (only for teardown)
|
|
31
|
+
if (phase === 'teardown') {
|
|
32
|
+
await verifyFinalState(DATALAYER_TOKEN, RUNTIMES_BASE_URL, SPACER_BASE_URL);
|
|
33
|
+
}
|
|
34
|
+
console.log('='.repeat(60));
|
|
35
|
+
console.log(`${phaseLabel} CLEANUP: ${phase === 'setup' ? 'Pre' : 'Post'}-test cleanup completed`);
|
|
36
|
+
console.log('='.repeat(60));
|
|
37
|
+
}
|
|
38
|
+
async function cleanupRuntimes(token, baseUrl, phaseLabel) {
|
|
39
|
+
try {
|
|
40
|
+
console.log(`Cleaning up ${phaseLabel === 'PRE-TEST' ? 'existing' : 'test'} runtimes...`);
|
|
41
|
+
const runtimesResponse = await runtimes.listRuntimes(token, baseUrl);
|
|
42
|
+
if (!runtimesResponse.runtimes || runtimesResponse.runtimes.length === 0) {
|
|
43
|
+
console.log('✓ No runtimes to clean up');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.log(`Found ${runtimesResponse.runtimes.length} runtime(s) to clean up`);
|
|
47
|
+
let removedCount = 0;
|
|
48
|
+
let failedCount = 0;
|
|
49
|
+
for (const runtime of runtimesResponse.runtimes) {
|
|
50
|
+
try {
|
|
51
|
+
await runtimes.deleteRuntime(token, runtime.pod_name, baseUrl);
|
|
52
|
+
console.log(` ✓ Removed runtime: ${runtime.pod_name}`);
|
|
53
|
+
removedCount++;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.log(` ✗ Failed to remove runtime ${runtime.pod_name}: ${error.message}`);
|
|
57
|
+
failedCount++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (removedCount > 0 || failedCount > 0) {
|
|
61
|
+
console.log(`Runtime cleanup summary: ${removedCount} removed, ${failedCount} failed`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error('Error cleaning up runtimes:', error.message);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function cleanupTestSnapshots(token, baseUrl, phaseLabel) {
|
|
69
|
+
try {
|
|
70
|
+
console.log(`Cleaning up ALL snapshots${phaseLabel === 'POST-TEST' ? ' created during tests' : ''}...`);
|
|
71
|
+
const snapshotsResponse = await snapshots.listSnapshots(token, baseUrl);
|
|
72
|
+
if (!snapshotsResponse.snapshots ||
|
|
73
|
+
snapshotsResponse.snapshots.length === 0) {
|
|
74
|
+
console.log('✓ No snapshots found');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Filter out already deleted snapshots
|
|
78
|
+
const allSnapshots = snapshotsResponse.snapshots.filter(s => s.status !== 'deleted');
|
|
79
|
+
if (allSnapshots.length === 0) {
|
|
80
|
+
const deletedCount = snapshotsResponse.snapshots.filter(s => s.status === 'deleted').length;
|
|
81
|
+
if (deletedCount > 0) {
|
|
82
|
+
console.log(`Found ${deletedCount} already deleted snapshot(s), skipping`);
|
|
83
|
+
}
|
|
84
|
+
console.log('✓ No active snapshots to clean up');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.log(`Found ${allSnapshots.length} active snapshot(s) to clean up`);
|
|
88
|
+
let removedCount = 0;
|
|
89
|
+
let skippedCount = 0;
|
|
90
|
+
let failedCount = 0;
|
|
91
|
+
for (const snapshot of allSnapshots) {
|
|
92
|
+
try {
|
|
93
|
+
console.log(` Attempting to delete: ${snapshot.name} (uid: ${snapshot.uid}, status: ${snapshot.status})`);
|
|
94
|
+
await snapshots.deleteSnapshot(token, snapshot.uid, baseUrl);
|
|
95
|
+
console.log(` ✓ Removed snapshot: ${snapshot.name}`);
|
|
96
|
+
removedCount++;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error.message?.includes('404') ||
|
|
100
|
+
error.message?.includes('not found')) {
|
|
101
|
+
console.log(` - Already removed: ${snapshot.name}`);
|
|
102
|
+
skippedCount++;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.log(` ✗ Failed to remove snapshot ${snapshot.name}: ${error.message}`);
|
|
106
|
+
failedCount++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (removedCount > 0 || skippedCount > 0 || failedCount > 0) {
|
|
111
|
+
console.log(`Snapshot cleanup summary: ${removedCount} removed, ${skippedCount} skipped, ${failedCount} failed`);
|
|
112
|
+
// Verify snapshots are actually deleted
|
|
113
|
+
console.log('Verifying snapshot deletion...');
|
|
114
|
+
const verifyResponse = await snapshots.listSnapshots(token, baseUrl);
|
|
115
|
+
const remainingCount = verifyResponse.snapshots?.length || 0;
|
|
116
|
+
console.log(` Remaining snapshots after cleanup: ${remainingCount}`);
|
|
117
|
+
if (remainingCount > 0 && verifyResponse.snapshots) {
|
|
118
|
+
console.log(' Still present:');
|
|
119
|
+
for (const snap of verifyResponse.snapshots.slice(0, 5)) {
|
|
120
|
+
console.log(` - ${snap.name} (uid: ${snap.uid}, status: ${snap.status})`);
|
|
121
|
+
}
|
|
122
|
+
if (verifyResponse.snapshots.length > 5) {
|
|
123
|
+
console.log(` ... and ${verifyResponse.snapshots.length - 5} more`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error('Error cleaning up snapshots:', error.message);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function cleanupSpacerItems(token, baseUrl, phaseLabel) {
|
|
133
|
+
try {
|
|
134
|
+
console.log(`Cleaning up ALL spacer items${phaseLabel === 'POST-TEST' ? ' (complete cleanup)' : ''}...`);
|
|
135
|
+
// Get user spaces
|
|
136
|
+
const spacesResponse = await users.getMySpaces(token, baseUrl);
|
|
137
|
+
if (!spacesResponse.spaces || spacesResponse.spaces.length === 0) {
|
|
138
|
+
console.log('✓ No spaces found');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
console.log(`Found ${spacesResponse.spaces.length} space(s)`);
|
|
142
|
+
let totalRemovedCount = 0;
|
|
143
|
+
let totalFailedCount = 0;
|
|
144
|
+
// Go through each space and clean up test items
|
|
145
|
+
for (const space of spacesResponse.spaces) {
|
|
146
|
+
try {
|
|
147
|
+
const itemsResponse = await items.getSpaceItems(token, space.uid, baseUrl);
|
|
148
|
+
if (!itemsResponse.items || itemsResponse.items.length === 0) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const spaceName = space.name || space.name_t || 'Unknown Space';
|
|
152
|
+
console.log(` Space "${spaceName}": Found ${itemsResponse.items.length} item(s) to clean up`);
|
|
153
|
+
for (const item of itemsResponse.items) {
|
|
154
|
+
try {
|
|
155
|
+
// API returns uid field but TypeScript interface doesn't include it
|
|
156
|
+
await items.deleteItem(token, item.uid, baseUrl);
|
|
157
|
+
console.log(` ✓ Removed item: ${item.name || item.name_t}`);
|
|
158
|
+
totalRemovedCount++;
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
console.log(` ✗ Failed to remove item ${item.name || item.name_t}: ${error.message}`);
|
|
162
|
+
totalFailedCount++;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
const spaceName = space.name || space.name_t || 'Unknown Space';
|
|
168
|
+
console.log(` Error processing space ${spaceName}: ${error.message}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (totalRemovedCount > 0 || totalFailedCount > 0) {
|
|
172
|
+
console.log(`Spacer items cleanup summary: ${totalRemovedCount} removed, ${totalFailedCount} failed`);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.log('✓ No spacer items to clean up');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
console.error('Error cleaning up spacer items:', error.message);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async function verifyFinalState(token, runtimesBaseUrl, spacerBaseUrl) {
|
|
183
|
+
try {
|
|
184
|
+
const finalRuntimes = await runtimes.listRuntimes(token, runtimesBaseUrl);
|
|
185
|
+
const finalSnapshots = await snapshots.listSnapshots(token, runtimesBaseUrl);
|
|
186
|
+
const remainingRuntimes = finalRuntimes.runtimes?.length || 0;
|
|
187
|
+
const remainingSnapshots = (finalSnapshots.snapshots || []).filter(s => s.status !== 'deleted').length;
|
|
188
|
+
// Count spacer test items
|
|
189
|
+
let remainingTestItems = 0;
|
|
190
|
+
try {
|
|
191
|
+
const spacesResponse = await users.getMySpaces(token, spacerBaseUrl);
|
|
192
|
+
if (spacesResponse.spaces) {
|
|
193
|
+
for (const space of spacesResponse.spaces) {
|
|
194
|
+
try {
|
|
195
|
+
const itemsResponse = await items.getSpaceItems(token, space.uid, spacerBaseUrl);
|
|
196
|
+
if (itemsResponse.items) {
|
|
197
|
+
remainingTestItems += itemsResponse.items.length;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
// Ignore errors for individual spaces
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
// Ignore errors
|
|
208
|
+
}
|
|
209
|
+
console.log('-'.repeat(60));
|
|
210
|
+
console.log('Final state after all tests:');
|
|
211
|
+
console.log(` - Remaining runtimes: ${remainingRuntimes}`);
|
|
212
|
+
console.log(` - Remaining active snapshots: ${remainingSnapshots}`);
|
|
213
|
+
console.log(` - Total snapshots: ${finalSnapshots.snapshots?.length || 0}`);
|
|
214
|
+
console.log(` - Remaining spacer items: ${remainingTestItems}`);
|
|
215
|
+
if (remainingRuntimes > 0) {
|
|
216
|
+
console.log(`\nNOTE: ${remainingRuntimes} runtime(s) still active. These may require manual cleanup.`);
|
|
217
|
+
}
|
|
218
|
+
if (remainingSnapshots > 0) {
|
|
219
|
+
console.log(`\nNOTE: ${remainingSnapshots} snapshot(s) still active. These may require manual cleanup.`);
|
|
220
|
+
}
|
|
221
|
+
if (remainingTestItems > 0) {
|
|
222
|
+
console.log(`\nNOTE: ${remainingTestItems} spacer item(s) still active. These may require manual cleanup.`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
console.error('Error during final verification:', error.message);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test configuration object with proper fallbacks
|
|
3
|
+
*/
|
|
4
|
+
export declare const testConfig: {
|
|
5
|
+
/**
|
|
6
|
+
* Check if we have a token available
|
|
7
|
+
*/
|
|
8
|
+
hasToken(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Get the Datalayer API token for testing
|
|
11
|
+
* Priority order:
|
|
12
|
+
* 1. DATALAYER_TEST_TOKEN environment variable
|
|
13
|
+
* 2. DATALAYER_API_TOKEN environment variable
|
|
14
|
+
* 3. VITE_DATALAYER_API_TOKEN environment variable
|
|
15
|
+
* 4. Throw error if not found
|
|
16
|
+
*/
|
|
17
|
+
getToken(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Get the base URL for testing
|
|
20
|
+
* Uses DEFAULT_SERVICE_URLS or can be overridden via environment
|
|
21
|
+
*/
|
|
22
|
+
getBaseUrl(service?: "IAM" | "RUNTIMES" | "SPACER"): string;
|
|
23
|
+
/**
|
|
24
|
+
* Check if expensive tests should be skipped
|
|
25
|
+
* Default is false (run expensive tests) unless explicitly set to 'true'
|
|
26
|
+
*/
|
|
27
|
+
shouldSkipExpensive(): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Get test environment names for runtime creation
|
|
30
|
+
*/
|
|
31
|
+
getTestEnvironments(): {
|
|
32
|
+
python: string;
|
|
33
|
+
ai: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Check if debug output is enabled
|
|
37
|
+
*/
|
|
38
|
+
isDebugEnabled(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Get custom timeout for API calls (in milliseconds)
|
|
41
|
+
*/
|
|
42
|
+
getTimeout(): number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Helper function to skip tests if no token is available
|
|
46
|
+
*/
|
|
47
|
+
export declare const skipIfNoToken: () => boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Log debug information if debug is enabled
|
|
50
|
+
*/
|
|
51
|
+
export declare const debugLog: (...args: any[]) => void;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
import * as dotenv from 'dotenv';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { DEFAULT_SERVICE_URLS } from '../../api/constants';
|
|
8
|
+
/**
|
|
9
|
+
* Load test environment configuration
|
|
10
|
+
* Loads from .env.test file and falls back to environment variables
|
|
11
|
+
*/
|
|
12
|
+
const loadTestConfig = () => {
|
|
13
|
+
// Load .env.test file from project root
|
|
14
|
+
const testEnvPath = path.resolve(process.cwd(), '.env.test');
|
|
15
|
+
dotenv.config({ path: testEnvPath });
|
|
16
|
+
// Also load regular .env as fallback
|
|
17
|
+
dotenv.config();
|
|
18
|
+
};
|
|
19
|
+
// Load config immediately when module is imported
|
|
20
|
+
loadTestConfig();
|
|
21
|
+
/**
|
|
22
|
+
* Test configuration object with proper fallbacks
|
|
23
|
+
*/
|
|
24
|
+
export const testConfig = {
|
|
25
|
+
/**
|
|
26
|
+
* Check if we have a token available
|
|
27
|
+
*/
|
|
28
|
+
hasToken() {
|
|
29
|
+
return !!(process.env.DATALAYER_TEST_TOKEN ||
|
|
30
|
+
process.env.DATALAYER_API_TOKEN ||
|
|
31
|
+
process.env.VITE_DATALAYER_API_TOKEN);
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* Get the Datalayer API token for testing
|
|
35
|
+
* Priority order:
|
|
36
|
+
* 1. DATALAYER_TEST_TOKEN environment variable
|
|
37
|
+
* 2. DATALAYER_API_TOKEN environment variable
|
|
38
|
+
* 3. VITE_DATALAYER_API_TOKEN environment variable
|
|
39
|
+
* 4. Throw error if not found
|
|
40
|
+
*/
|
|
41
|
+
getToken() {
|
|
42
|
+
const token = process.env.DATALAYER_TEST_TOKEN ||
|
|
43
|
+
process.env.DATALAYER_API_TOKEN ||
|
|
44
|
+
process.env.VITE_DATALAYER_API_TOKEN;
|
|
45
|
+
if (!token) {
|
|
46
|
+
throw new Error('No Datalayer API token found. Please set DATALAYER_TEST_TOKEN, DATALAYER_API_TOKEN, or VITE_DATALAYER_API_TOKEN environment variable');
|
|
47
|
+
}
|
|
48
|
+
return token;
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* Get the base URL for testing
|
|
52
|
+
* Uses DEFAULT_SERVICE_URLS or can be overridden via environment
|
|
53
|
+
*/
|
|
54
|
+
getBaseUrl(service = 'IAM') {
|
|
55
|
+
const envBaseUrl = process.env.DATALAYER_TEST_BASE_URL;
|
|
56
|
+
if (envBaseUrl) {
|
|
57
|
+
return envBaseUrl;
|
|
58
|
+
}
|
|
59
|
+
return DEFAULT_SERVICE_URLS[service];
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* Check if expensive tests should be skipped
|
|
63
|
+
* Default is false (run expensive tests) unless explicitly set to 'true'
|
|
64
|
+
*/
|
|
65
|
+
shouldSkipExpensive() {
|
|
66
|
+
return process.env.DATALAYER_TEST_SKIP_EXPENSIVE === 'true';
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* Get test environment names for runtime creation
|
|
70
|
+
*/
|
|
71
|
+
getTestEnvironments() {
|
|
72
|
+
return {
|
|
73
|
+
python: process.env.DATALAYER_TEST_PYTHON_ENV || 'python-cpu-env',
|
|
74
|
+
ai: process.env.DATALAYER_TEST_AI_ENV || 'ai-env',
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* Check if debug output is enabled
|
|
79
|
+
*/
|
|
80
|
+
isDebugEnabled() {
|
|
81
|
+
return process.env.DATALAYER_TEST_DEBUG === 'true';
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* Get custom timeout for API calls (in milliseconds)
|
|
85
|
+
*/
|
|
86
|
+
getTimeout() {
|
|
87
|
+
const timeout = process.env.DATALAYER_TEST_TIMEOUT;
|
|
88
|
+
return timeout ? parseInt(timeout, 10) : 60000; // Default 60 seconds
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Helper function to skip tests if no token is available
|
|
93
|
+
*/
|
|
94
|
+
export const skipIfNoToken = () => {
|
|
95
|
+
try {
|
|
96
|
+
testConfig.getToken();
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Log debug information if debug is enabled
|
|
105
|
+
*/
|
|
106
|
+
export const debugLog = (...args) => {
|
|
107
|
+
if (testConfig.isDebugEnabled()) {
|
|
108
|
+
console.log('[DEBUG]', ...args);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test constants for API unit tests
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* A valid mock JWT token for testing
|
|
6
|
+
* This token has the correct JWT structure (header.payload.signature) but is not cryptographically valid
|
|
7
|
+
*
|
|
8
|
+
* Decoded header: {"alg":"HS256","typ":"JWT"}
|
|
9
|
+
* Decoded payload: {"sub":"test-user","iss":"test-issuer","iat":1700000000,"exp":9999999999,"aud":"test-audience"}
|
|
10
|
+
*
|
|
11
|
+
* The token is constructed to:
|
|
12
|
+
* - Have three parts separated by dots
|
|
13
|
+
* - Have valid base64url encoded parts
|
|
14
|
+
* - Have proper JWT header with alg and typ
|
|
15
|
+
* - Have common JWT claims in payload
|
|
16
|
+
* - Have an expiration far in the future to avoid expiration errors
|
|
17
|
+
* - Be long enough to pass length validation (>100 chars)
|
|
18
|
+
*/
|
|
19
|
+
export declare const MOCK_JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXIiLCJpc3MiOiJ0ZXN0LWlzc3VlciIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjo5OTk5OTk5OTk5LCJhdWQiOiJ0ZXN0LWF1ZGllbmNlIn0.abcdefghijklmnopqrstuvwxyz012345678901234567890";
|
|
20
|
+
/**
|
|
21
|
+
* An invalid token (not JWT format) for negative testing
|
|
22
|
+
*/
|
|
23
|
+
export declare const INVALID_TOKEN = "invalid-token";
|
|
24
|
+
/**
|
|
25
|
+
* A malformed JWT token (only two parts instead of three)
|
|
26
|
+
*/
|
|
27
|
+
export declare const MALFORMED_JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXIifQ";
|
|
28
|
+
/**
|
|
29
|
+
* A JWT token with an expired timestamp
|
|
30
|
+
* Decoded payload: {"sub":"test-user","exp":1000000000}
|
|
31
|
+
*/
|
|
32
|
+
export declare const EXPIRED_JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXIiLCJleHAiOjEwMDAwMDAwMDB9.abcdefghijklmnopqrstuvwxyz012345678901234567890";
|
|
33
|
+
/**
|
|
34
|
+
* Mock API responses
|
|
35
|
+
*/
|
|
36
|
+
export declare const MOCK_ENVIRONMENTS_RESPONSE: {
|
|
37
|
+
success: boolean;
|
|
38
|
+
message: string;
|
|
39
|
+
environments: {
|
|
40
|
+
title: string;
|
|
41
|
+
description: string;
|
|
42
|
+
dockerImage: string;
|
|
43
|
+
language: string;
|
|
44
|
+
burning_rate: number;
|
|
45
|
+
}[];
|
|
46
|
+
};
|
|
47
|
+
export declare const MOCK_RUNTIME_RESPONSE: {
|
|
48
|
+
success: boolean;
|
|
49
|
+
message: string;
|
|
50
|
+
runtime: {
|
|
51
|
+
pod_name: string;
|
|
52
|
+
status: string;
|
|
53
|
+
environment: string;
|
|
54
|
+
created_at: string;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
export declare const MOCK_SNAPSHOT_RESPONSE: {
|
|
58
|
+
success: boolean;
|
|
59
|
+
message: string;
|
|
60
|
+
snapshot: {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
created_at: string;
|
|
64
|
+
size: number;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Test constants for API unit tests
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A valid mock JWT token for testing
|
|
10
|
+
* This token has the correct JWT structure (header.payload.signature) but is not cryptographically valid
|
|
11
|
+
*
|
|
12
|
+
* Decoded header: {"alg":"HS256","typ":"JWT"}
|
|
13
|
+
* Decoded payload: {"sub":"test-user","iss":"test-issuer","iat":1700000000,"exp":9999999999,"aud":"test-audience"}
|
|
14
|
+
*
|
|
15
|
+
* The token is constructed to:
|
|
16
|
+
* - Have three parts separated by dots
|
|
17
|
+
* - Have valid base64url encoded parts
|
|
18
|
+
* - Have proper JWT header with alg and typ
|
|
19
|
+
* - Have common JWT claims in payload
|
|
20
|
+
* - Have an expiration far in the future to avoid expiration errors
|
|
21
|
+
* - Be long enough to pass length validation (>100 chars)
|
|
22
|
+
*/
|
|
23
|
+
export const MOCK_JWT_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXIiLCJpc3MiOiJ0ZXN0LWlzc3VlciIsImlhdCI6MTcwMDAwMDAwMCwiZXhwIjo5OTk5OTk5OTk5LCJhdWQiOiJ0ZXN0LWF1ZGllbmNlIn0.abcdefghijklmnopqrstuvwxyz012345678901234567890';
|
|
24
|
+
/**
|
|
25
|
+
* An invalid token (not JWT format) for negative testing
|
|
26
|
+
*/
|
|
27
|
+
export const INVALID_TOKEN = 'invalid-token';
|
|
28
|
+
/**
|
|
29
|
+
* A malformed JWT token (only two parts instead of three)
|
|
30
|
+
*/
|
|
31
|
+
export const MALFORMED_JWT_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXIifQ';
|
|
32
|
+
/**
|
|
33
|
+
* A JWT token with an expired timestamp
|
|
34
|
+
* Decoded payload: {"sub":"test-user","exp":1000000000}
|
|
35
|
+
*/
|
|
36
|
+
export const EXPIRED_JWT_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVzZXIiLCJleHAiOjEwMDAwMDAwMDB9.abcdefghijklmnopqrstuvwxyz012345678901234567890';
|
|
37
|
+
/**
|
|
38
|
+
* Mock API responses
|
|
39
|
+
*/
|
|
40
|
+
export const MOCK_ENVIRONMENTS_RESPONSE = {
|
|
41
|
+
success: true,
|
|
42
|
+
message: 'Environments retrieved successfully',
|
|
43
|
+
environments: [
|
|
44
|
+
{
|
|
45
|
+
title: 'Python Base',
|
|
46
|
+
description: 'Basic Python environment',
|
|
47
|
+
dockerImage: 'python:3.9',
|
|
48
|
+
language: 'python',
|
|
49
|
+
burning_rate: 1,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
title: 'R Statistical',
|
|
53
|
+
description: 'R environment for statistical computing',
|
|
54
|
+
dockerImage: 'r-base:4.3',
|
|
55
|
+
language: 'r',
|
|
56
|
+
burning_rate: 2,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
export const MOCK_RUNTIME_RESPONSE = {
|
|
61
|
+
success: true,
|
|
62
|
+
message: 'Runtime retrieved successfully',
|
|
63
|
+
runtime: {
|
|
64
|
+
pod_name: 'test-runtime-pod',
|
|
65
|
+
status: 'running',
|
|
66
|
+
environment: 'python-base',
|
|
67
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
export const MOCK_SNAPSHOT_RESPONSE = {
|
|
71
|
+
success: true,
|
|
72
|
+
message: 'Snapshot retrieved successfully',
|
|
73
|
+
snapshot: {
|
|
74
|
+
id: 'test-snapshot-123',
|
|
75
|
+
name: 'Test Snapshot',
|
|
76
|
+
created_at: '2024-01-01T00:00:00Z',
|
|
77
|
+
size: 1024000,
|
|
78
|
+
},
|
|
79
|
+
};
|