@powerhousedao/reactor 5.0.0-staging.9 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/src/cache/buffer/ring-buffer.d.ts +37 -0
  2. package/dist/src/cache/buffer/ring-buffer.d.ts.map +1 -0
  3. package/dist/src/cache/buffer/ring-buffer.js +69 -0
  4. package/dist/src/cache/buffer/ring-buffer.js.map +1 -0
  5. package/dist/src/cache/kysely-write-cache.d.ts +133 -0
  6. package/dist/src/cache/kysely-write-cache.d.ts.map +1 -0
  7. package/dist/src/cache/kysely-write-cache.js +375 -0
  8. package/dist/src/cache/kysely-write-cache.js.map +1 -0
  9. package/dist/src/cache/lru/lru-tracker.d.ts +15 -0
  10. package/dist/src/cache/lru/lru-tracker.d.ts.map +1 -0
  11. package/dist/src/cache/lru/lru-tracker.js +96 -0
  12. package/dist/src/cache/lru/lru-tracker.js.map +1 -0
  13. package/dist/src/cache/types.d.ts +42 -0
  14. package/dist/src/cache/types.d.ts.map +1 -0
  15. package/dist/src/cache/types.js +2 -0
  16. package/dist/src/cache/types.js.map +1 -0
  17. package/dist/src/cache/write/interfaces.d.ts +83 -0
  18. package/dist/src/cache/write/interfaces.d.ts.map +1 -0
  19. package/dist/src/cache/write/interfaces.js +2 -0
  20. package/dist/src/cache/write/interfaces.js.map +1 -0
  21. package/dist/src/client/reactor-client.d.ts +103 -0
  22. package/dist/src/client/reactor-client.d.ts.map +1 -0
  23. package/dist/src/client/reactor-client.js +184 -0
  24. package/dist/src/client/reactor-client.js.map +1 -0
  25. package/dist/src/client/types.d.ts +213 -0
  26. package/dist/src/client/types.d.ts.map +1 -0
  27. package/dist/src/client/types.js +14 -0
  28. package/dist/src/client/types.js.map +1 -0
  29. package/dist/src/core/builder.d.ts +20 -0
  30. package/dist/src/core/builder.d.ts.map +1 -0
  31. package/dist/src/core/builder.js +47 -0
  32. package/dist/src/core/builder.js.map +1 -0
  33. package/dist/src/{reactor.d.ts → core/reactor.d.ts} +14 -30
  34. package/dist/src/core/reactor.d.ts.map +1 -0
  35. package/dist/src/{reactor.js → core/reactor.js} +162 -114
  36. package/dist/src/core/reactor.js.map +1 -0
  37. package/dist/src/{interfaces/reactor.d.ts → core/types.d.ts} +7 -7
  38. package/dist/src/core/types.d.ts.map +1 -0
  39. package/dist/src/core/types.js +2 -0
  40. package/dist/src/core/types.js.map +1 -0
  41. package/dist/src/{utils.d.ts → core/utils.d.ts} +2 -2
  42. package/dist/src/core/utils.d.ts.map +1 -0
  43. package/dist/src/core/utils.js.map +1 -0
  44. package/dist/src/events/event-bus.d.ts +2 -2
  45. package/dist/src/events/event-bus.d.ts.map +1 -1
  46. package/dist/src/events/event-bus.js +1 -1
  47. package/dist/src/events/event-bus.js.map +1 -1
  48. package/dist/src/events/interfaces.d.ts +1 -1
  49. package/dist/src/events/interfaces.d.ts.map +1 -1
  50. package/dist/src/events/types.d.ts +14 -0
  51. package/dist/src/events/types.d.ts.map +1 -1
  52. package/dist/src/events/types.js +6 -0
  53. package/dist/src/events/types.js.map +1 -1
  54. package/dist/src/executor/interfaces.d.ts +2 -2
  55. package/dist/src/executor/interfaces.d.ts.map +1 -1
  56. package/dist/src/executor/simple-job-executor-manager.d.ts +6 -1
  57. package/dist/src/executor/simple-job-executor-manager.d.ts.map +1 -1
  58. package/dist/src/executor/simple-job-executor-manager.js +121 -37
  59. package/dist/src/executor/simple-job-executor-manager.js.map +1 -1
  60. package/dist/src/executor/simple-job-executor.d.ts +32 -3
  61. package/dist/src/executor/simple-job-executor.d.ts.map +1 -1
  62. package/dist/src/executor/simple-job-executor.js +451 -17
  63. package/dist/src/executor/simple-job-executor.js.map +1 -1
  64. package/dist/src/executor/types.d.ts +10 -3
  65. package/dist/src/executor/types.d.ts.map +1 -1
  66. package/dist/src/executor/types.js.map +1 -1
  67. package/dist/src/executor/util.d.ts +47 -0
  68. package/dist/src/executor/util.d.ts.map +1 -0
  69. package/dist/src/executor/util.js +113 -0
  70. package/dist/src/executor/util.js.map +1 -0
  71. package/dist/src/index.d.ts +23 -3
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +30 -2
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/job-tracker/in-memory-job-tracker.d.ts +16 -0
  76. package/dist/src/job-tracker/in-memory-job-tracker.d.ts.map +1 -0
  77. package/dist/src/job-tracker/in-memory-job-tracker.js +78 -0
  78. package/dist/src/job-tracker/in-memory-job-tracker.js.map +1 -0
  79. package/dist/src/job-tracker/index.d.ts +3 -0
  80. package/dist/src/job-tracker/index.d.ts.map +1 -0
  81. package/dist/src/job-tracker/index.js +2 -0
  82. package/dist/src/job-tracker/index.js.map +1 -0
  83. package/dist/src/job-tracker/interfaces.d.ts +41 -0
  84. package/dist/src/job-tracker/interfaces.d.ts.map +1 -0
  85. package/dist/src/job-tracker/interfaces.js +2 -0
  86. package/dist/src/job-tracker/interfaces.js.map +1 -0
  87. package/dist/src/queue/interfaces.d.ts +6 -5
  88. package/dist/src/queue/interfaces.d.ts.map +1 -1
  89. package/dist/src/queue/job-execution-handle.d.ts +5 -3
  90. package/dist/src/queue/job-execution-handle.d.ts.map +1 -1
  91. package/dist/src/queue/job-execution-handle.js +2 -2
  92. package/dist/src/queue/job-execution-handle.js.map +1 -1
  93. package/dist/src/queue/queue.d.ts +7 -5
  94. package/dist/src/queue/queue.d.ts.map +1 -1
  95. package/dist/src/queue/queue.js +61 -27
  96. package/dist/src/queue/queue.js.map +1 -1
  97. package/dist/src/queue/types.d.ts +11 -8
  98. package/dist/src/queue/types.d.ts.map +1 -1
  99. package/dist/src/queue/types.js.map +1 -1
  100. package/dist/src/read-models/coordinator.d.ts +38 -0
  101. package/dist/src/read-models/coordinator.d.ts.map +1 -0
  102. package/dist/src/read-models/coordinator.js +62 -0
  103. package/dist/src/read-models/coordinator.js.map +1 -0
  104. package/dist/src/read-models/document-view.d.ts +20 -0
  105. package/dist/src/read-models/document-view.d.ts.map +1 -0
  106. package/dist/src/read-models/document-view.js +365 -0
  107. package/dist/src/read-models/document-view.js.map +1 -0
  108. package/dist/src/read-models/interfaces.d.ts +29 -0
  109. package/dist/src/read-models/interfaces.d.ts.map +1 -0
  110. package/dist/src/read-models/interfaces.js +2 -0
  111. package/dist/src/read-models/interfaces.js.map +1 -0
  112. package/dist/src/read-models/types.d.ts +46 -0
  113. package/dist/src/read-models/types.d.ts.map +1 -0
  114. package/dist/src/read-models/types.js +2 -0
  115. package/dist/src/read-models/types.js.map +1 -0
  116. package/dist/src/registry/implementation.js +1 -1
  117. package/dist/src/registry/implementation.js.map +1 -1
  118. package/dist/src/shared/awaiter.d.ts +32 -0
  119. package/dist/src/shared/awaiter.d.ts.map +1 -0
  120. package/dist/src/shared/awaiter.js +132 -0
  121. package/dist/src/shared/awaiter.js.map +1 -0
  122. package/dist/src/shared/errors.d.ts +17 -0
  123. package/dist/src/shared/errors.d.ts.map +1 -0
  124. package/dist/src/shared/errors.js +33 -0
  125. package/dist/src/shared/errors.js.map +1 -0
  126. package/dist/src/shared/factories.d.ts +1 -1
  127. package/dist/src/shared/factories.d.ts.map +1 -1
  128. package/dist/src/shared/types.d.ts +17 -1
  129. package/dist/src/shared/types.d.ts.map +1 -1
  130. package/dist/src/shared/types.js +5 -0
  131. package/dist/src/shared/types.js.map +1 -1
  132. package/dist/src/signer/passthrough-signer.d.ts +6 -0
  133. package/dist/src/signer/passthrough-signer.d.ts.map +1 -0
  134. package/dist/src/signer/passthrough-signer.js +6 -0
  135. package/dist/src/signer/passthrough-signer.js.map +1 -0
  136. package/dist/src/signer/types.d.ts +15 -0
  137. package/dist/src/signer/types.d.ts.map +1 -0
  138. package/dist/src/signer/types.js +2 -0
  139. package/dist/src/signer/types.js.map +1 -0
  140. package/dist/src/storage/interfaces.d.ts +121 -0
  141. package/dist/src/storage/interfaces.d.ts.map +1 -0
  142. package/dist/src/storage/interfaces.js +19 -0
  143. package/dist/src/storage/interfaces.js.map +1 -0
  144. package/dist/src/storage/kysely/keyframe-store.d.ts +15 -0
  145. package/dist/src/storage/kysely/keyframe-store.d.ts.map +1 -0
  146. package/dist/src/storage/kysely/keyframe-store.js +71 -0
  147. package/dist/src/storage/kysely/keyframe-store.js.map +1 -0
  148. package/dist/src/storage/kysely/store.d.ts +15 -0
  149. package/dist/src/storage/kysely/store.d.ts.map +1 -0
  150. package/dist/src/storage/kysely/store.js +196 -0
  151. package/dist/src/storage/kysely/store.js.map +1 -0
  152. package/dist/src/storage/kysely/types.d.ts +39 -0
  153. package/dist/src/storage/kysely/types.d.ts.map +1 -0
  154. package/dist/src/storage/kysely/types.js +2 -0
  155. package/dist/src/storage/kysely/types.js.map +1 -0
  156. package/dist/src/storage/txn.d.ts +15 -0
  157. package/dist/src/storage/txn.d.ts.map +1 -0
  158. package/dist/src/storage/txn.js +41 -0
  159. package/dist/src/storage/txn.js.map +1 -0
  160. package/dist/src/subs/default-error-handler.d.ts +13 -0
  161. package/dist/src/subs/default-error-handler.d.ts.map +1 -0
  162. package/dist/src/subs/default-error-handler.js +27 -0
  163. package/dist/src/subs/default-error-handler.js.map +1 -0
  164. package/dist/src/subs/react-subscription-manager.d.ts +45 -0
  165. package/dist/src/subs/react-subscription-manager.d.ts.map +1 -0
  166. package/dist/src/subs/react-subscription-manager.js +185 -0
  167. package/dist/src/subs/react-subscription-manager.js.map +1 -0
  168. package/dist/src/subs/types.d.ts +64 -0
  169. package/dist/src/subs/types.d.ts.map +1 -0
  170. package/dist/src/subs/types.js +2 -0
  171. package/dist/src/subs/types.js.map +1 -0
  172. package/dist/src/utils/reshuffle.d.ts +30 -0
  173. package/dist/src/utils/reshuffle.d.ts.map +1 -0
  174. package/dist/src/utils/reshuffle.js +47 -0
  175. package/dist/src/utils/reshuffle.js.map +1 -0
  176. package/package.json +15 -9
  177. package/dist/bench/event-bus.bench.d.ts +0 -2
  178. package/dist/bench/event-bus.bench.d.ts.map +0 -1
  179. package/dist/bench/event-bus.bench.js +0 -228
  180. package/dist/bench/event-bus.bench.js.map +0 -1
  181. package/dist/bench/queue-only.bench.d.ts +0 -2
  182. package/dist/bench/queue-only.bench.d.ts.map +0 -1
  183. package/dist/bench/queue-only.bench.js +0 -46
  184. package/dist/bench/queue-only.bench.js.map +0 -1
  185. package/dist/bench/reactor-throughput.bench.d.ts +0 -2
  186. package/dist/bench/reactor-throughput.bench.d.ts.map +0 -1
  187. package/dist/bench/reactor-throughput.bench.js +0 -144
  188. package/dist/bench/reactor-throughput.bench.js.map +0 -1
  189. package/dist/src/interfaces/reactor.d.ts.map +0 -1
  190. package/dist/src/interfaces/reactor.js +0 -2
  191. package/dist/src/interfaces/reactor.js.map +0 -1
  192. package/dist/src/reactor.d.ts.map +0 -1
  193. package/dist/src/reactor.js.map +0 -1
  194. package/dist/src/utils.d.ts.map +0 -1
  195. package/dist/src/utils.js.map +0 -1
  196. package/dist/test/event-bus.test.d.ts +0 -2
  197. package/dist/test/event-bus.test.d.ts.map +0 -1
  198. package/dist/test/event-bus.test.js +0 -541
  199. package/dist/test/event-bus.test.js.map +0 -1
  200. package/dist/test/executor/executor-integration.test.d.ts +0 -2
  201. package/dist/test/executor/executor-integration.test.d.ts.map +0 -1
  202. package/dist/test/executor/executor-integration.test.js +0 -287
  203. package/dist/test/executor/executor-integration.test.js.map +0 -1
  204. package/dist/test/executor/job-execution-handle.test.d.ts +0 -2
  205. package/dist/test/executor/job-execution-handle.test.d.ts.map +0 -1
  206. package/dist/test/executor/job-execution-handle.test.js +0 -272
  207. package/dist/test/executor/job-execution-handle.test.js.map +0 -1
  208. package/dist/test/executor/simple-job-executor-manager.test.d.ts +0 -2
  209. package/dist/test/executor/simple-job-executor-manager.test.d.ts.map +0 -1
  210. package/dist/test/executor/simple-job-executor-manager.test.js +0 -132
  211. package/dist/test/executor/simple-job-executor-manager.test.js.map +0 -1
  212. package/dist/test/executor/simple-job-executor.test.d.ts +0 -2
  213. package/dist/test/executor/simple-job-executor.test.d.ts.map +0 -1
  214. package/dist/test/executor/simple-job-executor.test.js +0 -139
  215. package/dist/test/executor/simple-job-executor.test.js.map +0 -1
  216. package/dist/test/factories.d.ts +0 -122
  217. package/dist/test/factories.d.ts.map +0 -1
  218. package/dist/test/factories.js +0 -319
  219. package/dist/test/factories.js.map +0 -1
  220. package/dist/test/integration/document-drive-integration.test.d.ts +0 -2
  221. package/dist/test/integration/document-drive-integration.test.d.ts.map +0 -1
  222. package/dist/test/integration/document-drive-integration.test.js +0 -1102
  223. package/dist/test/integration/document-drive-integration.test.js.map +0 -1
  224. package/dist/test/integration/reactor-read.test.d.ts +0 -2
  225. package/dist/test/integration/reactor-read.test.d.ts.map +0 -1
  226. package/dist/test/integration/reactor-read.test.js +0 -300
  227. package/dist/test/integration/reactor-read.test.js.map +0 -1
  228. package/dist/test/queue/queue-integration.test.d.ts +0 -2
  229. package/dist/test/queue/queue-integration.test.d.ts.map +0 -1
  230. package/dist/test/queue/queue-integration.test.js +0 -322
  231. package/dist/test/queue/queue-integration.test.js.map +0 -1
  232. package/dist/test/queue/queue.test.d.ts +0 -2
  233. package/dist/test/queue/queue.test.d.ts.map +0 -1
  234. package/dist/test/queue/queue.test.js +0 -770
  235. package/dist/test/queue/queue.test.js.map +0 -1
  236. package/dist/test/registry/registry.test.d.ts +0 -2
  237. package/dist/test/registry/registry.test.d.ts.map +0 -1
  238. package/dist/test/registry/registry.test.js +0 -182
  239. package/dist/test/registry/registry.test.js.map +0 -1
  240. package/dist/test/utils.test.d.ts +0 -2
  241. package/dist/test/utils.test.d.ts.map +0 -1
  242. package/dist/test/utils.test.js +0 -66
  243. package/dist/test/utils.test.js.map +0 -1
  244. package/dist/tsconfig.tsbuildinfo +0 -1
  245. package/dist/vitest.config.d.ts +0 -3
  246. package/dist/vitest.config.d.ts.map +0 -1
  247. package/dist/vitest.config.js +0 -11
  248. package/dist/vitest.config.js.map +0 -1
  249. /package/dist/src/{utils.js → core/utils.js} +0 -0
@@ -1,1102 +0,0 @@
1
- import { MemoryStorage, ReactorBuilder, addFile, addFolder, copyNode, deleteNode, driveDocumentModelModule, moveNode, setAvailableOffline, setDriveIcon, setDriveName, setSharingType, updateFile, updateNode, } from "document-drive";
2
- import { generateId } from "document-model";
3
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4
- import { EventBus } from "../../src/events/event-bus.js";
5
- import { SimpleJobExecutorManager } from "../../src/executor/simple-job-executor-manager.js";
6
- import { SimpleJobExecutor } from "../../src/executor/simple-job-executor.js";
7
- import { InMemoryQueue } from "../../src/queue/queue.js";
8
- import { Reactor } from "../../src/reactor.js";
9
- import { DocumentModelRegistry } from "../../src/registry/implementation.js";
10
- import { JobStatus } from "../../src/shared/types.js";
11
- describe("Integration Test: Reactor <> Document Drive Document Model", () => {
12
- let reactor;
13
- let registry;
14
- let storage;
15
- let eventBus;
16
- let queue;
17
- let executor;
18
- let executorManager;
19
- let driveServer;
20
- beforeEach(async () => {
21
- // Setup real components
22
- storage = new MemoryStorage();
23
- registry = new DocumentModelRegistry();
24
- registry.registerModules(driveDocumentModelModule);
25
- // Create real drive server using ReactorBuilder
26
- const builder = new ReactorBuilder([
27
- driveDocumentModelModule,
28
- ]).withStorage(storage);
29
- driveServer = builder.build();
30
- await driveServer.initialize();
31
- eventBus = new EventBus();
32
- queue = new InMemoryQueue(eventBus);
33
- executor = new SimpleJobExecutor(registry, storage, storage);
34
- executorManager = new SimpleJobExecutorManager(() => executor, eventBus, queue);
35
- // Start the executor manager to process jobs
36
- await executorManager.start(1);
37
- // Create reactor with all components
38
- reactor = new Reactor(driveServer, storage, eventBus, queue, executor);
39
- });
40
- afterEach(async () => {
41
- await executorManager.stop();
42
- });
43
- describe("ADD Operations", () => {
44
- it("should add a folder via reactor.mutate", async () => {
45
- // Create a document-drive document
46
- const document = driveDocumentModelModule.utils.createDocument();
47
- await storage.create(document);
48
- // Create an ADD_FOLDER action
49
- const folderId = generateId();
50
- const action = addFolder({
51
- id: folderId,
52
- name: "Test Folder",
53
- parentFolder: null,
54
- });
55
- // Submit via reactor.mutate
56
- const jobInfo = await reactor.mutate(document.header.id, [action]);
57
- expect(jobInfo.status).toBe(JobStatus.PENDING);
58
- // Wait for processing
59
- await vi.waitFor(async () => {
60
- const operations = await reactor.getOperations(document.header.id);
61
- expect(operations.global.results).toHaveLength(1);
62
- });
63
- // Verify the operation was processed
64
- const operations = await reactor.getOperations(document.header.id);
65
- expect(operations.global.results[0].action.type).toBe("ADD_FOLDER");
66
- // Verify state was updated
67
- const { document: updatedDocument } = await reactor.get(document.header.id);
68
- const globalState = updatedDocument.state.global;
69
- expect(globalState.nodes).toHaveLength(1);
70
- expect(globalState.nodes[0]).toMatchObject({
71
- id: folderId,
72
- name: "Test Folder",
73
- parentFolder: null,
74
- kind: "folder",
75
- });
76
- });
77
- it("should add a file via reactor.mutate", async () => {
78
- const document = driveDocumentModelModule.utils.createDocument();
79
- await storage.create(document);
80
- // First add a folder
81
- const folderId = generateId();
82
- const folderAction = addFolder({
83
- id: folderId,
84
- name: "Documents",
85
- parentFolder: null,
86
- });
87
- await reactor.mutate(document.header.id, [folderAction]);
88
- // Wait for folder to be created
89
- await vi.waitFor(async () => {
90
- const operations = await reactor.getOperations(document.header.id);
91
- expect(operations.global.results).toHaveLength(1);
92
- });
93
- // Then add a file to the folder
94
- const fileId = generateId();
95
- const fileAction = addFile({
96
- id: fileId,
97
- name: "test.txt",
98
- documentType: "text/plain",
99
- parentFolder: folderId,
100
- });
101
- await reactor.mutate(document.header.id, [fileAction]);
102
- // Wait for processing
103
- await vi.waitFor(async () => {
104
- const operations = await reactor.getOperations(document.header.id);
105
- expect(operations.global.results).toHaveLength(2);
106
- });
107
- // Verify both operations were processed
108
- const operations = await reactor.getOperations(document.header.id);
109
- expect(operations.global.results).toHaveLength(2);
110
- // Verify state contains both nodes
111
- const { document: updatedDocument } = await reactor.get(document.header.id);
112
- const globalState = updatedDocument.state.global;
113
- expect(globalState.nodes).toHaveLength(2);
114
- const file = globalState.nodes.find((n) => n.id === fileId);
115
- expect(file).toMatchObject({
116
- id: fileId,
117
- name: "test.txt",
118
- documentType: "text/plain",
119
- parentFolder: folderId,
120
- kind: "file",
121
- });
122
- });
123
- it("should handle nested folder structure", async () => {
124
- const document = driveDocumentModelModule.utils.createDocument();
125
- await storage.create(document);
126
- // Create a hierarchy: root -> folder1 -> folder2 -> folder3
127
- const folder1Id = generateId();
128
- const folder2Id = generateId();
129
- const folder3Id = generateId();
130
- const actions = [
131
- addFolder({
132
- id: folder1Id,
133
- name: "Folder 1",
134
- parentFolder: null,
135
- }),
136
- addFolder({
137
- id: folder2Id,
138
- name: "Folder 2",
139
- parentFolder: folder1Id,
140
- }),
141
- addFolder({
142
- id: folder3Id,
143
- name: "Folder 3",
144
- parentFolder: folder2Id,
145
- }),
146
- ];
147
- // Submit all actions at once
148
- await reactor.mutate(document.header.id, actions);
149
- // Wait for processing
150
- await vi.waitFor(async () => {
151
- const operations = await reactor.getOperations(document.header.id);
152
- expect(operations.global.results).toHaveLength(3);
153
- });
154
- // Verify the hierarchy
155
- const { document: updatedDocument } = await reactor.get(document.header.id);
156
- const globalState = updatedDocument.state.global;
157
- expect(globalState.nodes).toHaveLength(3);
158
- const folder1 = globalState.nodes.find((n) => n.id === folder1Id);
159
- const folder2 = globalState.nodes.find((n) => n.id === folder2Id);
160
- const folder3 = globalState.nodes.find((n) => n.id === folder3Id);
161
- expect(folder1).toBeDefined();
162
- expect(folder2).toBeDefined();
163
- expect(folder3).toBeDefined();
164
- expect(folder1?.parentFolder).toBe(null);
165
- expect(folder2?.parentFolder).toBe(folder1Id);
166
- expect(folder3?.parentFolder).toBe(folder2Id);
167
- });
168
- });
169
- describe("UPDATE Operations", () => {
170
- it("should update a file via reactor.mutate", async () => {
171
- const document = driveDocumentModelModule.utils.createDocument();
172
- await storage.create(document);
173
- // Add a file first
174
- const fileId = generateId();
175
- const addAction = addFile({
176
- id: fileId,
177
- name: "original.txt",
178
- documentType: "text/plain",
179
- parentFolder: null,
180
- });
181
- await reactor.mutate(document.header.id, [addAction]);
182
- await vi.waitFor(async () => {
183
- const operations = await reactor.getOperations(document.header.id);
184
- expect(operations.global.results).toHaveLength(1);
185
- });
186
- // Update the file
187
- const updateAction = updateFile({
188
- id: fileId,
189
- name: "renamed.txt",
190
- documentType: "text/markdown",
191
- });
192
- await reactor.mutate(document.header.id, [updateAction]);
193
- await vi.waitFor(async () => {
194
- const operations = await reactor.getOperations(document.header.id);
195
- expect(operations.global.results).toHaveLength(2);
196
- });
197
- // Verify the update
198
- const { document: updatedDocument } = await reactor.get(document.header.id);
199
- const globalState = updatedDocument.state.global;
200
- const file = globalState.nodes.find((n) => n.id === fileId);
201
- expect(file).toMatchObject({
202
- id: fileId,
203
- name: "renamed.txt",
204
- documentType: "text/markdown",
205
- kind: "file",
206
- });
207
- });
208
- it("should update a node (folder) via reactor.mutate", async () => {
209
- const document = driveDocumentModelModule.utils.createDocument();
210
- await storage.create(document);
211
- // Add a folder first
212
- const folderId = generateId();
213
- const addAction = addFolder({
214
- id: folderId,
215
- name: "Original Folder",
216
- parentFolder: null,
217
- });
218
- await reactor.mutate(document.header.id, [addAction]);
219
- await vi.waitFor(async () => {
220
- const operations = await reactor.getOperations(document.header.id);
221
- expect(operations.global.results).toHaveLength(1);
222
- });
223
- // Update the folder
224
- const updateAction = updateNode({
225
- id: folderId,
226
- name: "Renamed Folder",
227
- });
228
- await reactor.mutate(document.header.id, [updateAction]);
229
- await vi.waitFor(async () => {
230
- const operations = await reactor.getOperations(document.header.id);
231
- expect(operations.global.results).toHaveLength(2);
232
- });
233
- // Verify the update
234
- const { document: updatedDocument } = await reactor.get(document.header.id);
235
- const globalState = updatedDocument.state.global;
236
- const folder = globalState.nodes.find((n) => n.id === folderId);
237
- expect(folder).toMatchObject({
238
- id: folderId,
239
- name: "Renamed Folder",
240
- kind: "folder",
241
- });
242
- });
243
- });
244
- describe("DELETE Operations", () => {
245
- it("should delete a node via reactor.mutate", async () => {
246
- const document = driveDocumentModelModule.utils.createDocument();
247
- await storage.create(document);
248
- // Add a folder first
249
- const folderId = generateId();
250
- const addAction = addFolder({
251
- id: folderId,
252
- name: "To Be Deleted",
253
- parentFolder: null,
254
- });
255
- await reactor.mutate(document.header.id, [addAction]);
256
- await vi.waitFor(async () => {
257
- const operations = await reactor.getOperations(document.header.id);
258
- expect(operations.global.results).toHaveLength(1);
259
- });
260
- // Delete the folder
261
- const deleteAction = deleteNode({
262
- id: folderId,
263
- });
264
- await reactor.mutate(document.header.id, [deleteAction]);
265
- await vi.waitFor(async () => {
266
- const operations = await reactor.getOperations(document.header.id);
267
- expect(operations.global.results).toHaveLength(2);
268
- });
269
- // Verify the deletion
270
- const { document: updatedDocument } = await reactor.get(document.header.id);
271
- const globalState = updatedDocument.state.global;
272
- const folder = globalState.nodes.find((n) => n.id === folderId);
273
- expect(folder).toBeUndefined();
274
- expect(globalState.nodes).toHaveLength(0);
275
- });
276
- it("should delete children when parent is deleted", async () => {
277
- const document = driveDocumentModelModule.utils.createDocument();
278
- await storage.create(document);
279
- // Create parent folder with children
280
- const parentId = generateId();
281
- const child1Id = generateId();
282
- const child2Id = generateId();
283
- const fileId = generateId();
284
- const actions = [
285
- addFolder({
286
- id: parentId,
287
- name: "Parent",
288
- parentFolder: null,
289
- }),
290
- addFolder({
291
- id: child1Id,
292
- name: "Child 1",
293
- parentFolder: parentId,
294
- }),
295
- addFolder({
296
- id: child2Id,
297
- name: "Child 2",
298
- parentFolder: parentId,
299
- }),
300
- addFile({
301
- id: fileId,
302
- name: "file.txt",
303
- documentType: "text/plain",
304
- parentFolder: child1Id,
305
- }),
306
- ];
307
- await reactor.mutate(document.header.id, actions);
308
- await vi.waitFor(async () => {
309
- const operations = await reactor.getOperations(document.header.id);
310
- expect(operations.global.results).toHaveLength(4);
311
- });
312
- // Delete parent
313
- const deleteAction = deleteNode({
314
- id: parentId,
315
- });
316
- await reactor.mutate(document.header.id, [deleteAction]);
317
- await vi.waitFor(async () => {
318
- const operations = await reactor.getOperations(document.header.id);
319
- expect(operations.global.results).toHaveLength(5);
320
- });
321
- // Verify all nodes were deleted
322
- const { document: updatedDocument } = await reactor.get(document.header.id);
323
- const globalState = updatedDocument.state.global;
324
- expect(globalState.nodes).toHaveLength(0);
325
- });
326
- });
327
- describe("MOVE Operations", () => {
328
- it("should move a node to a different parent", async () => {
329
- const document = driveDocumentModelModule.utils.createDocument();
330
- await storage.create(document);
331
- // Create folder structure
332
- const folder1Id = generateId();
333
- const folder2Id = generateId();
334
- const childId = generateId();
335
- const actions = [
336
- addFolder({
337
- id: folder1Id,
338
- name: "Folder 1",
339
- parentFolder: null,
340
- }),
341
- addFolder({
342
- id: folder2Id,
343
- name: "Folder 2",
344
- parentFolder: null,
345
- }),
346
- addFolder({
347
- id: childId,
348
- name: "Child",
349
- parentFolder: folder1Id,
350
- }),
351
- ];
352
- await reactor.mutate(document.header.id, actions);
353
- await vi.waitFor(async () => {
354
- const operations = await reactor.getOperations(document.header.id);
355
- expect(operations.global.results).toHaveLength(3);
356
- });
357
- // Move child from folder1 to folder2
358
- const moveAction = moveNode({
359
- srcFolder: childId,
360
- targetParentFolder: folder2Id,
361
- });
362
- await reactor.mutate(document.header.id, [moveAction]);
363
- await vi.waitFor(async () => {
364
- const operations = await reactor.getOperations(document.header.id);
365
- expect(operations.global.results).toHaveLength(4);
366
- });
367
- // Verify the move
368
- const { document: updatedDocument } = await reactor.get(document.header.id);
369
- const globalState = updatedDocument.state.global;
370
- const child = globalState.nodes.find((n) => n.id === childId);
371
- expect(child).toBeDefined();
372
- expect(child?.parentFolder).toBe(folder2Id);
373
- });
374
- it("should move a node to root when targetParentFolder is null", async () => {
375
- const document = driveDocumentModelModule.utils.createDocument();
376
- await storage.create(document);
377
- // Create nested folder
378
- const parentId = generateId();
379
- const childId = generateId();
380
- const actions = [
381
- addFolder({
382
- id: parentId,
383
- name: "Parent",
384
- parentFolder: null,
385
- }),
386
- addFolder({
387
- id: childId,
388
- name: "Child",
389
- parentFolder: parentId,
390
- }),
391
- ];
392
- await reactor.mutate(document.header.id, actions);
393
- await vi.waitFor(async () => {
394
- const operations = await reactor.getOperations(document.header.id);
395
- expect(operations.global.results).toHaveLength(2);
396
- });
397
- // Move child to root
398
- const moveAction = moveNode({
399
- srcFolder: childId,
400
- targetParentFolder: null,
401
- });
402
- await reactor.mutate(document.header.id, [moveAction]);
403
- await vi.waitFor(async () => {
404
- const operations = await reactor.getOperations(document.header.id);
405
- expect(operations.global.results).toHaveLength(3);
406
- });
407
- // Verify the move
408
- const { document: updatedDocument } = await reactor.get(document.header.id);
409
- const globalState = updatedDocument.state.global;
410
- const child = globalState.nodes.find((n) => n.id === childId);
411
- expect(child).toBeDefined();
412
- expect(child?.parentFolder).toBe(null);
413
- });
414
- it("should prevent moving folder to its descendant", async () => {
415
- const document = driveDocumentModelModule.utils.createDocument();
416
- await storage.create(document);
417
- // Create nested folders
418
- const folder1Id = generateId();
419
- const folder2Id = generateId();
420
- const folder3Id = generateId();
421
- const actions = [
422
- addFolder({
423
- id: folder1Id,
424
- name: "Folder 1",
425
- parentFolder: null,
426
- }),
427
- addFolder({
428
- id: folder2Id,
429
- name: "Folder 2",
430
- parentFolder: folder1Id,
431
- }),
432
- addFolder({
433
- id: folder3Id,
434
- name: "Folder 3",
435
- parentFolder: folder2Id,
436
- }),
437
- ];
438
- await reactor.mutate(document.header.id, actions);
439
- await vi.waitFor(async () => {
440
- const operations = await reactor.getOperations(document.header.id);
441
- expect(operations.global.results).toHaveLength(3);
442
- });
443
- // Try to move folder1 to folder3 (its descendant)
444
- const moveAction = moveNode({
445
- srcFolder: folder1Id,
446
- targetParentFolder: folder3Id,
447
- });
448
- await reactor.mutate(document.header.id, [moveAction]);
449
- // Wait a bit for potential processing
450
- await new Promise((resolve) => setTimeout(resolve, 100));
451
- // The operation should have been rejected - verify folder1 is still at root
452
- const { document: updatedDocument } = await reactor.get(document.header.id);
453
- const globalState = updatedDocument.state.global;
454
- const folder1 = globalState.nodes.find((n) => n.id === folder1Id);
455
- expect(folder1).toBeDefined();
456
- expect(folder1?.parentFolder).toBe(null);
457
- });
458
- });
459
- describe("COPY Operations", () => {
460
- it("should copy a node to a different parent", async () => {
461
- const document = driveDocumentModelModule.utils.createDocument();
462
- await storage.create(document);
463
- // Create folder structure
464
- const folder1Id = generateId();
465
- const folder2Id = generateId();
466
- const sourceId = generateId();
467
- const actions = [
468
- addFolder({
469
- id: folder1Id,
470
- name: "Folder 1",
471
- parentFolder: null,
472
- }),
473
- addFolder({
474
- id: folder2Id,
475
- name: "Folder 2",
476
- parentFolder: null,
477
- }),
478
- addFolder({
479
- id: sourceId,
480
- name: "Source",
481
- parentFolder: folder1Id,
482
- }),
483
- ];
484
- await reactor.mutate(document.header.id, actions);
485
- await vi.waitFor(async () => {
486
- const operations = await reactor.getOperations(document.header.id);
487
- expect(operations.global.results).toHaveLength(3);
488
- });
489
- // Copy source to folder2
490
- const targetId = generateId();
491
- const copyAction = copyNode({
492
- srcId: sourceId,
493
- targetId: targetId,
494
- targetParentFolder: folder2Id,
495
- });
496
- await reactor.mutate(document.header.id, [copyAction]);
497
- await vi.waitFor(async () => {
498
- const operations = await reactor.getOperations(document.header.id);
499
- expect(operations.global.results).toHaveLength(4);
500
- });
501
- // Verify the copy
502
- const { document: updatedDocument } = await reactor.get(document.header.id);
503
- const globalState = updatedDocument.state.global;
504
- // Original should still exist
505
- const original = globalState.nodes.find((n) => n.id === sourceId);
506
- expect(original).toBeDefined();
507
- expect(original?.parentFolder).toBe(folder1Id);
508
- // Copy should exist in new location
509
- const copy = globalState.nodes.find((n) => n.id === targetId);
510
- expect(copy).toBeDefined();
511
- expect(copy?.parentFolder).toBe(folder2Id);
512
- expect(copy?.name).toBe("Source");
513
- });
514
- it("should copy a node with a new name", async () => {
515
- const document = driveDocumentModelModule.utils.createDocument();
516
- await storage.create(document);
517
- // Create source folder
518
- const sourceId = generateId();
519
- const addAction = addFolder({
520
- id: sourceId,
521
- name: "Original Name",
522
- parentFolder: null,
523
- });
524
- await reactor.mutate(document.header.id, [addAction]);
525
- await vi.waitFor(async () => {
526
- const operations = await reactor.getOperations(document.header.id);
527
- expect(operations.global.results).toHaveLength(1);
528
- });
529
- // Copy with new name
530
- const targetId = generateId();
531
- const copyAction = copyNode({
532
- srcId: sourceId,
533
- targetId: targetId,
534
- targetName: "New Name",
535
- targetParentFolder: null,
536
- });
537
- await reactor.mutate(document.header.id, [copyAction]);
538
- await vi.waitFor(async () => {
539
- const operations = await reactor.getOperations(document.header.id);
540
- expect(operations.global.results).toHaveLength(2);
541
- });
542
- // Verify the copy
543
- const { document: updatedDocument } = await reactor.get(document.header.id);
544
- const globalState = updatedDocument.state.global;
545
- const copy = globalState.nodes.find((n) => n.id === targetId);
546
- expect(copy).toBeDefined();
547
- expect(copy?.name).toBe("New Name");
548
- });
549
- it("should copy a single node", async () => {
550
- const document = driveDocumentModelModule.utils.createDocument();
551
- await storage.create(document);
552
- // Create source structure
553
- const parentId = generateId();
554
- const child1Id = generateId();
555
- const child2Id = generateId();
556
- const fileId = generateId();
557
- const actions = [
558
- addFolder({
559
- id: parentId,
560
- name: "Parent",
561
- parentFolder: null,
562
- }),
563
- addFolder({
564
- id: child1Id,
565
- name: "Child 1",
566
- parentFolder: parentId,
567
- }),
568
- addFolder({
569
- id: child2Id,
570
- name: "Child 2",
571
- parentFolder: parentId,
572
- }),
573
- addFile({
574
- id: fileId,
575
- name: "file.txt",
576
- documentType: "text/plain",
577
- parentFolder: child1Id,
578
- }),
579
- ];
580
- await reactor.mutate(document.header.id, actions);
581
- await vi.waitFor(async () => {
582
- const operations = await reactor.getOperations(document.header.id);
583
- expect(operations.global.results).toHaveLength(4);
584
- });
585
- // Copy the entire structure
586
- const targetId = generateId();
587
- const copyAction = copyNode({
588
- srcId: parentId,
589
- targetId: targetId,
590
- targetParentFolder: null,
591
- });
592
- await reactor.mutate(document.header.id, [copyAction]);
593
- await vi.waitFor(async () => {
594
- const { document: doc } = await reactor.get(document.header.id);
595
- const globalState = doc.state.global;
596
- // Should have original 4 nodes + 1 copied node
597
- expect(globalState.nodes.length).toBe(5);
598
- });
599
- // Verify the node was copied
600
- const { document: updatedDocument } = await reactor.get(document.header.id);
601
- const globalState = updatedDocument.state.global;
602
- // Find the copied parent
603
- const copiedParent = globalState.nodes.find((n) => n.id === targetId);
604
- expect(copiedParent).toBeDefined();
605
- // The name might have a suffix if there's a collision
606
- expect(copiedParent?.name).toMatch(/^Parent( \(copy\)( \d+)?)?$/);
607
- // Verify only the single node was copied (not children)
608
- expect(globalState.nodes.length).toBe(5); // 4 original + 1 copied
609
- // Verify original nodes still exist
610
- const originalParent = globalState.nodes.find((n) => n.id === parentId);
611
- const originalChild1 = globalState.nodes.find((n) => n.id === child1Id);
612
- const originalChild2 = globalState.nodes.find((n) => n.id === child2Id);
613
- const originalFile = globalState.nodes.find((n) => n.id === fileId);
614
- expect(originalParent).toBeDefined();
615
- expect(originalChild1).toBeDefined();
616
- expect(originalChild2).toBeDefined();
617
- expect(originalFile).toBeDefined();
618
- // The copied node should not have children automatically copied
619
- const copiedNodeChildren = globalState.nodes.filter((n) => n.parentFolder === targetId);
620
- expect(copiedNodeChildren.length).toBe(0);
621
- });
622
- });
623
- describe("Drive-level Operations", () => {
624
- it("should set drive name via reactor.mutate", async () => {
625
- const document = driveDocumentModelModule.utils.createDocument();
626
- await storage.create(document);
627
- // Set drive name
628
- const action = setDriveName({
629
- name: "My Drive",
630
- });
631
- await reactor.mutate(document.header.id, [action]);
632
- await vi.waitFor(async () => {
633
- const operations = await reactor.getOperations(document.header.id);
634
- expect(operations.global.results).toHaveLength(1);
635
- });
636
- // Verify the drive name was set
637
- const { document: updatedDocument } = await reactor.get(document.header.id);
638
- const globalState = updatedDocument.state.global;
639
- expect(globalState.name).toBe("My Drive");
640
- });
641
- it("should set drive icon via reactor.mutate", async () => {
642
- const document = driveDocumentModelModule.utils.createDocument();
643
- await storage.create(document);
644
- // Set drive icon
645
- const action = setDriveIcon({
646
- icon: "folder-open",
647
- });
648
- await reactor.mutate(document.header.id, [action]);
649
- await vi.waitFor(async () => {
650
- const operations = await reactor.getOperations(document.header.id);
651
- expect(operations.global.results).toHaveLength(1);
652
- });
653
- // Verify the drive icon was set
654
- const { document: updatedDocument } = await reactor.get(document.header.id);
655
- const globalState = updatedDocument.state.global;
656
- expect(globalState.icon).toBe("folder-open");
657
- });
658
- it("should set sharing type via reactor.mutate", async () => {
659
- const document = driveDocumentModelModule.utils.createDocument();
660
- await storage.create(document);
661
- // Set sharing type (note: this is a local operation)
662
- const action = setSharingType({
663
- type: "PUBLIC",
664
- });
665
- await reactor.mutate(document.header.id, [action]);
666
- await vi.waitFor(async () => {
667
- const operations = await reactor.getOperations(document.header.id);
668
- expect(operations.local.results).toHaveLength(1);
669
- });
670
- // Verify the sharing type was set
671
- const { document: updatedDocument } = await reactor.get(document.header.id);
672
- const localState = updatedDocument.state.local;
673
- expect(localState.sharingType).toBe("PUBLIC");
674
- });
675
- it("should set available offline via reactor.mutate", async () => {
676
- const document = driveDocumentModelModule.utils.createDocument();
677
- await storage.create(document);
678
- // Set available offline (note: this is a local operation)
679
- const action = setAvailableOffline({
680
- availableOffline: true,
681
- });
682
- await reactor.mutate(document.header.id, [action]);
683
- await vi.waitFor(async () => {
684
- const operations = await reactor.getOperations(document.header.id);
685
- expect(operations.local.results).toHaveLength(1);
686
- });
687
- // Verify available offline was set
688
- const { document: updatedDocument } = await reactor.get(document.header.id);
689
- const localState = updatedDocument.state.local;
690
- expect(localState.availableOffline).toBe(true);
691
- });
692
- });
693
- describe("Batch Operations", () => {
694
- it("should process multiple operations in a single mutate call", async () => {
695
- const document = driveDocumentModelModule.utils.createDocument();
696
- await storage.create(document);
697
- // Create multiple operations
698
- const folder1Id = generateId();
699
- const folder2Id = generateId();
700
- const file1Id = generateId();
701
- const file2Id = generateId();
702
- const actions = [
703
- setDriveName({
704
- name: "Multi-Op Drive",
705
- }),
706
- addFolder({
707
- id: folder1Id,
708
- name: "Folder 1",
709
- parentFolder: null,
710
- }),
711
- addFolder({
712
- id: folder2Id,
713
- name: "Folder 2",
714
- parentFolder: null,
715
- }),
716
- addFile({
717
- id: file1Id,
718
- name: "file1.txt",
719
- documentType: "text/plain",
720
- parentFolder: folder1Id,
721
- }),
722
- addFile({
723
- id: file2Id,
724
- name: "file2.txt",
725
- documentType: "text/plain",
726
- parentFolder: folder2Id,
727
- }),
728
- ];
729
- // Submit all at once
730
- await reactor.mutate(document.header.id, actions);
731
- await vi.waitFor(async () => {
732
- const operations = await reactor.getOperations(document.header.id);
733
- expect(operations.global.results).toHaveLength(5);
734
- });
735
- // Verify all operations were applied
736
- const { document: updatedDocument } = await reactor.get(document.header.id);
737
- const globalState = updatedDocument.state.global;
738
- expect(globalState.name).toBe("Multi-Op Drive");
739
- expect(globalState.nodes).toHaveLength(4);
740
- // Verify structure
741
- const folder1 = globalState.nodes.find((n) => n.id === folder1Id);
742
- const folder2 = globalState.nodes.find((n) => n.id === folder2Id);
743
- const file1 = globalState.nodes.find((n) => n.id === file1Id);
744
- const file2 = globalState.nodes.find((n) => n.id === file2Id);
745
- expect(folder1).toBeDefined();
746
- expect(folder2).toBeDefined();
747
- expect(file1).toBeDefined();
748
- expect(file1?.parentFolder).toBe(folder1Id);
749
- expect(file2).toBeDefined();
750
- expect(file2?.parentFolder).toBe(folder2Id);
751
- });
752
- it("should maintain operation order in batch processing", async () => {
753
- const document = driveDocumentModelModule.utils.createDocument();
754
- await storage.create(document);
755
- // Operations that depend on each other
756
- const folderId = generateId();
757
- const fileId = generateId();
758
- const actions = [
759
- // Create folder
760
- addFolder({
761
- id: folderId,
762
- name: "Original Name",
763
- parentFolder: null,
764
- }),
765
- // Add file to folder
766
- addFile({
767
- id: fileId,
768
- name: "test.txt",
769
- documentType: "text/plain",
770
- parentFolder: folderId,
771
- }),
772
- // Rename folder
773
- updateNode({
774
- id: folderId,
775
- name: "Updated Name",
776
- }),
777
- // Update file
778
- updateFile({
779
- id: fileId,
780
- documentType: "text/markdown",
781
- }),
782
- ];
783
- await reactor.mutate(document.header.id, actions);
784
- await vi.waitFor(async () => {
785
- const operations = await reactor.getOperations(document.header.id);
786
- expect(operations.global.results).toHaveLength(4);
787
- });
788
- // Verify final state
789
- const { document: updatedDocument } = await reactor.get(document.header.id);
790
- const globalState = updatedDocument.state.global;
791
- const folder = globalState.nodes.find((n) => n.id === folderId);
792
- const file = globalState.nodes.find((n) => n.id === fileId);
793
- expect(folder?.name).toBe("Updated Name");
794
- expect(file.documentType).toBe("text/markdown");
795
- });
796
- });
797
- describe("Error Handling", () => {
798
- it("should handle invalid node references gracefully", async () => {
799
- const document = driveDocumentModelModule.utils.createDocument();
800
- await storage.create(document);
801
- // Try to add a file to a non-existent folder
802
- const fileId = generateId();
803
- const action = addFile({
804
- id: fileId,
805
- name: "orphan.txt",
806
- documentType: "text/plain",
807
- parentFolder: "non-existent-folder",
808
- });
809
- await reactor.mutate(document.header.id, [action]);
810
- // Wait a bit for potential processing
811
- await new Promise((resolve) => setTimeout(resolve, 100));
812
- // The operation should have been attempted but may have failed
813
- // Check that the document state is still valid
814
- const { document: updatedDocument } = await reactor.get(document.header.id);
815
- const globalState = updatedDocument.state.global;
816
- // File might exist even with invalid parent (depends on reducer logic)
817
- // The important thing is that the system didn't crash
818
- const file = globalState.nodes.find((n) => n.id === fileId);
819
- // Either the file doesn't exist or exists with the invalid parent
820
- if (file) {
821
- expect(file.parentFolder).toBe("non-existent-folder");
822
- }
823
- });
824
- it("should handle duplicate node IDs", async () => {
825
- const document = driveDocumentModelModule.utils.createDocument();
826
- await storage.create(document);
827
- const duplicateId = generateId();
828
- // Try to create two folders with the same ID
829
- const actions = [
830
- addFolder({
831
- id: duplicateId,
832
- name: "First",
833
- parentFolder: null,
834
- }),
835
- addFolder({
836
- id: duplicateId,
837
- name: "Second",
838
- parentFolder: null,
839
- }),
840
- ];
841
- await reactor.mutate(document.header.id, actions);
842
- await vi.waitFor(async () => {
843
- const operations = await reactor.getOperations(document.header.id);
844
- // At least one operation should have been processed
845
- expect(operations.global.results.length).toBeGreaterThanOrEqual(1);
846
- });
847
- // Only one node with the ID should exist
848
- const { document: updatedDocument } = await reactor.get(document.header.id);
849
- const globalState = updatedDocument.state.global;
850
- const nodesWithId = globalState.nodes.filter((n) => n.id === duplicateId);
851
- expect(nodesWithId).toHaveLength(1);
852
- });
853
- it("should handle name collisions", async () => {
854
- const document = driveDocumentModelModule.utils.createDocument();
855
- await storage.create(document);
856
- const folder1Id = generateId();
857
- const folder2Id = generateId();
858
- // Try to create two folders with the same name in the same location
859
- const actions = [
860
- addFolder({
861
- id: folder1Id,
862
- name: "Duplicate Name",
863
- parentFolder: null,
864
- }),
865
- addFolder({
866
- id: folder2Id,
867
- name: "Duplicate Name",
868
- parentFolder: null,
869
- }),
870
- ];
871
- await reactor.mutate(document.header.id, actions);
872
- await vi.waitFor(async () => {
873
- const operations = await reactor.getOperations(document.header.id);
874
- // At least one operation should have been processed
875
- expect(operations.global.results.length).toBeGreaterThanOrEqual(1);
876
- });
877
- // Check how the system handled the collision
878
- const { document: updatedDocument } = await reactor.get(document.header.id);
879
- const globalState = updatedDocument.state.global;
880
- const foldersWithName = globalState.nodes.filter((n) => n.name === "Duplicate Name" || n.name.startsWith("Duplicate Name"));
881
- // System should have handled the collision somehow
882
- expect(foldersWithName.length).toBeGreaterThanOrEqual(1);
883
- });
884
- it("should continue processing after encountering an error", async () => {
885
- const document = driveDocumentModelModule.utils.createDocument();
886
- await storage.create(document);
887
- const folder1Id = generateId();
888
- const folder2Id = generateId();
889
- const actions = [
890
- // Valid operation
891
- addFolder({
892
- id: folder1Id,
893
- name: "Valid Folder 1",
894
- parentFolder: null,
895
- }),
896
- // Invalid operation - moving non-existent node
897
- moveNode({
898
- srcFolder: "non-existent",
899
- targetParentFolder: folder1Id,
900
- }),
901
- // Valid operation
902
- addFolder({
903
- id: folder2Id,
904
- name: "Valid Folder 2",
905
- parentFolder: null,
906
- }),
907
- ];
908
- await reactor.mutate(document.header.id, actions);
909
- await vi.waitFor(async () => {
910
- const operations = await reactor.getOperations(document.header.id);
911
- // At least some operations should have been processed
912
- expect(operations.global.results.length).toBeGreaterThanOrEqual(1);
913
- });
914
- // Check that valid operations were processed
915
- const { document: updatedDocument } = await reactor.get(document.header.id);
916
- const globalState = updatedDocument.state.global;
917
- const folder1 = globalState.nodes.find((n) => n.id === folder1Id);
918
- const folder2 = globalState.nodes.find((n) => n.id === folder2Id);
919
- // At least one of the valid operations should have succeeded
920
- expect(folder1 || folder2).toBeDefined();
921
- });
922
- });
923
- describe("Complex Scenarios", () => {
924
- it("should handle complex file reorganization", async () => {
925
- const document = driveDocumentModelModule.utils.createDocument();
926
- await storage.create(document);
927
- // Create initial structure
928
- const docsId = generateId();
929
- const imagesId = generateId();
930
- const tempId = generateId();
931
- const doc1Id = generateId();
932
- const doc2Id = generateId();
933
- const img1Id = generateId();
934
- const setupActions = [
935
- addFolder({
936
- id: docsId,
937
- name: "Documents",
938
- parentFolder: null,
939
- }),
940
- addFolder({
941
- id: imagesId,
942
- name: "Images",
943
- parentFolder: null,
944
- }),
945
- addFolder({
946
- id: tempId,
947
- name: "Temp",
948
- parentFolder: null,
949
- }),
950
- addFile({
951
- id: doc1Id,
952
- name: "report.docx",
953
- documentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
954
- parentFolder: tempId,
955
- }),
956
- addFile({
957
- id: doc2Id,
958
- name: "presentation.pptx",
959
- documentType: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
960
- parentFolder: tempId,
961
- }),
962
- addFile({
963
- id: img1Id,
964
- name: "logo.png",
965
- documentType: "image/png",
966
- parentFolder: tempId,
967
- }),
968
- ];
969
- await reactor.mutate(document.header.id, setupActions);
970
- await vi.waitFor(async () => {
971
- const operations = await reactor.getOperations(document.header.id);
972
- expect(operations.global.results).toHaveLength(6);
973
- });
974
- // Reorganize files
975
- const reorganizeActions = [
976
- // Move documents to Documents folder
977
- moveNode({
978
- srcFolder: doc1Id,
979
- targetParentFolder: docsId,
980
- }),
981
- moveNode({
982
- srcFolder: doc2Id,
983
- targetParentFolder: docsId,
984
- }),
985
- // Move image to Images folder
986
- moveNode({
987
- srcFolder: img1Id,
988
- targetParentFolder: imagesId,
989
- }),
990
- // Delete temp folder
991
- deleteNode({
992
- id: tempId,
993
- }),
994
- ];
995
- await reactor.mutate(document.header.id, reorganizeActions);
996
- await vi.waitFor(async () => {
997
- const operations = await reactor.getOperations(document.header.id);
998
- expect(operations.global.results).toHaveLength(10);
999
- });
1000
- // Verify final structure
1001
- const { document: updatedDocument } = await reactor.get(document.header.id);
1002
- const globalState = updatedDocument.state.global;
1003
- // Temp folder should be gone
1004
- const temp = globalState.nodes.find((n) => n.id === tempId);
1005
- expect(temp).toBeUndefined();
1006
- // Files should be in correct folders
1007
- const doc1 = globalState.nodes.find((n) => n.id === doc1Id);
1008
- const doc2 = globalState.nodes.find((n) => n.id === doc2Id);
1009
- const img1 = globalState.nodes.find((n) => n.id === img1Id);
1010
- expect(doc1?.parentFolder).toBe(docsId);
1011
- expect(doc2?.parentFolder).toBe(docsId);
1012
- expect(img1?.parentFolder).toBe(imagesId);
1013
- });
1014
- it("should handle project template creation", async () => {
1015
- const document = driveDocumentModelModule.utils.createDocument();
1016
- await storage.create(document);
1017
- // Create a project template structure
1018
- const projectId = generateId();
1019
- const srcId = generateId();
1020
- const testsId = generateId();
1021
- const docsId = generateId();
1022
- const configId = generateId();
1023
- const mainFileId = generateId();
1024
- const testFileId = generateId();
1025
- const readmeId = generateId();
1026
- const configFileId = generateId();
1027
- const templateActions = [
1028
- setDriveName({
1029
- name: "My Project",
1030
- }),
1031
- addFolder({
1032
- id: projectId,
1033
- name: "my-project",
1034
- parentFolder: null,
1035
- }),
1036
- addFolder({
1037
- id: srcId,
1038
- name: "src",
1039
- parentFolder: projectId,
1040
- }),
1041
- addFolder({
1042
- id: testsId,
1043
- name: "tests",
1044
- parentFolder: projectId,
1045
- }),
1046
- addFolder({
1047
- id: docsId,
1048
- name: "docs",
1049
- parentFolder: projectId,
1050
- }),
1051
- addFolder({
1052
- id: configId,
1053
- name: "config",
1054
- parentFolder: projectId,
1055
- }),
1056
- addFile({
1057
- id: mainFileId,
1058
- name: "index.ts",
1059
- documentType: "text/typescript",
1060
- parentFolder: srcId,
1061
- }),
1062
- addFile({
1063
- id: testFileId,
1064
- name: "index.test.ts",
1065
- documentType: "text/typescript",
1066
- parentFolder: testsId,
1067
- }),
1068
- addFile({
1069
- id: readmeId,
1070
- name: "README.md",
1071
- documentType: "text/markdown",
1072
- parentFolder: projectId,
1073
- }),
1074
- addFile({
1075
- id: configFileId,
1076
- name: "tsconfig.json",
1077
- documentType: "application/json",
1078
- parentFolder: configId,
1079
- }),
1080
- ];
1081
- await reactor.mutate(document.header.id, templateActions);
1082
- await vi.waitFor(async () => {
1083
- const operations = await reactor.getOperations(document.header.id);
1084
- expect(operations.global.results).toHaveLength(10);
1085
- });
1086
- // Verify the complete structure
1087
- const { document: updatedDocument } = await reactor.get(document.header.id);
1088
- const globalState = updatedDocument.state.global;
1089
- expect(globalState.name).toBe("My Project");
1090
- expect(globalState.nodes).toHaveLength(9); // 5 folders + 4 files
1091
- // Verify hierarchy
1092
- const project = globalState.nodes.find((n) => n.id === projectId);
1093
- expect(project?.parentFolder).toBe(null);
1094
- const src = globalState.nodes.find((n) => n.id === srcId);
1095
- expect(src?.parentFolder).toBe(projectId);
1096
- const mainFile = globalState.nodes.find((n) => n.id === mainFileId);
1097
- expect(mainFile?.parentFolder).toBe(srcId);
1098
- expect(mainFile.documentType).toBe("text/typescript");
1099
- });
1100
- });
1101
- });
1102
- //# sourceMappingURL=document-drive-integration.test.js.map