@aerogel/plugin-local-first 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aerogel-plugin-local-first.d.ts +48 -17
- package/dist/aerogel-plugin-local-first.js +1219 -1232
- package/dist/aerogel-plugin-local-first.js.map +1 -1
- package/package.json +5 -5
- package/src/components/Account.vue +9 -0
- package/src/components/AccountLoggedIn.vue +85 -0
- package/src/components/AccountLoggedOut.vue +26 -0
- package/src/components/AccountLoginModal.vue +36 -0
- package/src/components/AccountModal.vue +206 -0
- package/src/components/index.ts +5 -3
- package/src/components/settings/PurgeData.vue +43 -0
- package/src/components/settings/index.ts +10 -0
- package/src/index.css +1 -0
- package/src/index.ts +5 -1
- package/src/jobs/Migrate.test.ts +1 -1
- package/src/jobs/Migrate.ts +8 -1
- package/src/jobs/Sync.test.ts +313 -35
- package/src/jobs/Sync.ts +183 -77
- package/src/jobs/mixins/LoadsChildren.ts +48 -31
- package/src/jobs/mixins/LoadsTypeIndex.ts +7 -5
- package/src/jobs/mixins/TracksLocalModels.ts +4 -4
- package/src/lib/mirroring.ts +45 -21
- package/src/services/Cloud.ts +84 -25
- package/src/services/DocumentsCache.ts +6 -2
- package/src/testing/cloud.ts +1 -1
- package/src/testing/setup.ts +3 -2
- package/src/testing/stubs/Person.schema.ts +9 -0
- package/src/testing/stubs/Person.ts +18 -0
- package/src/testing/stubs/Post.schema.ts +17 -0
- package/src/testing/stubs/Post.ts +18 -0
- package/dist/InruptAuthenticator.lazy-BvpQLGoj-BvpQLGoj.js +0 -9
- package/dist/InruptAuthenticator.lazy-BvpQLGoj-BvpQLGoj.js.map +0 -1
- package/dist/LegacyAuthenticator.lazy-ChFFqP14-ChFFqP14.js +0 -5
- package/dist/LegacyAuthenticator.lazy-ChFFqP14-ChFFqP14.js.map +0 -1
- package/src/components/AGCloudStatus.vue +0 -31
package/src/jobs/Sync.test.ts
CHANGED
|
@@ -18,8 +18,11 @@ import {
|
|
|
18
18
|
} from '@aerogel/plugin-local-first/testing/cloud';
|
|
19
19
|
import Movie from '@aerogel/plugin-local-first/testing/stubs/Movie';
|
|
20
20
|
import MoviesContainer from '@aerogel/plugin-local-first/testing/stubs/MoviesContainer';
|
|
21
|
+
import Post from '@aerogel/plugin-local-first/testing/stubs/Post';
|
|
21
22
|
import SolidMock from '@aerogel/plugin-local-first/testing/mocks/Solid.mock';
|
|
22
23
|
import Workspace from '@aerogel/plugin-local-first/testing/stubs/Workspace';
|
|
24
|
+
import Person from '@aerogel/plugin-local-first/testing/stubs/Person';
|
|
25
|
+
import DocumentsCache from '@aerogel/plugin-local-first/services/DocumentsCache';
|
|
23
26
|
|
|
24
27
|
describe('Sync', () => {
|
|
25
28
|
|
|
@@ -28,10 +31,8 @@ describe('Sync', () => {
|
|
|
28
31
|
it('Syncs containers', async () => {
|
|
29
32
|
// Arrange - Mint urls
|
|
30
33
|
const parentContainerUrl = SolidMock.requireUser().storageUrls[0];
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
// fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
34
|
-
const localContainerUrl = parentContainerUrl + 'local/';
|
|
34
|
+
const remoteContainerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
35
|
+
const localContainerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
35
36
|
|
|
36
37
|
// Arrange - Prepare models
|
|
37
38
|
await MoviesContainer.at(parentContainerUrl).create({
|
|
@@ -92,11 +93,13 @@ describe('Sync', () => {
|
|
|
92
93
|
expect(containers).toHaveLength(2);
|
|
93
94
|
expect(arrayFind(containers, 'name', 'Local Movies')).toBeInstanceOf(MoviesContainer);
|
|
94
95
|
expect(arrayFind(containers, 'name', 'Remote Movies')).toBeInstanceOf(MoviesContainer);
|
|
96
|
+
|
|
97
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
95
98
|
});
|
|
96
99
|
|
|
97
100
|
it('Syncs container documents', async () => {
|
|
98
101
|
// Arrange - Mint urls
|
|
99
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
102
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
100
103
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
101
104
|
const localDocumentUrl = fakeDocumentUrl({ containerUrl });
|
|
102
105
|
const remoteDocumentUrl = fakeDocumentUrl({ containerUrl });
|
|
@@ -148,6 +151,113 @@ describe('Sync', () => {
|
|
|
148
151
|
expect(movies).toHaveLength(2);
|
|
149
152
|
expect(arrayFind(movies, 'name', 'The Tale of Princess Kaguya')).toBeInstanceOf(Movie);
|
|
150
153
|
expect(arrayFind(movies, 'name', 'Spirited Away')).toBeInstanceOf(Movie);
|
|
154
|
+
|
|
155
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('Pushes container documents', async () => {
|
|
159
|
+
// Arrange - Mint urls
|
|
160
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
161
|
+
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
162
|
+
const localDocumentUrl = fakeDocumentUrl({ containerUrl });
|
|
163
|
+
|
|
164
|
+
// Arrange - Prepare models
|
|
165
|
+
const container = await MoviesContainer.at(parentContainerUrl).create({
|
|
166
|
+
url: containerUrl,
|
|
167
|
+
name: 'Remote Movies',
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
await container.relatedMovies.create({ url: `${localDocumentUrl}#it`, name: 'The Tale of Princess Kaguya' });
|
|
171
|
+
|
|
172
|
+
// Arrange - Prepare responses
|
|
173
|
+
const typeIndexUrl = SolidMock.requireUser().privateTypeIndexUrl ?? '';
|
|
174
|
+
|
|
175
|
+
FakeServer.respond(typeIndexUrl, typeIndexResponse({ [containerUrl]: '<https://schema.org/Movie>' }));
|
|
176
|
+
FakeServer.respond(
|
|
177
|
+
containerUrl,
|
|
178
|
+
containerResponse({
|
|
179
|
+
name: 'Remote Movies',
|
|
180
|
+
documentUrls: [],
|
|
181
|
+
createdAt: container.createdAt,
|
|
182
|
+
updatedAt: container.updatedAt,
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
FakeServer.respondOnce(localDocumentUrl, FakeResponse.notFound()); // Check before create
|
|
186
|
+
FakeServer.respondOnce(localDocumentUrl, FakeResponse.created()); // Create
|
|
187
|
+
|
|
188
|
+
// Arrange - Prepare service
|
|
189
|
+
await Cloud.launch();
|
|
190
|
+
await Cloud.register(MoviesContainer);
|
|
191
|
+
await Events.emit('application-ready');
|
|
192
|
+
|
|
193
|
+
// Act
|
|
194
|
+
const updates: number[] = [];
|
|
195
|
+
|
|
196
|
+
await Cloud.sync({ onUpdated: (progress) => updates.push(progress) });
|
|
197
|
+
|
|
198
|
+
// Assert
|
|
199
|
+
expect(updates).toEqual([0, 0.9, 1]);
|
|
200
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(1);
|
|
201
|
+
expect(FakeServer.getRequests(containerUrl)).toHaveLength(1);
|
|
202
|
+
expect(FakeServer.getRequests(localDocumentUrl)).toHaveLength(2);
|
|
203
|
+
expect(FakeServer.getRequests()).toHaveLength(4);
|
|
204
|
+
|
|
205
|
+
const movies = await Movie.from(containerUrl).all();
|
|
206
|
+
expect(movies).toHaveLength(1);
|
|
207
|
+
expect(arrayFind(movies, 'name', 'The Tale of Princess Kaguya')).toBeInstanceOf(Movie);
|
|
208
|
+
|
|
209
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('Pulls container documents', async () => {
|
|
213
|
+
// Arrange - Mint urls
|
|
214
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
215
|
+
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
216
|
+
const remoteDocumentUrl = fakeDocumentUrl({ containerUrl });
|
|
217
|
+
|
|
218
|
+
// Arrange - Prepare models
|
|
219
|
+
const container = await MoviesContainer.at(parentContainerUrl).create({
|
|
220
|
+
url: containerUrl,
|
|
221
|
+
name: 'Remote Movies',
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Arrange - Prepare responses
|
|
225
|
+
const typeIndexUrl = SolidMock.requireUser().privateTypeIndexUrl ?? '';
|
|
226
|
+
|
|
227
|
+
FakeServer.respond(typeIndexUrl, typeIndexResponse({ [containerUrl]: '<https://schema.org/Movie>' }));
|
|
228
|
+
FakeServer.respond(
|
|
229
|
+
containerUrl,
|
|
230
|
+
containerResponse({
|
|
231
|
+
name: 'Remote Movies',
|
|
232
|
+
documentUrls: [remoteDocumentUrl],
|
|
233
|
+
createdAt: container.createdAt,
|
|
234
|
+
updatedAt: container.updatedAt,
|
|
235
|
+
}),
|
|
236
|
+
);
|
|
237
|
+
FakeServer.respond(remoteDocumentUrl, movieResponse('Spirited Away'));
|
|
238
|
+
|
|
239
|
+
// Arrange - Prepare service
|
|
240
|
+
await Cloud.launch();
|
|
241
|
+
await Cloud.register(MoviesContainer);
|
|
242
|
+
await Events.emit('application-ready');
|
|
243
|
+
|
|
244
|
+
// Act
|
|
245
|
+
const updates: number[] = [];
|
|
246
|
+
|
|
247
|
+
await Cloud.sync({ onUpdated: (progress) => updates.push(progress) });
|
|
248
|
+
|
|
249
|
+
// Assert
|
|
250
|
+
expect(updates).toEqual([0, 0.9, 1]);
|
|
251
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(1);
|
|
252
|
+
expect(FakeServer.getRequests(containerUrl)).toHaveLength(1);
|
|
253
|
+
expect(FakeServer.getRequests(remoteDocumentUrl)).toHaveLength(1);
|
|
254
|
+
expect(FakeServer.getRequests()).toHaveLength(3);
|
|
255
|
+
|
|
256
|
+
const movies = await Movie.from(containerUrl).all();
|
|
257
|
+
expect(movies).toHaveLength(1);
|
|
258
|
+
expect(arrayFind(movies, 'name', 'Spirited Away')).toBeInstanceOf(Movie);
|
|
259
|
+
|
|
260
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
151
261
|
});
|
|
152
262
|
|
|
153
263
|
it('Syncs documents', async () => {
|
|
@@ -195,11 +305,13 @@ describe('Sync', () => {
|
|
|
195
305
|
expect(movies).toHaveLength(2);
|
|
196
306
|
expect(arrayFind(movies, 'name', 'The Tale of Princess Kaguya')).toBeInstanceOf(Movie);
|
|
197
307
|
expect(arrayFind(movies, 'name', 'Spirited Away')).toBeInstanceOf(Movie);
|
|
308
|
+
|
|
309
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
198
310
|
});
|
|
199
311
|
|
|
200
312
|
it('Syncs individual container updates', async () => {
|
|
201
313
|
// Arrange - Mint urls
|
|
202
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
314
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
203
315
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
204
316
|
const movieDocumentUrl = fakeDocumentUrl({ containerUrl });
|
|
205
317
|
|
|
@@ -311,6 +423,8 @@ describe('Sync', () => {
|
|
|
311
423
|
crdt:value "Great Movies" .
|
|
312
424
|
} .
|
|
313
425
|
`);
|
|
426
|
+
|
|
427
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
314
428
|
});
|
|
315
429
|
|
|
316
430
|
it('Syncs individual container documents', async () => {
|
|
@@ -347,11 +461,13 @@ describe('Sync', () => {
|
|
|
347
461
|
|
|
348
462
|
expect(FakeServer.getRequests(documentUrl)).toHaveLength(4);
|
|
349
463
|
expect(FakeServer.getRequests()).toHaveLength(4);
|
|
464
|
+
|
|
465
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
350
466
|
});
|
|
351
467
|
|
|
352
468
|
it('Creates containers with documents', async () => {
|
|
353
469
|
// Arrange - Mint urls
|
|
354
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
470
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
355
471
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
356
472
|
const documentUrl = fakeDocumentUrl({ containerUrl });
|
|
357
473
|
|
|
@@ -401,11 +517,13 @@ describe('Sync', () => {
|
|
|
401
517
|
expect(FakeServer.getRequests(`${containerUrl}.meta`)).toHaveLength(1);
|
|
402
518
|
expect(FakeServer.getRequests(documentUrl)).toHaveLength(2);
|
|
403
519
|
expect(FakeServer.getRequests()).toHaveLength(10);
|
|
520
|
+
|
|
521
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
404
522
|
});
|
|
405
523
|
|
|
406
524
|
it('Skips pulling fresh documents', async () => {
|
|
407
525
|
// Arrange - Mint urls
|
|
408
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
526
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'tasks/';
|
|
409
527
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
410
528
|
const childContainerUrl = fakeContainerUrl({ baseUrl: containerUrl });
|
|
411
529
|
const freshDocumentUrl = fakeDocumentUrl({ containerUrl });
|
|
@@ -447,16 +565,18 @@ describe('Sync', () => {
|
|
|
447
565
|
// Arrange - Prepare responses
|
|
448
566
|
const now = Date.now();
|
|
449
567
|
const typeIndexUrl = SolidMock.requireUser().privateTypeIndexUrl ?? '';
|
|
450
|
-
|
|
568
|
+
|
|
569
|
+
FakeServer.respond(
|
|
570
|
+
typeIndexUrl,
|
|
571
|
+
`
|
|
451
572
|
<${typeIndexUrl}> a <http://www.w3.org/ns/solid/terms#TypeIndex> .
|
|
452
573
|
|
|
453
574
|
<#tasks>
|
|
454
575
|
a <http://www.w3.org/ns/solid/terms#TypeRegistration> ;
|
|
455
576
|
<http://www.w3.org/ns/solid/terms#forClass> <https://schema.org/Action> ;
|
|
456
577
|
<http://www.w3.org/ns/solid/terms#instanceContainer> <${containerUrl}> .
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
FakeServer.respondOnce(typeIndexUrl, FakeResponse.success(typeIndexTurtle)); // Initial fetch
|
|
578
|
+
`,
|
|
579
|
+
);
|
|
460
580
|
FakeServer.respondOnce(
|
|
461
581
|
containerUrl,
|
|
462
582
|
containerResponse({
|
|
@@ -559,19 +679,19 @@ describe('Sync', () => {
|
|
|
559
679
|
await Cloud.sync();
|
|
560
680
|
|
|
561
681
|
// Assert
|
|
562
|
-
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(
|
|
682
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(2);
|
|
563
683
|
expect(FakeServer.getRequests(containerUrl)).toHaveLength(2);
|
|
564
684
|
expect(FakeServer.getRequests(childContainerUrl)).toHaveLength(2);
|
|
565
685
|
expect(FakeServer.getRequests(freshDocumentUrl)).toHaveLength(1);
|
|
566
686
|
expect(FakeServer.getRequests(staleDocumentUrl)).toHaveLength(2);
|
|
567
687
|
expect(FakeServer.getRequests(freshChildDocumentUrl)).toHaveLength(1);
|
|
568
688
|
expect(FakeServer.getRequests(staleChildDocumentUrl)).toHaveLength(2);
|
|
569
|
-
expect(FakeServer.getRequests()).toHaveLength(
|
|
689
|
+
expect(FakeServer.getRequests()).toHaveLength(12);
|
|
570
690
|
});
|
|
571
691
|
|
|
572
692
|
it('Ignores missing children', async () => {
|
|
573
693
|
// Arrange - Mint urls
|
|
574
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
694
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'tasks/';
|
|
575
695
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
576
696
|
const existingChildDocumentUrl = fakeContainerUrl({ baseUrl: containerUrl });
|
|
577
697
|
const notFoundChildDocumentUrl = fakeDocumentUrl({ containerUrl });
|
|
@@ -629,7 +749,7 @@ describe('Sync', () => {
|
|
|
629
749
|
|
|
630
750
|
it('Ignores malformed documents', async () => {
|
|
631
751
|
// Arrange - Mint urls
|
|
632
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
752
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
633
753
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
634
754
|
const firstDocumentUrl = fakeDocumentUrl({ containerUrl: containerUrl });
|
|
635
755
|
const secondDocumentUrl = fakeDocumentUrl({ containerUrl: containerUrl });
|
|
@@ -692,7 +812,7 @@ describe('Sync', () => {
|
|
|
692
812
|
|
|
693
813
|
it('Ignores malformed documents in individual syncs', async () => {
|
|
694
814
|
// Arrange - Mint urls
|
|
695
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
815
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
696
816
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
697
817
|
const documentUrl = fakeDocumentUrl({ containerUrl: containerUrl });
|
|
698
818
|
|
|
@@ -725,9 +845,155 @@ describe('Sync', () => {
|
|
|
725
845
|
expect(Cloud.localModelUpdates).toEqual({ [movie.url]: 1 });
|
|
726
846
|
});
|
|
727
847
|
|
|
848
|
+
it('Syncs nested fresh containers', async () => {
|
|
849
|
+
// Arrange - Mint urls
|
|
850
|
+
const rootContainerUrl = Solid.requireUser().storageUrls[0];
|
|
851
|
+
const parentContainerUrl = fakeContainerUrl({ baseUrl: rootContainerUrl });
|
|
852
|
+
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
853
|
+
const documentUrl = fakeDocumentUrl({ containerUrl });
|
|
854
|
+
|
|
855
|
+
// Arrange - Prepare models
|
|
856
|
+
const parentContainer = await Workspace.at(rootContainerUrl).create({
|
|
857
|
+
url: parentContainerUrl,
|
|
858
|
+
name: 'Tasks',
|
|
859
|
+
});
|
|
860
|
+
const container = await parentContainer.relatedLists.create({
|
|
861
|
+
url: containerUrl,
|
|
862
|
+
name: 'More Tasks',
|
|
863
|
+
});
|
|
864
|
+
const task = await container.relatedTasks.create({ url: `${documentUrl}#it`, name: 'Sync' });
|
|
865
|
+
|
|
866
|
+
// Arrange - Prepare responses
|
|
867
|
+
const typeIndexUrl = SolidMock.requireUser().privateTypeIndexUrl ?? '';
|
|
868
|
+
const now = Date.now();
|
|
869
|
+
|
|
870
|
+
FakeServer.respond(typeIndexUrl, typeIndexResponse({ [parentContainerUrl]: '<https://schema.org/Action>' }));
|
|
871
|
+
FakeServer.respond(parentContainerUrl, containerResponse({ name: 'Tasks', documentUrls: [containerUrl] }));
|
|
872
|
+
FakeServer.respond(
|
|
873
|
+
containerUrl,
|
|
874
|
+
containerResponse({
|
|
875
|
+
name: 'More Tasks',
|
|
876
|
+
documentUrls: [documentUrl],
|
|
877
|
+
append: `
|
|
878
|
+
<${documentUrl}>
|
|
879
|
+
a <http://www.w3.org/ns/iana/media-types/text/turtle#Resource> ;
|
|
880
|
+
<http://purl.org/dc/terms/modified>
|
|
881
|
+
"${new Date(now).toISOString()}"^^<http://www.w3.org/2001/XMLSchema#dateTime> .
|
|
882
|
+
`,
|
|
883
|
+
}),
|
|
884
|
+
);
|
|
885
|
+
FakeServer.respond(documentUrl, await task.toTurtle());
|
|
886
|
+
FakeServer.respond(`${parentContainerUrl}.meta`);
|
|
887
|
+
FakeServer.respond(`${containerUrl}.meta`);
|
|
888
|
+
|
|
889
|
+
// Arrange - Prepare service
|
|
890
|
+
await DocumentsCache.remember(documentUrl, now);
|
|
891
|
+
await Cloud.launch();
|
|
892
|
+
await Cloud.register(Workspace);
|
|
893
|
+
await Events.emit('application-ready');
|
|
894
|
+
|
|
895
|
+
// Act
|
|
896
|
+
await Cloud.sync();
|
|
897
|
+
|
|
898
|
+
// Assert
|
|
899
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(1);
|
|
900
|
+
expect(FakeServer.getRequests(parentContainerUrl)).toHaveLength(2);
|
|
901
|
+
expect(FakeServer.getRequests(containerUrl)).toHaveLength(2);
|
|
902
|
+
expect(FakeServer.getRequests(`${parentContainerUrl}.meta`)).toHaveLength(1);
|
|
903
|
+
expect(FakeServer.getRequests(`${containerUrl}.meta`)).toHaveLength(1);
|
|
904
|
+
expect(FakeServer.getRequests()).toHaveLength(7);
|
|
905
|
+
|
|
906
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
it('Ignores nested container registrations', async () => {
|
|
910
|
+
// Arrange - Mint urls
|
|
911
|
+
const rootContainerUrl = Solid.requireUser().storageUrls[0];
|
|
912
|
+
const parentContainerUrl = fakeContainerUrl({ baseUrl: rootContainerUrl });
|
|
913
|
+
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
914
|
+
const parentDocumentUrl = fakeDocumentUrl({ containerUrl: parentContainerUrl });
|
|
915
|
+
const documentUrl = fakeDocumentUrl({ containerUrl });
|
|
916
|
+
|
|
917
|
+
// Arrange - Prepare responses
|
|
918
|
+
const typeIndexUrl = SolidMock.requireUser().privateTypeIndexUrl ?? '';
|
|
919
|
+
|
|
920
|
+
FakeServer.respond(
|
|
921
|
+
typeIndexUrl,
|
|
922
|
+
typeIndexResponse({
|
|
923
|
+
[parentContainerUrl]: '<https://schema.org/Movie>',
|
|
924
|
+
[containerUrl]: '<https://schema.org/Movie>',
|
|
925
|
+
}),
|
|
926
|
+
);
|
|
927
|
+
FakeServer.respond(parentContainerUrl, containerResponse({ documentUrls: [containerUrl, parentDocumentUrl] }));
|
|
928
|
+
FakeServer.respond(containerUrl, containerResponse({ documentUrls: [documentUrl] }));
|
|
929
|
+
FakeServer.respond(parentDocumentUrl, movieResponse());
|
|
930
|
+
|
|
931
|
+
// Arrange - Prepare service
|
|
932
|
+
await Cloud.launch();
|
|
933
|
+
await Cloud.register(MoviesContainer);
|
|
934
|
+
await Events.emit('application-ready');
|
|
935
|
+
|
|
936
|
+
// Act
|
|
937
|
+
await Cloud.sync();
|
|
938
|
+
|
|
939
|
+
// Assert
|
|
940
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(1);
|
|
941
|
+
expect(FakeServer.getRequests(parentContainerUrl)).toHaveLength(1);
|
|
942
|
+
expect(FakeServer.getRequests(containerUrl)).toHaveLength(1);
|
|
943
|
+
expect(FakeServer.getRequests(parentDocumentUrl)).toHaveLength(1);
|
|
944
|
+
expect(FakeServer.getRequests()).toHaveLength(4);
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
it('Works with circular relationships', async () => {
|
|
948
|
+
// Arrange
|
|
949
|
+
const typeIndexUrl = SolidMock.requireUser().privateTypeIndexUrl ?? '';
|
|
950
|
+
const containerUrl = fakeContainerUrl();
|
|
951
|
+
const documentUrl = fakeDocumentUrl({ containerUrl });
|
|
952
|
+
const typeIndexTurtle = `
|
|
953
|
+
<${typeIndexUrl}> a <http://www.w3.org/ns/solid/terms#TypeIndex> .
|
|
954
|
+
|
|
955
|
+
<#posts>
|
|
956
|
+
a <http://www.w3.org/ns/solid/terms#TypeRegistration> ;
|
|
957
|
+
<http://www.w3.org/ns/solid/terms#forClass> <https://schema.org/Article> ;
|
|
958
|
+
<http://www.w3.org/ns/solid/terms#instanceContainer> <${containerUrl}> .
|
|
959
|
+
`;
|
|
960
|
+
|
|
961
|
+
FakeServer.respondOnce(typeIndexUrl, FakeResponse.success(typeIndexTurtle));
|
|
962
|
+
FakeServer.respondOnce(containerUrl, containerResponse({ documentUrls: [documentUrl] }));
|
|
963
|
+
FakeServer.respondOnce(
|
|
964
|
+
documentUrl,
|
|
965
|
+
`
|
|
966
|
+
<#it>
|
|
967
|
+
a <https://schema.org/Article> ;
|
|
968
|
+
<https://schema.org/name> "Title" ;
|
|
969
|
+
<https://schema.org/author> <#author> .
|
|
970
|
+
|
|
971
|
+
<#author>
|
|
972
|
+
a <https://schema.org/Person> ;
|
|
973
|
+
<https://schema.org/name> "Author" .
|
|
974
|
+
`,
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
Cloud.ready = true;
|
|
978
|
+
|
|
979
|
+
await Cloud.launch();
|
|
980
|
+
await Cloud.register(Post);
|
|
981
|
+
await Events.emit('application-ready');
|
|
982
|
+
|
|
983
|
+
// Act
|
|
984
|
+
await Cloud.sync();
|
|
985
|
+
|
|
986
|
+
// Assert
|
|
987
|
+
const [post] = await Post.all();
|
|
988
|
+
|
|
989
|
+
expect(post).toBeInstanceOf(Post);
|
|
990
|
+
expect(post?.author).toBeInstanceOf(Person);
|
|
991
|
+
expect(post?.author?.posts?.[0]).toBe(post);
|
|
992
|
+
});
|
|
993
|
+
|
|
728
994
|
testRegisterVariants('Leaves tombstones behind', async (registerModels) => {
|
|
729
995
|
// Arrange - Mint urls
|
|
730
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
996
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
731
997
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
732
998
|
const documentUrl = fakeDocumentUrl({ containerUrl });
|
|
733
999
|
|
|
@@ -783,14 +1049,16 @@ describe('Sync', () => {
|
|
|
783
1049
|
crdt:deletedAt "[[.*]]"^^xsd:dateTime .
|
|
784
1050
|
} .
|
|
785
1051
|
`);
|
|
1052
|
+
|
|
1053
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
786
1054
|
});
|
|
787
1055
|
|
|
788
1056
|
testRegisterVariants('Deletes tombstone models', async (registerModels, variant) => {
|
|
789
1057
|
const usingContainers = variant === 'container registration';
|
|
790
1058
|
|
|
791
1059
|
// Arrange - Mint urls
|
|
792
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
793
|
-
const containerUrl =
|
|
1060
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
1061
|
+
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
794
1062
|
const documentUrl = fakeDocumentUrl({ containerUrl });
|
|
795
1063
|
|
|
796
1064
|
// Arrange - Prepare models
|
|
@@ -816,12 +1084,17 @@ describe('Sync', () => {
|
|
|
816
1084
|
FakeResponse.success('<> a <http://www.w3.org/ns/ldp#Container> .'),
|
|
817
1085
|
);
|
|
818
1086
|
FakeServer.respondOnce(`${containerUrl}.meta`, FakeResponse.success()); // Update meta
|
|
819
|
-
FakeServer.respondOnce(typeIndexUrl, typeIndexResponse()); // Check before update
|
|
820
|
-
FakeServer.respondOnce(typeIndexUrl, FakeResponse.success()); // Update
|
|
821
1087
|
} else {
|
|
822
1088
|
FakeServer.respondOnce(documentUrl, FakeResponse.notFound()); // Tombstone check
|
|
823
1089
|
}
|
|
824
1090
|
|
|
1091
|
+
FakeServer.respondOnce(typeIndexUrl, typeIndexResponse()); // Check before update
|
|
1092
|
+
FakeServer.respondOnce(typeIndexUrl, FakeResponse.success()); // Update
|
|
1093
|
+
FakeServer.respondOnce(
|
|
1094
|
+
typeIndexUrl,
|
|
1095
|
+
typeIndexResponse({ [containerUrl]: '<https://schema.org/Movie>' }), // Second sync
|
|
1096
|
+
);
|
|
1097
|
+
|
|
825
1098
|
FakeServer.respondOnce(documentUrl, FakeResponse.notFound()); // Existence check
|
|
826
1099
|
FakeServer.respondOnce(documentUrl, FakeResponse.success()); // Create
|
|
827
1100
|
|
|
@@ -854,9 +1127,6 @@ describe('Sync', () => {
|
|
|
854
1127
|
|
|
855
1128
|
// Assert
|
|
856
1129
|
const freshMovie = await Movie.at(containerUrl).find(movie.url);
|
|
857
|
-
const cloud = Cloud;
|
|
858
|
-
|
|
859
|
-
cloud;
|
|
860
1130
|
|
|
861
1131
|
expect(freshMovie).toBeNull();
|
|
862
1132
|
|
|
@@ -868,23 +1138,26 @@ describe('Sync', () => {
|
|
|
868
1138
|
expect(freshContainer.movies).toHaveLength(0);
|
|
869
1139
|
expect(freshContainer.resourceUrls).toHaveLength(1);
|
|
870
1140
|
|
|
871
|
-
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(
|
|
1141
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(4);
|
|
872
1142
|
expect(FakeServer.getRequests(containerUrl)).toHaveLength(5);
|
|
873
1143
|
expect(FakeServer.getRequests(documentUrl)).toHaveLength(3);
|
|
874
1144
|
expect(FakeServer.getRequests(`${containerUrl}.meta`)).toHaveLength(1);
|
|
875
|
-
expect(FakeServer.getRequests()).toHaveLength(
|
|
1145
|
+
expect(FakeServer.getRequests()).toHaveLength(13);
|
|
876
1146
|
} else {
|
|
877
|
-
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(
|
|
878
|
-
expect(FakeServer.getRequests(
|
|
879
|
-
expect(FakeServer.getRequests()).toHaveLength(5);
|
|
1147
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(4);
|
|
1148
|
+
expect(FakeServer.getRequests(containerUrl)).toHaveLength(1);
|
|
1149
|
+
expect(FakeServer.getRequests(documentUrl)).toHaveLength(5);
|
|
1150
|
+
expect(FakeServer.getRequests()).toHaveLength(10);
|
|
880
1151
|
}
|
|
1152
|
+
|
|
1153
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
881
1154
|
});
|
|
882
1155
|
|
|
883
1156
|
testRegisterVariants('Deletes updated tombstone models', async (registerModels, variant) => {
|
|
884
1157
|
const usingContainers = variant === 'container registration';
|
|
885
1158
|
|
|
886
1159
|
// Arrange - Mint urls
|
|
887
|
-
const parentContainerUrl = Solid.requireUser().storageUrls[0];
|
|
1160
|
+
const parentContainerUrl = Solid.requireUser().storageUrls[0] + 'movies/';
|
|
888
1161
|
const containerUrl = fakeContainerUrl({ baseUrl: parentContainerUrl });
|
|
889
1162
|
const documentUrl = fakeDocumentUrl({ containerUrl });
|
|
890
1163
|
|
|
@@ -911,12 +1184,13 @@ describe('Sync', () => {
|
|
|
911
1184
|
FakeResponse.success('<> a <http://www.w3.org/ns/ldp#Container> .'),
|
|
912
1185
|
);
|
|
913
1186
|
FakeServer.respondOnce(`${containerUrl}.meta`, FakeResponse.success()); // Update meta
|
|
914
|
-
FakeServer.respondOnce(typeIndexUrl, typeIndexResponse()); // Check before update
|
|
915
|
-
FakeServer.respondOnce(typeIndexUrl, FakeResponse.success()); // Update
|
|
916
1187
|
} else {
|
|
917
1188
|
FakeServer.respondOnce(documentUrl, FakeResponse.notFound()); // Tombstone check
|
|
918
1189
|
}
|
|
919
1190
|
|
|
1191
|
+
FakeServer.respondOnce(typeIndexUrl, typeIndexResponse()); // Check before update
|
|
1192
|
+
FakeServer.respondOnce(typeIndexUrl, FakeResponse.success()); // Update
|
|
1193
|
+
|
|
920
1194
|
FakeServer.respondOnce(documentUrl, FakeResponse.notFound()); // Existence check
|
|
921
1195
|
FakeServer.respondOnce(documentUrl, FakeResponse.success()); // Create
|
|
922
1196
|
|
|
@@ -958,10 +1232,12 @@ describe('Sync', () => {
|
|
|
958
1232
|
expect(FakeServer.getRequests(`${containerUrl}.meta`)).toHaveLength(1);
|
|
959
1233
|
expect(FakeServer.getRequests()).toHaveLength(12);
|
|
960
1234
|
} else {
|
|
961
|
-
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(
|
|
1235
|
+
expect(FakeServer.getRequests(typeIndexUrl)).toHaveLength(3);
|
|
962
1236
|
expect(FakeServer.getRequests(documentUrl)).toHaveLength(5);
|
|
963
|
-
expect(FakeServer.getRequests()).toHaveLength(
|
|
1237
|
+
expect(FakeServer.getRequests()).toHaveLength(8);
|
|
964
1238
|
}
|
|
1239
|
+
|
|
1240
|
+
expect(Cloud.localModelUpdates).toEqual({});
|
|
965
1241
|
});
|
|
966
1242
|
|
|
967
1243
|
testRegisterVariants('Tracks model updates', async (registerModels) => {
|
|
@@ -971,6 +1247,8 @@ describe('Sync', () => {
|
|
|
971
1247
|
|
|
972
1248
|
await registerModels();
|
|
973
1249
|
|
|
1250
|
+
Cloud.ready = true;
|
|
1251
|
+
|
|
974
1252
|
// Act
|
|
975
1253
|
await movie.update({ releaseDate: new Date('2023-07-14') });
|
|
976
1254
|
await container.relatedMovies.create({ name: 'The Tale of Princess Kaguya' });
|