@datalayer/core 0.0.9 → 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.
Files changed (199) hide show
  1. package/lib/__tests__/shared/cleanup-shared.d.ts +4 -0
  2. package/lib/__tests__/shared/cleanup-shared.js +228 -0
  3. package/lib/__tests__/shared/test-config.d.ts +51 -0
  4. package/lib/__tests__/shared/test-config.js +110 -0
  5. package/lib/__tests__/shared/test-constants.d.ts +66 -0
  6. package/lib/__tests__/shared/test-constants.js +79 -0
  7. package/lib/api/DatalayerApi.d.ts +1 -1
  8. package/lib/api/DatalayerApi.js +73 -42
  9. package/lib/api/__tests__/iam.authentication.integration.test.d.ts +1 -0
  10. package/lib/api/__tests__/iam.authentication.integration.test.js +247 -0
  11. package/lib/api/__tests__/iam.healthz.integration.test.d.ts +1 -0
  12. package/lib/api/__tests__/iam.healthz.integration.test.js +63 -0
  13. package/lib/api/__tests__/iam.profile.integration.test.d.ts +1 -0
  14. package/lib/api/__tests__/iam.profile.integration.test.js +252 -0
  15. package/lib/api/__tests__/runtimes.environments.integration.test.d.ts +1 -0
  16. package/lib/api/__tests__/runtimes.environments.integration.test.js +122 -0
  17. package/lib/api/__tests__/runtimes.healthz.integration.test.d.ts +1 -0
  18. package/lib/api/__tests__/runtimes.healthz.integration.test.js +50 -0
  19. package/lib/api/__tests__/runtimes.integration.test.d.ts +1 -0
  20. package/lib/api/__tests__/runtimes.integration.test.js +369 -0
  21. package/lib/api/__tests__/spacer.healthz.integration.test.d.ts +1 -0
  22. package/lib/api/__tests__/spacer.healthz.integration.test.js +50 -0
  23. package/lib/api/__tests__/spacer.integration.test.d.ts +1 -0
  24. package/lib/api/__tests__/spacer.integration.test.js +519 -0
  25. package/lib/api/constants.d.ts +19 -0
  26. package/lib/api/constants.js +23 -0
  27. package/lib/api/iam/__tests__/authentication.unit.test.d.ts +1 -0
  28. package/lib/api/iam/__tests__/authentication.unit.test.js +63 -0
  29. package/lib/api/iam/__tests__/healthz.unit.test.d.ts +1 -0
  30. package/lib/api/iam/__tests__/healthz.unit.test.js +60 -0
  31. package/lib/api/iam/__tests__/profile.unit.test.d.ts +1 -0
  32. package/lib/api/iam/__tests__/profile.unit.test.js +57 -0
  33. package/lib/api/iam/authentication.d.ts +40 -0
  34. package/lib/api/iam/authentication.js +128 -0
  35. package/lib/api/iam/healthz.d.ts +15 -0
  36. package/lib/api/iam/healthz.js +43 -0
  37. package/lib/api/iam/index.d.ts +12 -0
  38. package/lib/api/iam/index.js +17 -0
  39. package/lib/api/iam/profile.d.ts +15 -0
  40. package/lib/api/iam/profile.js +41 -0
  41. package/lib/api/index.d.ts +20 -3
  42. package/lib/api/index.js +22 -3
  43. package/lib/api/runtimes/__tests__/environments.unit.test.d.ts +1 -0
  44. package/lib/api/runtimes/__tests__/environments.unit.test.js +77 -0
  45. package/lib/api/runtimes/__tests__/healthz.unit.test.d.ts +1 -0
  46. package/lib/api/runtimes/__tests__/healthz.unit.test.js +57 -0
  47. package/lib/api/runtimes/__tests__/runtimes.unit.test.d.ts +1 -0
  48. package/lib/api/runtimes/__tests__/runtimes.unit.test.js +139 -0
  49. package/lib/api/runtimes/__tests__/snapshots.unit.test.d.ts +1 -0
  50. package/lib/api/runtimes/__tests__/snapshots.unit.test.js +96 -0
  51. package/lib/api/runtimes/environments.d.ts +9 -0
  52. package/lib/api/runtimes/environments.js +28 -0
  53. package/lib/api/runtimes/healthz.d.ts +25 -0
  54. package/lib/api/runtimes/healthz.js +43 -0
  55. package/lib/api/runtimes/index.d.ts +10 -5
  56. package/lib/api/runtimes/index.js +10 -5
  57. package/lib/api/runtimes/runtimes.d.ts +54 -0
  58. package/lib/api/runtimes/runtimes.js +169 -0
  59. package/lib/api/runtimes/snapshots.d.ts +34 -21
  60. package/lib/api/runtimes/snapshots.js +69 -138
  61. package/lib/api/spacer/__tests__/healthz.unit.test.d.ts +1 -0
  62. package/lib/api/spacer/__tests__/healthz.unit.test.js +57 -0
  63. package/lib/api/spacer/__tests__/items.unit.test.d.ts +1 -0
  64. package/lib/api/spacer/__tests__/items.unit.test.js +165 -0
  65. package/lib/api/spacer/__tests__/lexicals.unit.test.d.ts +1 -0
  66. package/lib/api/spacer/__tests__/lexicals.unit.test.js +323 -0
  67. package/lib/api/spacer/__tests__/notebooks.unit.test.d.ts +1 -0
  68. package/lib/api/spacer/__tests__/notebooks.unit.test.js +224 -0
  69. package/lib/api/spacer/__tests__/users.unit.test.d.ts +1 -0
  70. package/lib/api/spacer/__tests__/users.unit.test.js +132 -0
  71. package/lib/api/spacer/healthz.d.ts +25 -0
  72. package/lib/api/spacer/healthz.js +43 -0
  73. package/lib/api/spacer/index.d.ts +13 -0
  74. package/lib/api/spacer/index.js +17 -0
  75. package/lib/api/spacer/items.d.ts +17 -0
  76. package/lib/api/spacer/items.js +40 -0
  77. package/lib/api/spacer/lexicals.d.ts +26 -0
  78. package/lib/api/spacer/lexicals.js +74 -0
  79. package/lib/api/spacer/notebooks.d.ts +26 -0
  80. package/lib/api/spacer/notebooks.js +74 -0
  81. package/lib/api/spacer/spaces.d.ts +9 -0
  82. package/lib/api/spacer/spaces.js +29 -0
  83. package/lib/api/spacer/users.d.ts +9 -0
  84. package/lib/api/spacer/users.js +28 -0
  85. package/lib/api/types/iam.d.ts +180 -0
  86. package/lib/api/types/index.d.ts +32 -0
  87. package/lib/api/types/index.js +36 -0
  88. package/lib/api/types/runtimes.d.ts +235 -0
  89. package/lib/api/types/runtimes.js +5 -0
  90. package/lib/api/types/spacer.d.ts +271 -0
  91. package/lib/api/types/spacer.js +5 -0
  92. package/lib/api/utils/__tests__/validation.test.d.ts +1 -0
  93. package/lib/api/utils/__tests__/validation.test.js +109 -0
  94. package/lib/api/utils/validation.d.ts +24 -0
  95. package/lib/api/utils/validation.js +133 -0
  96. package/lib/components/display/JupyterDialog.js +4 -8
  97. package/lib/components/progress/CreditsIndicator.d.ts +1 -1
  98. package/lib/components/runtimes/RuntimeCellVariablesDialog.js +2 -2
  99. package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +1 -1
  100. package/lib/components/runtimes/RuntimeLauncherDialog.js +5 -2
  101. package/lib/components/runtimes/RuntimePickerBase.d.ts +1 -1
  102. package/lib/components/runtimes/RuntimePickerBase.js +1 -1
  103. package/lib/components/runtimes/RuntimePickerCell.js +2 -1
  104. package/lib/components/runtimes/RuntimePickerNotebook.d.ts +1 -1
  105. package/lib/components/runtimes/RuntimePickerNotebook.js +1 -1
  106. package/lib/components/runtimes/RuntimeSimplePicker.js +2 -1
  107. package/lib/components/runtimes/RuntimeTransfer.d.ts +1 -1
  108. package/lib/components/runtimes/RuntimeUtils.d.ts +1 -1
  109. package/lib/components/snapshots/RuntimeSnapshotMenu.d.ts +1 -1
  110. package/lib/components/snapshots/RuntimeSnapshotMenu.js +2 -2
  111. package/lib/components/snippets/SnippetDialog.js +1 -1
  112. package/lib/components/storage/ContentsBrowser.js +2 -2
  113. package/lib/components/tables/DataTable.js +2 -1
  114. package/lib/hooks/useDatalayer.d.ts +1 -1
  115. package/lib/hooks/useDatalayer.js +1 -1
  116. package/lib/hooks/useIAM.js +1 -1
  117. package/lib/hooks/useRuntimes.js +1 -1
  118. package/lib/index.d.ts +9 -0
  119. package/lib/index.js +10 -0
  120. package/lib/sdk/client/__tests__/sdk.health.integration.test.d.ts +1 -0
  121. package/lib/sdk/client/__tests__/sdk.health.integration.test.js +110 -0
  122. package/lib/sdk/client/__tests__/sdk.iam.integration.test.d.ts +1 -0
  123. package/lib/sdk/client/__tests__/sdk.iam.integration.test.js +179 -0
  124. package/lib/sdk/client/__tests__/sdk.models.integration.test.d.ts +1 -0
  125. package/lib/sdk/client/__tests__/sdk.models.integration.test.js +376 -0
  126. package/lib/sdk/client/__tests__/sdk.runtimes.integration.test.d.ts +1 -0
  127. package/lib/sdk/client/__tests__/sdk.runtimes.integration.test.js +276 -0
  128. package/lib/sdk/client/__tests__/sdk.spacer.integration.test.d.ts +1 -0
  129. package/lib/sdk/client/__tests__/sdk.spacer.integration.test.js +361 -0
  130. package/lib/sdk/client/base.d.ts +88 -0
  131. package/lib/sdk/client/base.js +112 -0
  132. package/lib/sdk/client/index.d.ts +192 -0
  133. package/lib/sdk/client/index.js +128 -0
  134. package/lib/sdk/client/mixins/HealthMixin.d.ts +100 -0
  135. package/lib/sdk/client/mixins/HealthMixin.js +133 -0
  136. package/lib/sdk/client/mixins/IAMMixin.d.ts +59 -0
  137. package/lib/sdk/client/mixins/IAMMixin.js +83 -0
  138. package/lib/sdk/client/mixins/RuntimesMixin.d.ts +134 -0
  139. package/lib/sdk/client/mixins/RuntimesMixin.js +221 -0
  140. package/lib/sdk/client/mixins/SpacerMixin.d.ts +184 -0
  141. package/lib/sdk/client/mixins/SpacerMixin.js +278 -0
  142. package/lib/sdk/client/models/Lexical.d.ts +156 -0
  143. package/lib/sdk/client/models/Lexical.js +275 -0
  144. package/lib/sdk/client/models/Notebook.d.ts +174 -0
  145. package/lib/sdk/client/models/Notebook.js +311 -0
  146. package/lib/sdk/client/models/Runtime.d.ts +221 -0
  147. package/lib/sdk/client/models/Runtime.js +341 -0
  148. package/lib/sdk/client/models/Snapshot.d.ts +156 -0
  149. package/lib/sdk/client/models/Snapshot.js +244 -0
  150. package/lib/sdk/client/models/Space.d.ts +182 -0
  151. package/lib/sdk/client/models/Space.js +276 -0
  152. package/lib/sdk/client/models/__tests__/Lexical.test.d.ts +1 -0
  153. package/lib/sdk/client/models/__tests__/Lexical.test.js +288 -0
  154. package/lib/sdk/client/models/__tests__/Notebook.test.d.ts +1 -0
  155. package/lib/sdk/client/models/__tests__/Notebook.test.js +206 -0
  156. package/lib/sdk/client/models/__tests__/Runtime.test.d.ts +1 -0
  157. package/lib/sdk/client/models/__tests__/Runtime.test.js +133 -0
  158. package/lib/sdk/client/models/__tests__/Snapshot.test.d.ts +1 -0
  159. package/lib/sdk/client/models/__tests__/Snapshot.test.js +244 -0
  160. package/lib/sdk/client/models/__tests__/Space.test.d.ts +1 -0
  161. package/lib/sdk/client/models/__tests__/Space.test.js +334 -0
  162. package/lib/sdk/client/models/index.d.ts +30 -0
  163. package/lib/sdk/client/models/index.js +30 -0
  164. package/lib/sdk/client/utils/mixins.d.ts +42 -0
  165. package/lib/sdk/client/utils/mixins.js +47 -0
  166. package/lib/sdk/index.d.ts +26 -0
  167. package/lib/sdk/index.js +32 -0
  168. package/lib/sdk/stateful/index.d.ts +3 -0
  169. package/lib/sdk/stateful/index.js +7 -0
  170. package/lib/{api → sdk/stateful}/runtimes/actions.d.ts +1 -1
  171. package/lib/{api → sdk/stateful}/runtimes/actions.js +3 -3
  172. package/lib/{api → sdk/stateful}/runtimes/apis.d.ts +1 -1
  173. package/lib/sdk/stateful/runtimes/apis.js +5 -0
  174. package/lib/sdk/stateful/runtimes/index.d.ts +5 -0
  175. package/lib/sdk/stateful/runtimes/index.js +9 -0
  176. package/lib/sdk/stateful/runtimes/snapshots.d.ts +25 -0
  177. package/lib/sdk/stateful/runtimes/snapshots.js +150 -0
  178. package/lib/services/DatalayerServiceManager.js +1 -1
  179. package/lib/state/substates/IAMState.js +1 -1
  180. package/lib/state/substates/RuntimesState.d.ts +1 -1
  181. package/lib/state/substates/RuntimesState.js +1 -1
  182. package/lib/state/substates/SurveysState.js +1 -1
  183. package/lib/test-setup.js +1 -0
  184. package/package.json +19 -9
  185. /package/lib/api/{runtimes/apis.js → types/iam.js} +0 -0
  186. /package/lib/{api → sdk/stateful}/jupyter/exec/Python.d.ts +0 -0
  187. /package/lib/{api → sdk/stateful}/jupyter/exec/Python.js +0 -0
  188. /package/lib/{api → sdk/stateful}/jupyter/exec/Snippets.d.ts +0 -0
  189. /package/lib/{api → sdk/stateful}/jupyter/exec/Snippets.js +0 -0
  190. /package/lib/{api → sdk/stateful}/jupyter/exec/index.d.ts +0 -0
  191. /package/lib/{api → sdk/stateful}/jupyter/exec/index.js +0 -0
  192. /package/lib/{api → sdk/stateful}/jupyter/index.d.ts +0 -0
  193. /package/lib/{api → sdk/stateful}/jupyter/index.js +0 -0
  194. /package/lib/{api → sdk/stateful}/jupyter/kernelsHandler.d.ts +0 -0
  195. /package/lib/{api → sdk/stateful}/jupyter/kernelsHandler.js +0 -0
  196. /package/lib/{api → sdk/stateful}/runtimes/settings.d.ts +0 -0
  197. /package/lib/{api → sdk/stateful}/runtimes/settings.js +0 -0
  198. /package/lib/{api → sdk/stateful}/runtimes/utils.d.ts +0 -0
  199. /package/lib/{api → sdk/stateful}/runtimes/utils.js +0 -0
@@ -0,0 +1,376 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
6
+ import { DatalayerSDK } from '..';
7
+ import { Runtime } from '../models/Runtime';
8
+ import { Snapshot } from '../models/Snapshot';
9
+ import { Notebook } from '../models/Notebook';
10
+ import { Lexical } from '../models/Lexical';
11
+ import { testConfig } from '../../../__tests__/shared/test-config';
12
+ import { DEFAULT_SERVICE_URLS } from '../../../api/constants';
13
+ import { performCleanup } from '../../../__tests__/shared/cleanup-shared';
14
+ /**
15
+ * SDK Models Integration Tests
16
+ *
17
+ * Tests model state management, lazy loading, and relationships
18
+ * using the SDK client and model classes.
19
+ */
20
+ describe('SDK Models Integration Tests', () => {
21
+ let sdk;
22
+ let testSpace = null;
23
+ let testNotebook = null;
24
+ let testLexical = null;
25
+ let testRuntime = null;
26
+ let testSnapshot = null;
27
+ beforeAll(async () => {
28
+ if (!testConfig.hasToken()) {
29
+ return;
30
+ }
31
+ await performCleanup('setup');
32
+ sdk = new DatalayerSDK({
33
+ token: testConfig.getToken(),
34
+ iamRunUrl: DEFAULT_SERVICE_URLS.IAM,
35
+ runtimesRunUrl: DEFAULT_SERVICE_URLS.RUNTIMES,
36
+ spacerRunUrl: DEFAULT_SERVICE_URLS.SPACER,
37
+ });
38
+ });
39
+ afterAll(async () => {
40
+ if (!testConfig.hasToken()) {
41
+ return;
42
+ }
43
+ await performCleanup('teardown');
44
+ }, 30000);
45
+ describe.skipIf(!testConfig.hasToken())('Model state management', () => {
46
+ it('should mark models as deleted after deletion', async () => {
47
+ console.log('Testing model deletion state...');
48
+ // Get a space to work with
49
+ const spaces = await sdk.getMySpaces();
50
+ expect(spaces.length).toBeGreaterThan(0);
51
+ testSpace = spaces[0];
52
+ // Create a notebook
53
+ const notebook = await sdk.createNotebook({
54
+ spaceId: testSpace.uid,
55
+ notebookType: 'jupyter',
56
+ name: 'model-test-notebook-' + Date.now(),
57
+ description: 'Test notebook for model tests',
58
+ });
59
+ expect(notebook).toBeInstanceOf(Notebook);
60
+ // Delete it
61
+ await notebook.delete();
62
+ // Verify it's marked as deleted
63
+ expect(notebook.isDeleted).toBe(true);
64
+ // Verify methods throw errors
65
+ try {
66
+ await notebook.getName();
67
+ expect(true).toBe(false); // Should not reach here
68
+ }
69
+ catch (error) {
70
+ expect(error.message).toContain('deleted');
71
+ console.log('Deleted model correctly throws error');
72
+ }
73
+ });
74
+ it('should handle lazy loading of model properties', async () => {
75
+ if (!testSpace) {
76
+ throw new Error('Test dependency failed: testSpace should be available from previous test');
77
+ }
78
+ console.log('Testing lazy loading...');
79
+ // Create a notebook with minimal data
80
+ testNotebook = await sdk.createNotebook({
81
+ spaceId: testSpace.uid,
82
+ notebookType: 'jupyter',
83
+ name: 'lazy-load-test-' + Date.now(),
84
+ description: 'Test description',
85
+ });
86
+ // First call should fetch from API
87
+ const name1 = await testNotebook.getName();
88
+ expect(name1).toBeDefined();
89
+ // Second call should use cached value (faster)
90
+ const start = Date.now();
91
+ const name2 = await testNotebook.getName();
92
+ const elapsed = Date.now() - start;
93
+ expect(name2).toBe(name1);
94
+ expect(elapsed).toBeLessThan(10); // Should be instant
95
+ console.log(`Lazy loading working - cached access in ${elapsed}ms`);
96
+ });
97
+ it('should refresh data when explicitly requested', async () => {
98
+ // Ensure we have a space to work with
99
+ if (!testSpace) {
100
+ const spaces = await sdk.getMySpaces();
101
+ expect(spaces.length).toBeGreaterThan(0);
102
+ testSpace = spaces[0];
103
+ }
104
+ // Create a notebook if we don't have one
105
+ if (!testNotebook) {
106
+ testNotebook = await sdk.createNotebook({
107
+ spaceId: testSpace.uid,
108
+ notebookType: 'jupyter',
109
+ name: 'refresh-test-' + Date.now(),
110
+ description: 'Test description',
111
+ });
112
+ }
113
+ console.log('Testing data refresh...');
114
+ // Get initial name
115
+ const originalName = await testNotebook.getName();
116
+ expect(originalName).toBeDefined();
117
+ // Test that we can fetch fresh data
118
+ const content = await testNotebook.getContent();
119
+ expect(content).toBeDefined();
120
+ // Verify the model is working properly
121
+ const uid = testNotebook.uid;
122
+ expect(uid).toBeDefined();
123
+ console.log(`Model data access verified - name: ${originalName}, uid: ${uid}`);
124
+ });
125
+ });
126
+ describe.skipIf(!testConfig.hasToken())('Model relationships', () => {
127
+ it('should handle Space → Notebook relationship', async () => {
128
+ // Ensure we have a space
129
+ if (!testSpace) {
130
+ const spaces = await sdk.getMySpaces();
131
+ expect(spaces.length).toBeGreaterThan(0);
132
+ testSpace = spaces[0];
133
+ }
134
+ // Ensure we have a notebook
135
+ if (!testNotebook) {
136
+ testNotebook = await sdk.createNotebook({
137
+ spaceId: testSpace.uid,
138
+ notebookType: 'jupyter',
139
+ name: 'relationship-test-' + Date.now(),
140
+ description: 'Test notebook for relationships',
141
+ });
142
+ }
143
+ console.log('Testing Space → Notebook relationship...');
144
+ // Get items from space
145
+ const items = await testSpace.getItems();
146
+ expect(Array.isArray(items)).toBe(true);
147
+ console.log(`Space has ${items.length} items`);
148
+ console.log('Looking for notebook with id:', testNotebook.id);
149
+ console.log('Items in space:', items.map(item => ({
150
+ id: item.id,
151
+ uid: item.uid,
152
+ type: item.type || item.type,
153
+ name: item.name,
154
+ })));
155
+ // Our test notebook should be in the items
156
+ // Note: Space items may not have a 'type' property
157
+ const foundNotebook = items.find(item => item.id === testNotebook.id ||
158
+ item.uid === testNotebook.uid);
159
+ expect(foundNotebook).toBeDefined();
160
+ console.log(`Found notebook ${testNotebook.id} in space items`);
161
+ console.log(`Space contains ${items.length} items`);
162
+ });
163
+ it('should handle Space → Lexical relationship', async () => {
164
+ // Ensure we have a space
165
+ if (!testSpace) {
166
+ const spaces = await sdk.getMySpaces();
167
+ expect(spaces.length).toBeGreaterThan(0);
168
+ testSpace = spaces[0];
169
+ }
170
+ console.log('Testing Space → Lexical relationship...');
171
+ // Create a lexical document
172
+ testLexical = await sdk.createLexical({
173
+ spaceId: testSpace.uid,
174
+ name: 'relationship-test-lexical-' + Date.now(),
175
+ documentType: 'document',
176
+ description: 'Test lexical document for relationships',
177
+ });
178
+ expect(testLexical).toBeInstanceOf(Lexical);
179
+ // Verify it appears in space items
180
+ const items = await testSpace.getItems();
181
+ const foundLexical = items.find(item => item.id === testLexical.id || item.uid === testLexical.uid);
182
+ expect(foundLexical).toBeDefined();
183
+ console.log(`Created lexical ${testLexical.id} in space`);
184
+ });
185
+ });
186
+ describe.skipIf(!testConfig.hasToken() || testConfig.shouldSkipExpensive())('Runtime → Snapshot relationship', () => {
187
+ it('should handle Runtime → Snapshot creation', async () => {
188
+ console.log('Testing Runtime → Snapshot relationship...');
189
+ // Create a runtime
190
+ testRuntime = await sdk.createRuntime({
191
+ environment_name: 'python-cpu-env',
192
+ type: 'notebook',
193
+ given_name: 'model-test-runtime',
194
+ credits_limit: 10,
195
+ });
196
+ expect(testRuntime).toBeInstanceOf(Runtime);
197
+ console.log(`Created runtime: ${testRuntime.podName}`);
198
+ // Create a snapshot from the runtime
199
+ testSnapshot = await testRuntime.createSnapshot('model-test-snapshot', 'Test snapshot from model test');
200
+ expect(testSnapshot).toBeInstanceOf(Snapshot);
201
+ // Snapshots don't have a podName property
202
+ // Instead, check that the snapshot was created successfully
203
+ expect(testSnapshot.uid).toBeDefined();
204
+ expect(testSnapshot.name).toBe('model-test-snapshot');
205
+ console.log(`Created snapshot ${testSnapshot.uid} from runtime`);
206
+ });
207
+ it('should list runtime snapshots', async () => {
208
+ if (!testRuntime || !testSnapshot) {
209
+ throw new Error('Test dependency failed: testRuntime and testSnapshot should be available from previous test');
210
+ }
211
+ console.log('Testing runtime snapshot listing...');
212
+ // List all snapshots
213
+ const snapshots = await sdk.listSnapshots();
214
+ // Find our test snapshot
215
+ const found = snapshots.find(s => s.uid === testSnapshot.uid);
216
+ expect(found).toBeDefined();
217
+ console.log(`Found ${snapshots.length} snapshots total`);
218
+ });
219
+ });
220
+ describe.skipIf(!testConfig.hasToken())('Model serialization', () => {
221
+ it('should serialize Space model to JSON', async () => {
222
+ if (!testSpace) {
223
+ throw new Error('Test dependency failed: testSpace should be available from previous test');
224
+ }
225
+ console.log('Testing Space serialization...');
226
+ const json = await testSpace.toJSON();
227
+ expect(json).toBeDefined();
228
+ expect(json.id || json.uid).toBeDefined();
229
+ // JSON should include fetched properties
230
+ if (testSpace.name) {
231
+ expect(json.name).toBe(testSpace.name);
232
+ }
233
+ console.log('Space serialized successfully');
234
+ });
235
+ it('should serialize Notebook model to JSON', async () => {
236
+ // Always create a fresh notebook for this test to avoid state issues
237
+ // Ensure we have a space first
238
+ if (!testSpace) {
239
+ const spaces = await sdk.getMySpaces();
240
+ expect(spaces.length).toBeGreaterThan(0);
241
+ testSpace = spaces[0];
242
+ }
243
+ const freshNotebook = await sdk.createNotebook({
244
+ spaceId: testSpace.uid,
245
+ notebookType: 'jupyter',
246
+ name: 'serialization-test-' + Date.now(),
247
+ description: 'Test notebook for serialization',
248
+ });
249
+ console.log('Testing Notebook serialization...');
250
+ console.log('Notebook created with id:', freshNotebook.id);
251
+ const json = await freshNotebook.toJSON();
252
+ expect(json).toBeDefined();
253
+ expect(json.id).toBe(freshNotebook.id);
254
+ // Log the JSON to see what fields are present
255
+ console.log('Notebook JSON:', JSON.stringify(json, null, 2));
256
+ // The notebook should have its basic properties
257
+ // The name might be in name, name_t, or notebook_name_s
258
+ const name = json.name || json.name_t || json.notebook_name_s;
259
+ expect(name).toBeDefined();
260
+ expect(json.id).toBeDefined();
261
+ expect(json.uid).toBeDefined();
262
+ console.log('Notebook serialized successfully');
263
+ });
264
+ it.skipIf(testConfig.shouldSkipExpensive())('should serialize Runtime model to JSON', async () => {
265
+ if (!testRuntime) {
266
+ throw new Error('Test dependency failed: testRuntime should be available from previous test');
267
+ }
268
+ console.log('Testing Runtime serialization...');
269
+ const json = await testRuntime.toJSON();
270
+ expect(json).toBeDefined();
271
+ expect(json.pod_name).toBe(testRuntime.podName);
272
+ expect(json.environment_name).toBe(testRuntime.environmentName);
273
+ console.log('Runtime serialized successfully');
274
+ });
275
+ it.skipIf(testConfig.shouldSkipExpensive())('should serialize Snapshot model to JSON', async () => {
276
+ if (!testSnapshot) {
277
+ throw new Error('Test dependency failed: testSnapshot should be available from previous test');
278
+ }
279
+ console.log('Testing Snapshot serialization...');
280
+ const json = await testSnapshot.toJSON();
281
+ expect(json).toBeDefined();
282
+ expect(json.uid).toBe(testSnapshot.uid);
283
+ console.log('Snapshot serialized successfully');
284
+ });
285
+ });
286
+ describe.skipIf(!testConfig.hasToken())('Model error handling', () => {
287
+ it('should handle API errors gracefully in models', async () => {
288
+ console.log('Testing model error handling...');
289
+ // Create a fake notebook with invalid ID
290
+ const fakeNotebook = new Notebook({ id: 'invalid-id', space_id: 'invalid-space' }, sdk);
291
+ try {
292
+ await fakeNotebook.getName();
293
+ expect(true).toBe(false); // Should not reach here
294
+ }
295
+ catch (error) {
296
+ expect(error).toBeDefined();
297
+ console.log('Model correctly propagates API errors');
298
+ }
299
+ });
300
+ it('should validate model operations', async () => {
301
+ if (!testNotebook) {
302
+ throw new Error('Test dependency failed: testNotebook should be available from previous test');
303
+ }
304
+ console.log('Testing model validation...');
305
+ try {
306
+ // Try to update with invalid data
307
+ await testNotebook.update({});
308
+ // This might succeed with empty update, which is okay
309
+ console.log('Empty update handled');
310
+ }
311
+ catch (error) {
312
+ console.log('Invalid update rejected');
313
+ }
314
+ });
315
+ it('should handle concurrent model operations', async () => {
316
+ if (!testNotebook) {
317
+ throw new Error('Test dependency failed: testNotebook should be available from previous test');
318
+ }
319
+ console.log('Testing concurrent model operations...');
320
+ // Make multiple concurrent requests
321
+ const promises = [
322
+ testNotebook.getName(),
323
+ testNotebook.getContent(),
324
+ testNotebook.getUpdatedAt(),
325
+ ];
326
+ const results = await Promise.all(promises);
327
+ expect(results[0]).toBeDefined(); // name
328
+ expect(results[1]).toBeDefined(); // content
329
+ expect(results[2]).toBeInstanceOf(Date); // updatedAt
330
+ console.log('Concurrent operations completed successfully');
331
+ });
332
+ });
333
+ describe.skipIf(!testConfig.hasToken())('Model lifecycle', () => {
334
+ it('should support full model lifecycle', async () => {
335
+ // Ensure we have a space
336
+ if (!testSpace) {
337
+ const spaces = await sdk.getMySpaces();
338
+ expect(spaces.length).toBeGreaterThan(0);
339
+ testSpace = spaces[0];
340
+ }
341
+ console.log('Testing full model lifecycle...');
342
+ // 1. Create
343
+ const notebook = await sdk.createNotebook({
344
+ spaceId: testSpace.uid,
345
+ notebookType: 'jupyter',
346
+ name: 'lifecycle-test-' + Date.now(),
347
+ description: 'Lifecycle test notebook',
348
+ });
349
+ expect(notebook).toBeInstanceOf(Notebook);
350
+ console.log('1. Created notebook');
351
+ // 2. Read
352
+ const retrieved = await sdk.getNotebook(notebook.uid);
353
+ expect(retrieved.id).toBe(notebook.id);
354
+ console.log('2. Retrieved notebook');
355
+ // 3. Use methods to access properties
356
+ const name = await notebook.getName();
357
+ expect(name).toBeDefined();
358
+ const content = await notebook.getContent();
359
+ expect(content).toBeDefined();
360
+ console.log('3. Used notebook methods');
361
+ // 4. Delete
362
+ await notebook.delete();
363
+ expect(notebook.isDeleted).toBe(true);
364
+ console.log('4. Deleted notebook');
365
+ // 5. Verify deletion
366
+ try {
367
+ await notebook.getName();
368
+ expect(true).toBe(false);
369
+ }
370
+ catch (error) {
371
+ expect(error.message).toContain('deleted');
372
+ console.log('5. Verified deletion state');
373
+ }
374
+ });
375
+ });
376
+ });
@@ -0,0 +1,276 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
6
+ import { DatalayerSDK } from '..';
7
+ import { Runtime } from '../models/Runtime';
8
+ import { Snapshot } from '../models/Snapshot';
9
+ import { testConfig } from '../../../__tests__/shared/test-config';
10
+ import { DEFAULT_SERVICE_URLS } from '../../../api/constants';
11
+ import { performCleanup } from '../../../__tests__/shared/cleanup-shared';
12
+ /**
13
+ * SDK Runtimes Integration Tests
14
+ *
15
+ * Tests runtime and snapshot lifecycle management
16
+ * using the SDK client and model classes.
17
+ */
18
+ describe('SDK Runtimes Integration Tests', () => {
19
+ let sdk;
20
+ let createdRuntime = null;
21
+ let createdSnapshot = null;
22
+ beforeAll(async () => {
23
+ if (!testConfig.hasToken()) {
24
+ return;
25
+ }
26
+ await performCleanup('setup');
27
+ sdk = new DatalayerSDK({
28
+ token: testConfig.getToken(),
29
+ iamRunUrl: DEFAULT_SERVICE_URLS.IAM,
30
+ runtimesRunUrl: DEFAULT_SERVICE_URLS.RUNTIMES,
31
+ spacerRunUrl: DEFAULT_SERVICE_URLS.SPACER,
32
+ });
33
+ });
34
+ afterAll(async () => {
35
+ if (!testConfig.hasToken()) {
36
+ return;
37
+ }
38
+ await performCleanup('teardown');
39
+ }, 30000);
40
+ describe.skipIf(!testConfig.hasToken())('Environment management', () => {
41
+ it('should list available environments', async () => {
42
+ console.log('Testing list environments...');
43
+ const environments = await sdk.listEnvironments();
44
+ expect(environments).toBeDefined();
45
+ expect(Array.isArray(environments)).toBe(true);
46
+ expect(environments.length).toBeGreaterThan(0);
47
+ const firstEnv = environments[0];
48
+ expect(firstEnv.name).toBeDefined();
49
+ expect(firstEnv.title).toBeDefined();
50
+ expect(firstEnv.language).toBeDefined();
51
+ console.log(`Found ${environments.length} environment(s)`);
52
+ console.log(`First environment: ${firstEnv.title} (${firstEnv.name})`);
53
+ console.log(` Language: ${firstEnv.language}`);
54
+ console.log(` Resources: CPU ${firstEnv.resources?.cpu}, Memory ${firstEnv.resources?.memory}`);
55
+ });
56
+ });
57
+ describe.skipIf(!testConfig.hasToken() || testConfig.shouldSkipExpensive())('Runtime lifecycle', () => {
58
+ it('should create a runtime', async () => {
59
+ console.log('Creating runtime...');
60
+ const runtime = await sdk.createRuntime({
61
+ environment_name: 'python-cpu-env',
62
+ type: 'notebook',
63
+ given_name: 'sdk-test-runtime',
64
+ credits_limit: 10,
65
+ });
66
+ expect(runtime).toBeInstanceOf(Runtime);
67
+ expect(runtime.podName).toBeDefined();
68
+ expect(runtime.environmentName).toBe('python-cpu-env');
69
+ expect(runtime.givenName).toContain('sdk-test-runtime');
70
+ createdRuntime = runtime;
71
+ console.log(`Created runtime: ${runtime.podName}`);
72
+ console.log(` Given name: ${runtime.givenName}`);
73
+ console.log(` Environment: ${runtime.environmentName}`);
74
+ });
75
+ it('should list runtimes and find created runtime', async () => {
76
+ if (!createdRuntime) {
77
+ throw new Error('Test dependency failed: createdRuntime should be available from previous test');
78
+ }
79
+ console.log('Listing runtimes...');
80
+ const runtimes = await sdk.listRuntimes();
81
+ expect(Array.isArray(runtimes)).toBe(true);
82
+ const found = runtimes.find(r => r.podName === createdRuntime.podName);
83
+ expect(found).toBeDefined();
84
+ expect(found).toBeInstanceOf(Runtime);
85
+ console.log(`Found ${runtimes.length} runtime(s)`);
86
+ console.log(`Created runtime found in list: ${found.podName}`);
87
+ });
88
+ it('should get runtime details', async () => {
89
+ if (!createdRuntime) {
90
+ throw new Error('Test dependency failed: createdRuntime should be available from previous test');
91
+ }
92
+ console.log('Getting runtime details...');
93
+ const runtime = await sdk.getRuntime(createdRuntime.podName);
94
+ expect(runtime).toBeInstanceOf(Runtime);
95
+ expect(runtime.podName).toBe(createdRuntime.podName);
96
+ expect(runtime.environmentName).toBe(createdRuntime.environmentName);
97
+ console.log(`Retrieved runtime: ${runtime.podName}`);
98
+ console.log(` State: ${await runtime.getState()}`);
99
+ console.log(` Running: ${await runtime.isRunning()}`);
100
+ });
101
+ it('should test runtime model methods', async () => {
102
+ if (!createdRuntime) {
103
+ throw new Error('Test dependency failed: createdRuntime should be available from previous test');
104
+ }
105
+ console.log('Testing runtime model methods...');
106
+ // Test state checking methods
107
+ const state = await createdRuntime.getState();
108
+ expect(state).toBeDefined();
109
+ console.log(`Runtime state: ${state}`);
110
+ const isRunning = await createdRuntime.isRunning();
111
+ expect(typeof isRunning).toBe('boolean');
112
+ console.log(`Is running: ${isRunning}`);
113
+ const hasError = await createdRuntime.hasError();
114
+ expect(typeof hasError).toBe('boolean');
115
+ console.log(`Has error: ${hasError}`);
116
+ // Test JSON export
117
+ const json = await createdRuntime.toJSON();
118
+ expect(json).toBeDefined();
119
+ expect(json.pod_name).toBe(createdRuntime.podName);
120
+ // Test toString
121
+ const str = createdRuntime.toString();
122
+ expect(str).toContain('Runtime');
123
+ expect(str).toContain(createdRuntime.podName);
124
+ console.log(`Runtime string: ${str}`);
125
+ });
126
+ });
127
+ describe.skipIf(!testConfig.hasToken() || testConfig.shouldSkipExpensive())('Snapshot lifecycle', () => {
128
+ it('should create a snapshot from runtime', async () => {
129
+ if (!createdRuntime) {
130
+ throw new Error('Test dependency failed: createdRuntime should be available from previous test');
131
+ }
132
+ console.log('Creating snapshot from runtime...');
133
+ const snapshot = await createdRuntime.createSnapshot('sdk-test-snapshot', 'Test snapshot from SDK');
134
+ expect(snapshot).toBeInstanceOf(Snapshot);
135
+ expect(snapshot.uid).toBeDefined();
136
+ expect(snapshot.name).toContain('sdk-test-snapshot');
137
+ createdSnapshot = snapshot;
138
+ console.log(`Created snapshot: ${snapshot.uid}`);
139
+ console.log(` Name: ${snapshot.name}`);
140
+ console.log(` Description: ${snapshot.description}`);
141
+ });
142
+ it('should list snapshots and find created snapshot', async () => {
143
+ if (!createdSnapshot) {
144
+ throw new Error('Test dependency failed: createdSnapshot should be available from previous test');
145
+ }
146
+ console.log('Listing snapshots...');
147
+ const snapshots = await sdk.listSnapshots();
148
+ expect(Array.isArray(snapshots)).toBe(true);
149
+ const found = snapshots.find(s => s.uid === createdSnapshot.uid);
150
+ expect(found).toBeDefined();
151
+ expect(found).toBeInstanceOf(Snapshot);
152
+ console.log(`Found ${snapshots.length} snapshot(s)`);
153
+ console.log(`Created snapshot found in list: ${found.uid}`);
154
+ });
155
+ it('should get snapshot details', async () => {
156
+ if (!createdSnapshot) {
157
+ throw new Error('Test dependency failed: createdSnapshot should be available from previous test');
158
+ }
159
+ console.log('Getting snapshot details...');
160
+ const snapshot = await sdk.getSnapshot(createdSnapshot.uid);
161
+ expect(snapshot).toBeInstanceOf(Snapshot);
162
+ expect(snapshot.uid).toBe(createdSnapshot.uid);
163
+ expect(snapshot.environment).toBe(createdSnapshot.environment);
164
+ console.log(`Retrieved snapshot: ${snapshot.uid}`);
165
+ const status = await snapshot.getStatus();
166
+ console.log(` Status: ${status}`);
167
+ const size = await snapshot.getSize();
168
+ console.log(` Size: ${size} bytes`);
169
+ });
170
+ it('should test snapshot model methods', async () => {
171
+ if (!createdSnapshot) {
172
+ throw new Error('Test dependency failed: createdSnapshot should be available from previous test');
173
+ }
174
+ console.log('Testing snapshot model methods...');
175
+ // Test status method
176
+ const status = await createdSnapshot.getStatus();
177
+ expect(status).toBeDefined();
178
+ console.log(`Snapshot status: ${status}`);
179
+ // Test size method
180
+ const size = await createdSnapshot.getSize();
181
+ expect(typeof size).toBe('number');
182
+ console.log(`Snapshot size: ${size} bytes`);
183
+ // Test metadata method
184
+ const metadata = await createdSnapshot.getLatestMetadata();
185
+ expect(metadata).toBeDefined();
186
+ console.log(`Metadata keys: ${Object.keys(metadata).join(', ')}`);
187
+ // Test JSON export
188
+ const json = await createdSnapshot.toJSON();
189
+ expect(json).toBeDefined();
190
+ expect(json.uid).toBe(createdSnapshot.uid);
191
+ // Test toString
192
+ const str = createdSnapshot.toString();
193
+ expect(str).toContain('Snapshot');
194
+ expect(str).toContain(createdSnapshot.uid);
195
+ console.log(`Snapshot string: ${str}`);
196
+ });
197
+ it('should delete snapshot', async () => {
198
+ if (!createdSnapshot) {
199
+ throw new Error('Test dependency failed: createdSnapshot should be available from previous test');
200
+ }
201
+ console.log('Deleting snapshot...');
202
+ const snapshotUid = createdSnapshot.uid; // Get uid before deletion
203
+ await sdk.deleteSnapshot(createdSnapshot);
204
+ console.log(`Snapshot ${snapshotUid} deleted`);
205
+ // Verify deletion
206
+ try {
207
+ await createdSnapshot.getStatus();
208
+ expect(true).toBe(false); // Should not reach here
209
+ }
210
+ catch (error) {
211
+ expect(error.message).toContain('deleted');
212
+ console.log('Snapshot correctly marked as deleted');
213
+ }
214
+ createdSnapshot = null; // Clear reference as it's deleted
215
+ });
216
+ });
217
+ describe.skipIf(!testConfig.hasToken() || testConfig.shouldSkipExpensive())('Runtime deletion', () => {
218
+ it('should delete runtime', async () => {
219
+ if (!createdRuntime) {
220
+ throw new Error('Test dependency failed: createdRuntime should be available from previous test');
221
+ }
222
+ console.log('Deleting runtime...');
223
+ const podName = createdRuntime.podName; // Get podName before deletion
224
+ await sdk.deleteRuntime(createdRuntime);
225
+ console.log(`Runtime ${podName} deleted`);
226
+ // Verify deletion
227
+ try {
228
+ await createdRuntime.getState();
229
+ expect(true).toBe(false); // Should not reach here
230
+ }
231
+ catch (error) {
232
+ expect(error.message).toContain('deleted');
233
+ console.log('Runtime correctly marked as deleted');
234
+ }
235
+ createdRuntime = null; // Clear reference as it's deleted
236
+ });
237
+ });
238
+ describe.skipIf(!testConfig.hasToken())('Error handling', () => {
239
+ it('should handle non-existent runtime gracefully', async () => {
240
+ console.log('Testing non-existent runtime...');
241
+ try {
242
+ await sdk.getRuntime('non-existent-pod-name');
243
+ expect(true).toBe(false); // Should not reach here
244
+ }
245
+ catch (error) {
246
+ expect(error).toBeDefined();
247
+ console.log('Non-existent runtime error handled correctly');
248
+ }
249
+ });
250
+ it('should handle non-existent snapshot gracefully', async () => {
251
+ console.log('Testing non-existent snapshot...');
252
+ try {
253
+ await sdk.getSnapshot('non-existent-snapshot-uid');
254
+ expect(true).toBe(false); // Should not reach here
255
+ }
256
+ catch (error) {
257
+ expect(error).toBeDefined();
258
+ console.log('Non-existent snapshot error handled correctly');
259
+ }
260
+ });
261
+ it('should validate runtime creation parameters', async () => {
262
+ console.log('Testing invalid runtime creation...');
263
+ try {
264
+ await sdk.createRuntime({
265
+ environment_name: '', // Invalid
266
+ type: 'notebook',
267
+ });
268
+ expect(true).toBe(false); // Should not reach here
269
+ }
270
+ catch (error) {
271
+ expect(error).toBeDefined();
272
+ console.log('Invalid runtime parameters rejected');
273
+ }
274
+ });
275
+ });
276
+ });