@datalayer/core 0.0.10 → 0.0.12

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 (195) 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/progress/CreditsIndicator.d.ts +1 -1
  97. package/lib/components/runtimes/RuntimeCellVariablesDialog.js +1 -1
  98. package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +1 -1
  99. package/lib/components/runtimes/RuntimeLauncherDialog.js +2 -1
  100. package/lib/components/runtimes/RuntimePickerBase.d.ts +1 -1
  101. package/lib/components/runtimes/RuntimePickerBase.js +1 -1
  102. package/lib/components/runtimes/RuntimePickerCell.js +2 -1
  103. package/lib/components/runtimes/RuntimePickerNotebook.d.ts +1 -1
  104. package/lib/components/runtimes/RuntimePickerNotebook.js +1 -1
  105. package/lib/components/runtimes/RuntimeSimplePicker.js +2 -1
  106. package/lib/components/runtimes/RuntimeTransfer.d.ts +1 -1
  107. package/lib/components/runtimes/RuntimeUtils.d.ts +1 -1
  108. package/lib/components/snapshots/RuntimeSnapshotMenu.d.ts +1 -1
  109. package/lib/components/snapshots/RuntimeSnapshotMenu.js +1 -1
  110. package/lib/hooks/useDatalayer.d.ts +1 -1
  111. package/lib/hooks/useDatalayer.js +1 -1
  112. package/lib/hooks/useIAM.js +1 -1
  113. package/lib/hooks/useRuntimes.js +1 -1
  114. package/lib/index.d.ts +9 -0
  115. package/lib/index.js +10 -0
  116. package/lib/sdk/client/__tests__/sdk.health.integration.test.d.ts +1 -0
  117. package/lib/sdk/client/__tests__/sdk.health.integration.test.js +110 -0
  118. package/lib/sdk/client/__tests__/sdk.iam.integration.test.d.ts +1 -0
  119. package/lib/sdk/client/__tests__/sdk.iam.integration.test.js +179 -0
  120. package/lib/sdk/client/__tests__/sdk.models.integration.test.d.ts +1 -0
  121. package/lib/sdk/client/__tests__/sdk.models.integration.test.js +376 -0
  122. package/lib/sdk/client/__tests__/sdk.runtimes.integration.test.d.ts +1 -0
  123. package/lib/sdk/client/__tests__/sdk.runtimes.integration.test.js +276 -0
  124. package/lib/sdk/client/__tests__/sdk.spacer.integration.test.d.ts +1 -0
  125. package/lib/sdk/client/__tests__/sdk.spacer.integration.test.js +361 -0
  126. package/lib/sdk/client/base.d.ts +88 -0
  127. package/lib/sdk/client/base.js +112 -0
  128. package/lib/sdk/client/index.d.ts +192 -0
  129. package/lib/sdk/client/index.js +128 -0
  130. package/lib/sdk/client/mixins/HealthMixin.d.ts +100 -0
  131. package/lib/sdk/client/mixins/HealthMixin.js +133 -0
  132. package/lib/sdk/client/mixins/IAMMixin.d.ts +59 -0
  133. package/lib/sdk/client/mixins/IAMMixin.js +83 -0
  134. package/lib/sdk/client/mixins/RuntimesMixin.d.ts +134 -0
  135. package/lib/sdk/client/mixins/RuntimesMixin.js +221 -0
  136. package/lib/sdk/client/mixins/SpacerMixin.d.ts +184 -0
  137. package/lib/sdk/client/mixins/SpacerMixin.js +278 -0
  138. package/lib/sdk/client/models/Lexical.d.ts +156 -0
  139. package/lib/sdk/client/models/Lexical.js +275 -0
  140. package/lib/sdk/client/models/Notebook.d.ts +174 -0
  141. package/lib/sdk/client/models/Notebook.js +311 -0
  142. package/lib/sdk/client/models/Runtime.d.ts +221 -0
  143. package/lib/sdk/client/models/Runtime.js +341 -0
  144. package/lib/sdk/client/models/Snapshot.d.ts +156 -0
  145. package/lib/sdk/client/models/Snapshot.js +244 -0
  146. package/lib/sdk/client/models/Space.d.ts +182 -0
  147. package/lib/sdk/client/models/Space.js +276 -0
  148. package/lib/sdk/client/models/__tests__/Lexical.test.d.ts +1 -0
  149. package/lib/sdk/client/models/__tests__/Lexical.test.js +288 -0
  150. package/lib/sdk/client/models/__tests__/Notebook.test.d.ts +1 -0
  151. package/lib/sdk/client/models/__tests__/Notebook.test.js +206 -0
  152. package/lib/sdk/client/models/__tests__/Runtime.test.d.ts +1 -0
  153. package/lib/sdk/client/models/__tests__/Runtime.test.js +133 -0
  154. package/lib/sdk/client/models/__tests__/Snapshot.test.d.ts +1 -0
  155. package/lib/sdk/client/models/__tests__/Snapshot.test.js +244 -0
  156. package/lib/sdk/client/models/__tests__/Space.test.d.ts +1 -0
  157. package/lib/sdk/client/models/__tests__/Space.test.js +334 -0
  158. package/lib/sdk/client/models/index.d.ts +30 -0
  159. package/lib/sdk/client/models/index.js +30 -0
  160. package/lib/sdk/client/utils/mixins.d.ts +42 -0
  161. package/lib/sdk/client/utils/mixins.js +47 -0
  162. package/lib/sdk/index.d.ts +26 -0
  163. package/lib/sdk/index.js +32 -0
  164. package/lib/sdk/stateful/index.d.ts +3 -0
  165. package/lib/sdk/stateful/index.js +7 -0
  166. package/lib/{api → sdk/stateful}/runtimes/actions.d.ts +1 -1
  167. package/lib/{api → sdk/stateful}/runtimes/actions.js +3 -3
  168. package/lib/{api → sdk/stateful}/runtimes/apis.d.ts +1 -1
  169. package/lib/sdk/stateful/runtimes/apis.js +5 -0
  170. package/lib/sdk/stateful/runtimes/index.d.ts +5 -0
  171. package/lib/sdk/stateful/runtimes/index.js +9 -0
  172. package/lib/sdk/stateful/runtimes/snapshots.d.ts +25 -0
  173. package/lib/sdk/stateful/runtimes/snapshots.js +150 -0
  174. package/lib/services/DatalayerServiceManager.js +1 -1
  175. package/lib/state/substates/IAMState.js +1 -1
  176. package/lib/state/substates/RuntimesState.d.ts +1 -1
  177. package/lib/state/substates/RuntimesState.js +1 -1
  178. package/lib/state/substates/SurveysState.js +1 -1
  179. package/lib/test-setup.js +1 -0
  180. package/package.json +19 -9
  181. /package/lib/api/{runtimes/apis.js → types/iam.js} +0 -0
  182. /package/lib/{api → sdk/stateful}/jupyter/exec/Python.d.ts +0 -0
  183. /package/lib/{api → sdk/stateful}/jupyter/exec/Python.js +0 -0
  184. /package/lib/{api → sdk/stateful}/jupyter/exec/Snippets.d.ts +0 -0
  185. /package/lib/{api → sdk/stateful}/jupyter/exec/Snippets.js +0 -0
  186. /package/lib/{api → sdk/stateful}/jupyter/exec/index.d.ts +0 -0
  187. /package/lib/{api → sdk/stateful}/jupyter/exec/index.js +0 -0
  188. /package/lib/{api → sdk/stateful}/jupyter/index.d.ts +0 -0
  189. /package/lib/{api → sdk/stateful}/jupyter/index.js +0 -0
  190. /package/lib/{api → sdk/stateful}/jupyter/kernelsHandler.d.ts +0 -0
  191. /package/lib/{api → sdk/stateful}/jupyter/kernelsHandler.js +0 -0
  192. /package/lib/{api → sdk/stateful}/runtimes/settings.d.ts +0 -0
  193. /package/lib/{api → sdk/stateful}/runtimes/settings.js +0 -0
  194. /package/lib/{api → sdk/stateful}/runtimes/utils.d.ts +0 -0
  195. /package/lib/{api → sdk/stateful}/runtimes/utils.js +0 -0
@@ -0,0 +1,288 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * @module sdk/client/models/__tests__/Lexical.test
7
+ * @description Tests for the Lexical domain model.
8
+ */
9
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
10
+ import { Lexical } from '../Lexical';
11
+ import { lexicals, items } from '../../../../api/spacer';
12
+ // Mock the spacer API
13
+ vi.mock('../../../../api/spacer', () => ({
14
+ lexicals: {
15
+ getLexical: vi.fn(),
16
+ updateLexical: vi.fn(),
17
+ },
18
+ items: {
19
+ deleteItem: vi.fn(),
20
+ },
21
+ }));
22
+ describe('Lexical Model', () => {
23
+ const mockLexicalData = {
24
+ id: 'lexical-123',
25
+ uid: 'lexical-uid-456',
26
+ name: 'Project Documentation', // API uses name field
27
+ content: {
28
+ type: 'doc',
29
+ content: [
30
+ {
31
+ type: 'paragraph',
32
+ content: [
33
+ {
34
+ type: 'text',
35
+ text: 'This is a sample document',
36
+ },
37
+ ],
38
+ },
39
+ ],
40
+ },
41
+ space_id: 'space-789',
42
+ owner_id: 'user-abc',
43
+ created_at: '2023-01-01T10:00:00Z',
44
+ updated_at: '2023-01-02T15:30:00Z',
45
+ };
46
+ let mockSDK;
47
+ let lexical;
48
+ beforeEach(() => {
49
+ mockSDK = {
50
+ getToken: vi.fn().mockReturnValue('mock-token'),
51
+ getSpacerRunUrl: vi.fn().mockReturnValue('https://spacer.example.com'),
52
+ };
53
+ lexical = new Lexical(mockLexicalData, mockSDK);
54
+ vi.clearAllMocks();
55
+ });
56
+ describe('Static Properties', () => {
57
+ it('should return correct id', () => {
58
+ expect(lexical.id).toBe('lexical-123');
59
+ });
60
+ it('should return correct uid', () => {
61
+ expect(lexical.uid).toBe('lexical-uid-456');
62
+ });
63
+ it('should return correct space ID', () => {
64
+ expect(lexical.spaceId).toBe('space-789');
65
+ });
66
+ it('should return correct owner ID', () => {
67
+ expect(lexical.ownerId).toBe('user-abc');
68
+ });
69
+ it('should return correct created date', () => {
70
+ expect(lexical.createdAt).toEqual(new Date('2023-01-01T10:00:00Z'));
71
+ });
72
+ it('should handle minimal data gracefully', () => {
73
+ const minimalData = {
74
+ id: 'lexical-minimal',
75
+ uid: 'lexical-uid-minimal',
76
+ name: 'Minimal Document',
77
+ space_id: 'space-minimal',
78
+ owner_id: 'user-minimal',
79
+ created_at: '2023-01-01T08:00:00Z',
80
+ };
81
+ const minimalLexical = new Lexical(minimalData, mockSDK);
82
+ expect(minimalLexical.id).toBe('lexical-minimal');
83
+ expect(minimalLexical.uid).toBe('lexical-uid-minimal');
84
+ expect(minimalLexical.spaceId).toBe('space-minimal');
85
+ });
86
+ });
87
+ describe('Dynamic Methods', () => {
88
+ beforeEach(() => {
89
+ lexicals.getLexical.mockResolvedValue({
90
+ success: true,
91
+ document: { ...mockLexicalData, name: 'Updated Documentation' },
92
+ });
93
+ });
94
+ it('should fetch fresh name from API and update internal data', async () => {
95
+ const name = await lexical.getName();
96
+ expect(name).toBe('Updated Documentation');
97
+ expect(lexicals.getLexical).toHaveBeenCalledWith('mock-token', 'lexical-uid-456', // Use uid, not id
98
+ 'https://spacer.example.com');
99
+ });
100
+ it('should fetch fresh content from API', async () => {
101
+ const updatedContent = {
102
+ type: 'doc',
103
+ content: [
104
+ {
105
+ type: 'paragraph',
106
+ content: [
107
+ {
108
+ type: 'text',
109
+ text: 'Updated document content',
110
+ },
111
+ ],
112
+ },
113
+ ],
114
+ };
115
+ lexicals.getLexical.mockResolvedValue({
116
+ success: true,
117
+ document: { ...mockLexicalData, content: updatedContent },
118
+ });
119
+ const content = await lexical.getContent();
120
+ expect(content).toEqual(updatedContent);
121
+ expect(lexicals.getLexical).toHaveBeenCalledWith('mock-token', 'lexical-uid-456', // Use uid, not id
122
+ 'https://spacer.example.com');
123
+ });
124
+ it('should fetch fresh updated date from API', async () => {
125
+ const newUpdateTime = '2023-01-03T12:00:00Z';
126
+ lexicals.getLexical.mockResolvedValue({
127
+ success: true,
128
+ document: { ...mockLexicalData, updated_at: newUpdateTime },
129
+ });
130
+ const updatedAt = await lexical.getUpdatedAt();
131
+ expect(updatedAt).toEqual(new Date(newUpdateTime));
132
+ });
133
+ it('should handle missing updated_at by falling back to created_at', async () => {
134
+ lexicals.getLexical.mockResolvedValue({
135
+ success: true,
136
+ document: { ...mockLexicalData, updated_at: undefined },
137
+ });
138
+ const updatedAt = await lexical.getUpdatedAt();
139
+ expect(updatedAt).toEqual(new Date(mockLexicalData.created_at));
140
+ });
141
+ it('should handle missing content gracefully', async () => {
142
+ lexicals.getLexical.mockResolvedValue({
143
+ success: true,
144
+ document: { ...mockLexicalData, content: undefined },
145
+ });
146
+ const content = await lexical.getContent();
147
+ expect(content).toBeUndefined();
148
+ });
149
+ it('should update internal data when fetching dynamic properties', async () => {
150
+ const updatedData = {
151
+ ...mockLexicalData,
152
+ name: 'Fresh Name',
153
+ content: { type: 'doc', content: [] },
154
+ updated_at: '2023-01-04T10:00:00Z',
155
+ };
156
+ lexicals.getLexical.mockResolvedValue({
157
+ success: true,
158
+ document: updatedData,
159
+ });
160
+ await lexical.getName();
161
+ const json = await lexical.toJSON();
162
+ expect(json.name).toBe('Fresh Name');
163
+ expect(json.updated_at).toBe('2023-01-04T10:00:00Z');
164
+ });
165
+ });
166
+ describe('Action Methods', () => {
167
+ it('should update document and return new instance', async () => {
168
+ const updatedDocument = {
169
+ ...mockLexicalData,
170
+ name: 'New Documentation Title',
171
+ updated_at: '2023-01-03T14:00:00Z',
172
+ };
173
+ lexicals.updateLexical.mockResolvedValue({
174
+ success: true,
175
+ document: updatedDocument,
176
+ });
177
+ const updated = await lexical.update({
178
+ name: 'New Documentation Title',
179
+ description: 'Updated description',
180
+ });
181
+ expect(lexicals.updateLexical).toHaveBeenCalledWith('mock-token', 'lexical-uid-456', // Use uid, not id
182
+ { name: 'New Documentation Title', description: 'Updated description' }, 'https://spacer.example.com');
183
+ expect(updated).toBeInstanceOf(Lexical);
184
+ expect(updated.toString()).toBe('Lexical(lexical-123, New Documentation Title)');
185
+ });
186
+ it('should delete document and mark as deleted', async () => {
187
+ items.deleteItem.mockResolvedValue({ success: true });
188
+ await lexical.delete();
189
+ expect(items.deleteItem).toHaveBeenCalledWith('mock-token', 'lexical-uid-456', // Use uid, not id
190
+ 'https://spacer.example.com');
191
+ // After deletion, accessing properties should throw error
192
+ expect(() => lexical.id).toThrow('Lexical lexical-123 has been deleted and no longer exists');
193
+ });
194
+ });
195
+ describe('Utility Methods', () => {
196
+ it('should return JSON with fresh data', async () => {
197
+ const freshData = { ...mockLexicalData, name: 'Fresh Documentation' };
198
+ lexicals.getLexical.mockResolvedValue({
199
+ success: true,
200
+ document: freshData,
201
+ });
202
+ const json = await lexical.toJSON();
203
+ expect(json.name).toBe('Fresh Documentation');
204
+ expect(json.id).toBe('lexical-123');
205
+ });
206
+ it('should return string representation', () => {
207
+ expect(lexical.toString()).toBe('Lexical(lexical-123, Project Documentation)');
208
+ });
209
+ });
210
+ describe('Deletion State Management', () => {
211
+ beforeEach(async () => {
212
+ items.deleteItem.mockResolvedValue({ success: true });
213
+ await lexical.delete();
214
+ });
215
+ it('should throw error when accessing static properties after deletion', () => {
216
+ expect(() => lexical.id).toThrow('Lexical lexical-123 has been deleted and no longer exists');
217
+ expect(() => lexical.uid).toThrow('Lexical lexical-123 has been deleted and no longer exists');
218
+ expect(() => lexical.spaceId).toThrow('Lexical lexical-123 has been deleted and no longer exists');
219
+ expect(() => lexical.ownerId).toThrow('Lexical lexical-123 has been deleted and no longer exists');
220
+ expect(() => lexical.createdAt).toThrow('Lexical lexical-123 has been deleted and no longer exists');
221
+ });
222
+ it('should throw error when calling dynamic methods after deletion', async () => {
223
+ await expect(lexical.getName()).rejects.toThrow('Lexical lexical-123 has been deleted and no longer exists');
224
+ await expect(lexical.getContent()).rejects.toThrow('Lexical lexical-123 has been deleted and no longer exists');
225
+ await expect(lexical.getUpdatedAt()).rejects.toThrow('Lexical lexical-123 has been deleted and no longer exists');
226
+ });
227
+ it('should throw error when calling action methods after deletion', async () => {
228
+ await expect(lexical.update({ name: 'New Name' })).rejects.toThrow('Lexical lexical-123 has been deleted and no longer exists');
229
+ });
230
+ it('should throw error when calling utility methods after deletion', async () => {
231
+ await expect(lexical.toJSON()).rejects.toThrow('Lexical lexical-123 has been deleted and no longer exists');
232
+ expect(() => lexical.toString()).toThrow('Lexical lexical-123 has been deleted and no longer exists');
233
+ });
234
+ });
235
+ describe('Error Handling', () => {
236
+ it('should handle API errors gracefully in dynamic methods', async () => {
237
+ lexicals.getLexical.mockRejectedValue(new Error('API Error'));
238
+ await expect(lexical.getName()).rejects.toThrow('API Error');
239
+ });
240
+ it('should handle missing document in API response', async () => {
241
+ lexicals.getLexical.mockResolvedValue({
242
+ success: false,
243
+ message: 'Document not found',
244
+ document: null,
245
+ });
246
+ const name = await lexical.getName();
247
+ // Should return the current name from internal data when API returns null
248
+ expect(name).toBe('Project Documentation');
249
+ });
250
+ it('should handle API errors in update method', async () => {
251
+ lexicals.updateLexical.mockRejectedValue(new Error('Update failed'));
252
+ await expect(lexical.update({ name: 'New Name' })).rejects.toThrow('Update failed');
253
+ });
254
+ it('should handle API errors in delete method', async () => {
255
+ items.deleteItem.mockRejectedValue(new Error('Delete failed'));
256
+ await expect(lexical.delete()).rejects.toThrow('Delete failed');
257
+ });
258
+ });
259
+ describe('Edge Cases', () => {
260
+ it('should handle empty content gracefully', async () => {
261
+ lexicals.getLexical.mockResolvedValue({
262
+ success: true,
263
+ document: { ...mockLexicalData, content: {} },
264
+ });
265
+ const content = await lexical.getContent();
266
+ expect(content).toEqual({});
267
+ });
268
+ it('should handle null content gracefully', async () => {
269
+ lexicals.getLexical.mockResolvedValue({
270
+ success: true,
271
+ document: { ...mockLexicalData, content: null },
272
+ });
273
+ const content = await lexical.getContent();
274
+ expect(content).toBeNull();
275
+ });
276
+ it('should handle partial update requests', async () => {
277
+ const partialUpdate = { name: 'Only Name Updated' };
278
+ lexicals.updateLexical.mockResolvedValue({
279
+ success: true,
280
+ document: { ...mockLexicalData, name: 'Only Name Updated' },
281
+ });
282
+ const updated = await lexical.update(partialUpdate);
283
+ expect(lexicals.updateLexical).toHaveBeenCalledWith('mock-token', 'lexical-uid-456', // Use uid, not id
284
+ partialUpdate, 'https://spacer.example.com');
285
+ expect(updated.toString()).toBe('Lexical(lexical-123, Only Name Updated)');
286
+ });
287
+ });
288
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,206 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * @module sdk/client/models/__tests__/Notebook.test
7
+ * @description Tests for the Notebook domain model.
8
+ */
9
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
10
+ import { Notebook } from '../Notebook';
11
+ import { notebooks } from '../../../../api/spacer';
12
+ // Mock the notebooks API
13
+ vi.mock('../../../../api/spacer', () => ({
14
+ notebooks: {
15
+ getNotebook: vi.fn(),
16
+ updateNotebook: vi.fn(),
17
+ },
18
+ items: {
19
+ deleteItem: vi.fn(),
20
+ },
21
+ }));
22
+ describe('Notebook Model', () => {
23
+ const mockNotebookData = {
24
+ id: 'notebook-123',
25
+ uid: 'notebook-uid-456',
26
+ name: 'Test Notebook',
27
+ path: '/test/notebook.ipynb',
28
+ content: {
29
+ cells: [{ cell_type: 'code', source: 'print("hello")' }],
30
+ metadata: {},
31
+ nbformat: 4,
32
+ nbformat_minor: 2,
33
+ },
34
+ space_id: 'space-789',
35
+ owner_id: 'user-abc',
36
+ created_at: '2023-01-01T00:00:00Z',
37
+ updated_at: '2023-01-01T00:30:00Z',
38
+ version: 1,
39
+ kernel_spec: {
40
+ display_name: 'Python 3',
41
+ language: 'python',
42
+ name: 'python3',
43
+ },
44
+ metadata: {
45
+ custom_field: 'custom_value',
46
+ },
47
+ };
48
+ let mockSDK;
49
+ let notebook;
50
+ beforeEach(() => {
51
+ mockSDK = {
52
+ getToken: vi.fn().mockReturnValue('mock-token'),
53
+ getSpacerRunUrl: vi.fn().mockReturnValue('https://api.example.com'),
54
+ };
55
+ notebook = new Notebook(mockNotebookData, mockSDK);
56
+ vi.clearAllMocks();
57
+ });
58
+ describe('Static Properties', () => {
59
+ it('should return correct id', () => {
60
+ expect(notebook.id).toBe('notebook-123');
61
+ });
62
+ it('should return correct uid', () => {
63
+ expect(notebook.uid).toBe('notebook-uid-456');
64
+ });
65
+ it('should return correct path', () => {
66
+ expect(notebook.path).toBe('/test/notebook.ipynb');
67
+ });
68
+ it('should return correct space id', () => {
69
+ expect(notebook.spaceId).toBe('space-789');
70
+ });
71
+ it('should return correct owner id', () => {
72
+ expect(notebook.ownerId).toBe('user-abc');
73
+ });
74
+ it('should return correct created date', () => {
75
+ expect(notebook.createdAt).toEqual(new Date('2023-01-01T00:00:00Z'));
76
+ });
77
+ it('should return correct version', () => {
78
+ expect(notebook.version).toBe(1);
79
+ });
80
+ it('should return correct metadata', () => {
81
+ expect(notebook.metadata).toEqual({ custom_field: 'custom_value' });
82
+ });
83
+ it('should handle missing optional fields gracefully', () => {
84
+ const minimalData = {
85
+ id: 'notebook-123',
86
+ uid: 'notebook-uid-456',
87
+ name: 'Minimal Notebook',
88
+ path: '/minimal.ipynb',
89
+ space_id: 'space-789',
90
+ owner_id: 'user-abc',
91
+ created_at: '2023-01-01T00:00:00Z',
92
+ };
93
+ const minimalNotebook = new Notebook(minimalData, mockSDK);
94
+ expect(minimalNotebook.version).toBe(0);
95
+ expect(minimalNotebook.metadata).toEqual({});
96
+ });
97
+ });
98
+ describe('Dynamic Methods', () => {
99
+ beforeEach(() => {
100
+ notebooks.getNotebook.mockResolvedValue({
101
+ notebook: { ...mockNotebookData, name: 'Updated Name' },
102
+ });
103
+ });
104
+ it('should fetch fresh name from API and update internal data', async () => {
105
+ const name = await notebook.getName();
106
+ expect(name).toBe('Updated Name');
107
+ expect(notebooks.getNotebook).toHaveBeenCalledWith('mock-token', 'notebook-uid-456', // Use uid, not id
108
+ 'https://api.example.com');
109
+ });
110
+ it('should fetch fresh content from API', async () => {
111
+ const updatedContent = { cells: [], metadata: {}, nbformat: 4 };
112
+ notebooks.getNotebook.mockResolvedValue({
113
+ notebook: { ...mockNotebookData, content: updatedContent },
114
+ });
115
+ const content = await notebook.getContent();
116
+ expect(content).toEqual(updatedContent);
117
+ expect(notebooks.getNotebook).toHaveBeenCalledWith('mock-token', 'notebook-uid-456', // Use uid, not id
118
+ 'https://api.example.com');
119
+ });
120
+ it('should fetch fresh kernel spec from API', async () => {
121
+ const updatedKernelSpec = {
122
+ display_name: 'Python 3.9',
123
+ language: 'python',
124
+ };
125
+ notebooks.getNotebook.mockResolvedValue({
126
+ notebook: { ...mockNotebookData, kernel_spec: updatedKernelSpec },
127
+ });
128
+ const kernelSpec = await notebook.getKernelSpec();
129
+ expect(kernelSpec).toEqual(updatedKernelSpec);
130
+ });
131
+ it('should fetch fresh updated date from API', async () => {
132
+ const newUpdatedAt = '2023-01-02T12:00:00Z';
133
+ notebooks.getNotebook.mockResolvedValue({
134
+ notebook: { ...mockNotebookData, updated_at: newUpdatedAt },
135
+ });
136
+ const updatedAt = await notebook.getUpdatedAt();
137
+ expect(updatedAt).toEqual(new Date(newUpdatedAt));
138
+ });
139
+ it('should use created_at when updated_at is missing', async () => {
140
+ notebooks.getNotebook.mockResolvedValue({
141
+ notebook: { ...mockNotebookData, updated_at: undefined },
142
+ });
143
+ const updatedAt = await notebook.getUpdatedAt();
144
+ expect(updatedAt).toEqual(new Date(mockNotebookData.created_at));
145
+ });
146
+ it('should update internal data when fetching dynamic properties', async () => {
147
+ const updatedData = {
148
+ ...mockNotebookData,
149
+ name: 'Fresh Name',
150
+ version: 2,
151
+ };
152
+ notebooks.getNotebook.mockResolvedValue({
153
+ notebook: updatedData,
154
+ });
155
+ await notebook.getName();
156
+ const json = await notebook.toJSON();
157
+ expect(json.name).toBe('Fresh Name');
158
+ expect(json.version).toBe(2);
159
+ });
160
+ });
161
+ describe('Action Methods', () => {
162
+ it('should update notebook and return new instance', async () => {
163
+ const updateData = { name: 'New Name', description: 'New Description' };
164
+ const updatedNotebookData = { ...mockNotebookData, name: 'New Name' };
165
+ notebooks.updateNotebook.mockResolvedValue({
166
+ notebook: updatedNotebookData,
167
+ });
168
+ const updatedNotebook = await notebook.update(updateData);
169
+ expect(notebooks.updateNotebook).toHaveBeenCalledWith('mock-token', 'notebook-uid-456', // Use uid, not id
170
+ updateData, 'https://api.example.com');
171
+ expect(updatedNotebook).toBeInstanceOf(Notebook);
172
+ expect(updatedNotebook.id).toBe('notebook-123');
173
+ });
174
+ it('should delete notebook', async () => {
175
+ const { items } = await import('../../../../api/spacer');
176
+ items.deleteItem.mockResolvedValue({});
177
+ await notebook.delete();
178
+ expect(items.deleteItem).toHaveBeenCalledWith('mock-token', 'notebook-uid-456', // Use uid, not id
179
+ 'https://api.example.com');
180
+ });
181
+ });
182
+ describe('Utility Methods', () => {
183
+ it('should return JSON with fresh data', async () => {
184
+ const freshData = { ...mockNotebookData, name: 'Latest Name' };
185
+ notebooks.getNotebook.mockResolvedValue({ notebook: freshData });
186
+ const json = await notebook.toJSON();
187
+ expect(json.name).toBe('Latest Name');
188
+ expect(json.id).toBe('notebook-123');
189
+ });
190
+ it('should return string representation', () => {
191
+ expect(notebook.toString()).toBe('Notebook(notebook-123, Test Notebook)');
192
+ });
193
+ });
194
+ describe('Error Handling', () => {
195
+ it('should handle API errors gracefully in dynamic methods', async () => {
196
+ notebooks.getNotebook.mockRejectedValue(new Error('API Error'));
197
+ await expect(notebook.getName()).rejects.toThrow('API Error');
198
+ });
199
+ it('should handle missing notebook in API response', async () => {
200
+ notebooks.getNotebook.mockResolvedValue({ notebook: null });
201
+ const name = await notebook.getName();
202
+ // Should return the current name from internal data when API returns null
203
+ expect(name).toBe('Test Notebook');
204
+ });
205
+ });
206
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,133 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * @module sdk/client/models/__tests__/Runtime.test
7
+ * @description Tests for the Runtime domain model.
8
+ */
9
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
10
+ import { Runtime } from '../Runtime';
11
+ describe('Runtime Model', () => {
12
+ const mockRuntimeData = {
13
+ pod_name: 'test-runtime-123',
14
+ uid: 'runtime-uid-456',
15
+ environment_name: 'python-cpu',
16
+ state: 'running',
17
+ burning_rate: 1.5,
18
+ given_name: 'Test Runtime',
19
+ jupyter_url: 'https://jupyter.example.com',
20
+ jupyter_token: 'jupyter-token-789',
21
+ type: 'notebook',
22
+ created_at: '2023-01-01T00:00:00Z',
23
+ started_at: '2023-01-01T00:01:00Z',
24
+ expired_at: '2023-01-01T01:00:00Z',
25
+ };
26
+ let mockSDK;
27
+ let runtime;
28
+ beforeEach(() => {
29
+ mockSDK = {
30
+ getRuntime: vi.fn(),
31
+ deleteRuntime: vi.fn(),
32
+ createSnapshot: vi.fn(),
33
+ };
34
+ runtime = new Runtime(mockRuntimeData, mockSDK);
35
+ });
36
+ describe('Static Properties', () => {
37
+ it('should return correct pod name', () => {
38
+ expect(runtime.podName).toBe('test-runtime-123');
39
+ });
40
+ it('should return correct uid', () => {
41
+ expect(runtime.uid).toBe('runtime-uid-456');
42
+ });
43
+ it('should return correct environment name', () => {
44
+ expect(runtime.environmentName).toBe('python-cpu');
45
+ });
46
+ it('should return correct burning rate', () => {
47
+ expect(runtime.burningRate).toBe(1.5);
48
+ });
49
+ it('should return correct given name', () => {
50
+ expect(runtime.givenName).toBe('Test Runtime');
51
+ });
52
+ it('should return correct jupyter url', () => {
53
+ expect(runtime.jupyterUrl).toBe('https://jupyter.example.com');
54
+ });
55
+ it('should return correct jupyter token', () => {
56
+ expect(runtime.jupyterToken).toBe('jupyter-token-789');
57
+ });
58
+ it('should return correct type', () => {
59
+ expect(runtime.type).toBe('notebook');
60
+ });
61
+ it('should return correct dates', () => {
62
+ expect(runtime.createdAt).toEqual(new Date('2023-01-01T00:00:00Z'));
63
+ expect(runtime.startedAt).toEqual(new Date('2023-01-01T00:01:00Z'));
64
+ expect(runtime.expiredAt).toEqual(new Date('2023-01-01T01:00:00Z'));
65
+ });
66
+ });
67
+ describe('Dynamic State', () => {
68
+ it('should fetch fresh state from API', async () => {
69
+ const freshRuntime = new Runtime({ ...mockRuntimeData, state: 'stopped' }, mockSDK);
70
+ mockSDK.getRuntime.mockResolvedValue(freshRuntime);
71
+ const state = await runtime.getState();
72
+ expect(state).toBe('stopped');
73
+ expect(mockSDK.getRuntime).toHaveBeenCalledWith('test-runtime-123');
74
+ });
75
+ it('should update internal data when getting state', async () => {
76
+ const freshRuntime = new Runtime({ ...mockRuntimeData, state: 'error' }, mockSDK);
77
+ mockSDK.getRuntime.mockResolvedValue(freshRuntime);
78
+ await runtime.getState();
79
+ const json = await runtime.toJSON();
80
+ expect(json.state).toBe('error');
81
+ });
82
+ });
83
+ describe('State Checking Methods', () => {
84
+ beforeEach(() => {
85
+ const freshRuntime = new Runtime(mockRuntimeData, mockSDK);
86
+ mockSDK.getRuntime.mockResolvedValue(freshRuntime);
87
+ });
88
+ it('should check if runtime is running', async () => {
89
+ expect(await runtime.isRunning()).toBe(true);
90
+ });
91
+ it('should check if runtime is starting', async () => {
92
+ const startingRuntime = new Runtime({ ...mockRuntimeData, state: 'starting' }, mockSDK);
93
+ mockSDK.getRuntime.mockResolvedValue(startingRuntime);
94
+ expect(await runtime.isStarting()).toBe(true);
95
+ expect(await runtime.isRunning()).toBe(false);
96
+ });
97
+ it('should check if runtime has error', async () => {
98
+ const errorRuntime = new Runtime({ ...mockRuntimeData, state: 'error' }, mockSDK);
99
+ mockSDK.getRuntime.mockResolvedValue(errorRuntime);
100
+ expect(await runtime.hasError()).toBe(true);
101
+ });
102
+ });
103
+ describe('Action Methods', () => {
104
+ it('should delete runtime', async () => {
105
+ await runtime.delete();
106
+ expect(mockSDK.deleteRuntime).toHaveBeenCalledWith('test-runtime-123');
107
+ });
108
+ it('should create snapshot', async () => {
109
+ const mockSnapshot = { uid: 'snap-123', name: 'test-snap' };
110
+ mockSDK.createSnapshot.mockResolvedValue({
111
+ snapshot: mockSnapshot,
112
+ });
113
+ await runtime.createSnapshot('test-snap', 'desc');
114
+ expect(mockSDK.createSnapshot).toHaveBeenCalledWith({
115
+ pod_name: 'test-runtime-123',
116
+ name: 'test-snap',
117
+ description: 'desc',
118
+ stop: false,
119
+ });
120
+ });
121
+ });
122
+ describe('Utility Methods', () => {
123
+ it('should return JSON with fresh data', async () => {
124
+ const freshRuntime = new Runtime({ ...mockRuntimeData, state: 'stopped' }, mockSDK);
125
+ mockSDK.getRuntime.mockResolvedValue(freshRuntime);
126
+ const json = await runtime.toJSON();
127
+ expect(json.state).toBe('stopped');
128
+ });
129
+ it('should return string representation', () => {
130
+ expect(runtime.toString()).toBe('Runtime(test-runtime-123, python-cpu)');
131
+ });
132
+ });
133
+ });
@@ -0,0 +1 @@
1
+ export {};