提交 1063f59d 编写于 作者: S Sandeep Somavarapu

Fix #97555

上级 e4e1099f
......@@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { URI } from 'vs/base/common/uri';
import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncPreviewResult, IUserDataManifest } from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources';
import { CancelablePromise } from 'vs/base/common/async';
......@@ -108,7 +108,7 @@ export abstract class AbstractSynchroniser extends Disposable {
protected isEnabled(): boolean { return this.userDataSyncEnablementService.isResourceEnabled(this.resource); }
async sync(ref?: string): Promise<void> {
async sync(manifest: IUserDataManifest | null): Promise<void> {
if (!this.isEnabled()) {
if (this.status !== SyncStatus.Idle) {
await this.stop();
......@@ -129,7 +129,7 @@ export abstract class AbstractSynchroniser extends Disposable {
this.setStatus(SyncStatus.Syncing);
const lastSyncUserData = await this.getLastSyncUserData();
const remoteUserData = ref && lastSyncUserData && lastSyncUserData.ref === ref ? lastSyncUserData : await this.getRemoteUserData(lastSyncUserData);
const remoteUserData = await this.getLatestRemoteUserData(manifest, lastSyncUserData);
let status: SyncStatus = SyncStatus.Idle;
try {
......@@ -144,6 +144,24 @@ export abstract class AbstractSynchroniser extends Disposable {
}
}
private async getLatestRemoteUserData(manifest: IUserDataManifest | null, lastSyncUserData: IRemoteUserData | null): Promise<IRemoteUserData> {
if (lastSyncUserData) {
const latestRef = manifest && manifest.latest ? manifest.latest[this.resource] : undefined;
// Last time synced resource and latest resource on server are same
if (lastSyncUserData.ref === latestRef) {
return lastSyncUserData;
}
// There is no resource on server and last time it was synced with no resource
if (latestRef === undefined && lastSyncUserData.syncData === null) {
return lastSyncUserData;
}
}
return this.getRemoteUserData(lastSyncUserData);
}
async getSyncPreview(): Promise<ISyncPreviewResult> {
if (!this.isEnabled()) {
return { hasLocalChanged: false, hasRemoteChanged: false };
......@@ -225,15 +243,19 @@ export abstract class AbstractSynchroniser extends Disposable {
} catch (e) { /* ignore */ }
}
protected async getLastSyncUserData<T extends IRemoteUserData>(): Promise<T | null> {
async getLastSyncUserData<T extends IRemoteUserData>(): Promise<T | null> {
try {
const content = await this.fileService.readFile(this.lastSyncResource);
const parsed = JSON.parse(content.value.toString());
let syncData: ISyncData = JSON.parse(parsed.content);
const userData: IUserData = parsed as IUserData;
if (userData.content === null) {
return { ref: parsed.ref, syncData: null } as T;
}
let syncData: ISyncData = JSON.parse(userData.content);
// Migration from old content to sync data
if (!isSyncData(syncData)) {
syncData = { version: this.version, content: parsed.content };
syncData = { version: this.version, content: userData.content };
}
return { ...parsed, ...{ syncData, content: undefined } };
......@@ -247,11 +269,11 @@ export abstract class AbstractSynchroniser extends Disposable {
}
protected async updateLastSyncUserData(lastSyncRemoteUserData: IRemoteUserData, additionalProps: IStringDictionary<any> = {}): Promise<void> {
const lastSyncUserData: IUserData = { ref: lastSyncRemoteUserData.ref, content: JSON.stringify(lastSyncRemoteUserData.syncData), ...additionalProps };
const lastSyncUserData: IUserData = { ref: lastSyncRemoteUserData.ref, content: lastSyncRemoteUserData.syncData ? JSON.stringify(lastSyncRemoteUserData.syncData) : null, ...additionalProps };
await this.fileService.writeFile(this.lastSyncResource, VSBuffer.fromString(JSON.stringify(lastSyncUserData)));
}
protected async getRemoteUserData(lastSyncData: IRemoteUserData | null): Promise<IRemoteUserData> {
async getRemoteUserData(lastSyncData: IRemoteUserData | null): Promise<IRemoteUserData> {
const { ref, content } = await this.getUserData(lastSyncData);
let syncData: ISyncData | null = null;
if (content !== null) {
......
......@@ -21,11 +21,12 @@ export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISync
const updated: ISyncExtension[] = [];
if (!remoteExtensions) {
const remote = localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase()));
return {
added,
removed,
updated,
remote: localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase()))
remote: remote.length > 0 ? remote : null
};
}
......
......@@ -18,7 +18,7 @@ export interface IMergeResult {
export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStorage: IStringDictionary<IStorageValue> | null, baseStorage: IStringDictionary<IStorageValue> | null, storageKeys: ReadonlyArray<IStorageKey>, previouslySkipped: string[], logService: ILogService): IMergeResult {
if (!remoteStorage) {
return { remote: localStorage, local: { added: {}, removed: [], updated: {} }, skipped: [] };
return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} }, skipped: [] };
}
const localToRemote = compare(localStorage, remoteStorage);
......
......@@ -248,10 +248,10 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing keybindings.`);
}
if (lastSyncUserData?.ref !== remoteUserData.ref && (content !== null || fileContent !== null)) {
if (lastSyncUserData?.ref !== remoteUserData.ref) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized keybindings...`);
const lastSyncContent = this.toSyncContent(content !== null ? content : fileContent!.value.toString(), null);
await this.updateLastSyncUserData({ ref: remoteUserData.ref, syncData: { version: remoteUserData.syncData!.version, content: lastSyncContent } });
const lastSyncContent = content !== null || fileContent !== null ? this.toSyncContent(content !== null ? content : fileContent!.value.toString(), null) : null;
await this.updateLastSyncUserData({ ref: remoteUserData.ref, syncData: lastSyncContent ? { version: remoteUserData.syncData!.version, content: lastSyncContent } : null });
this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized keybindings`);
}
......@@ -315,7 +315,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
private getKeybindingsContentFromSyncContent(syncContent: string): string | null {
getKeybindingsContentFromSyncContent(syncContent: string): string | null {
try {
const parsed = <ISyncContent>JSON.parse(syncContent);
if (!this.configurationService.getValue<boolean>('sync.keybindingsPerPlatform')) {
......
......@@ -357,7 +357,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
return remoteUserData.syncData ? this.parseSettingsSyncContent(remoteUserData.syncData.content) : null;
}
private parseSettingsSyncContent(syncContent: string): ISettingsSyncContent | null {
parseSettingsSyncContent(syncContent: string): ISettingsSyncContent | null {
try {
const parsed = <ISettingsSyncContent>JSON.parse(syncContent);
return isSettingsSyncContent(parsed) ? parsed : /* migrate */ { settings: syncContent };
......
......@@ -26,7 +26,7 @@ export function merge(local: IStringDictionary<string>, remote: IStringDictionar
removed: values(removed),
updated,
conflicts: [],
remote: local
remote: Object.keys(local).length > 0 ? local : null
};
}
......
......@@ -285,7 +285,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
private async doGeneratePreview(local: IStringDictionary<IFileContent>, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resolvedConflicts: IStringDictionary<string | null> = {}, token: CancellationToken = CancellationToken.None): Promise<ISinppetsSyncPreviewResult> {
const localSnippets = this.toSnippetsContents(local);
const remoteSnippets: IStringDictionary<string> | null = remoteUserData.syncData ? this.parseSnippets(remoteUserData.syncData) : null;
const lastSyncSnippets: IStringDictionary<string> | null = lastSyncUserData ? this.parseSnippets(lastSyncUserData.syncData!) : null;
const lastSyncSnippets: IStringDictionary<string> | null = lastSyncUserData && lastSyncUserData.syncData ? this.parseSnippets(lastSyncUserData.syncData) : null;
if (remoteSnippets) {
this.logService.trace(`${this.syncResourceLogLabel}: Merging remote snippets with local snippets...`);
......
......@@ -274,7 +274,7 @@ export interface IUserDataSynchroniser {
pull(): Promise<void>;
push(): Promise<void>;
sync(ref?: string): Promise<void>;
sync(manifest: IUserDataManifest | null): Promise<void>;
stop(): Promise<void>;
getSyncPreview(): Promise<ISyncPreviewResult>
......
......@@ -136,7 +136,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
for (const synchroniser of this.synchronisers) {
try {
await synchroniser.sync(manifest && manifest.latest ? manifest.latest[synchroniser.resource] : undefined);
await synchroniser.sync(manifest);
} catch (e) {
this.handleSyncError(e, synchroniser.resource);
this._syncErrors.push([synchroniser.resource, UserDataSyncError.toUserDataSyncError(e)]);
......
......@@ -44,11 +44,58 @@ suite('GlobalStateSync', () => {
teardown(() => disposableStore.clear());
test('when global state does not exist', async () => {
assert.deepEqual(await testObject.getLastSyncUserData(), null);
let manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
]);
const lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.equal(lastSyncUserData!.syncData, null);
manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
});
test('when global state is created after first sync', async () => {
await testObject.sync(await testClient.manifest());
updateStorage('a', 'value1', testClient);
let lastSyncUserData = await testObject.getLastSyncUserData();
const manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
]);
lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.deepEqual(JSON.parse(lastSyncUserData!.syncData!.content).storage, { 'a': { version: 1, value: 'value1' } });
});
test('first time sync - outgoing to server (no state)', async () => {
updateStorage('a', 'value1', testClient);
await updateLocale(testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -63,7 +110,7 @@ suite('GlobalStateSync', () => {
await updateLocale(client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -76,7 +123,7 @@ suite('GlobalStateSync', () => {
await client2.sync();
updateStorage('b', 'value2', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -94,7 +141,7 @@ suite('GlobalStateSync', () => {
await client2.sync();
updateStorage('a', 'value2', client2);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -109,10 +156,10 @@ suite('GlobalStateSync', () => {
test('sync adding a storage value', async () => {
updateStorage('a', 'value1', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
updateStorage('b', 'value2', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -127,10 +174,10 @@ suite('GlobalStateSync', () => {
test('sync updating a storage value', async () => {
updateStorage('a', 'value1', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
updateStorage('a', 'value2', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -145,10 +192,10 @@ suite('GlobalStateSync', () => {
test('sync removing a storage value', async () => {
updateStorage('a', 'value1', testClient);
updateStorage('b', 'value2', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
removeStorage('b', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { IUserDataSyncStoreService, IUserDataSyncService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { IFileService } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync';
import { VSBuffer } from 'vs/base/common/buffer';
suite('KeybindingsSync', () => {
const disposableStore = new DisposableStore();
const server = new UserDataSyncTestServer();
let client: UserDataSyncClient;
let testObject: KeybindingsSynchroniser;
setup(async () => {
client = disposableStore.add(new UserDataSyncClient(server));
await client.setUp(true);
testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Keybindings) as KeybindingsSynchroniser;
disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear()));
});
teardown(() => disposableStore.clear());
test('when keybindings file does not exist', async () => {
const fileService = client.instantiationService.get(IFileService);
const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource;
assert.deepEqual(await testObject.getLastSyncUserData(), null);
let manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
]);
assert.ok(!await fileService.exists(keybindingsResource));
const lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.equal(lastSyncUserData!.syncData, null);
manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
});
test('when keybindings file is created after first sync', async () => {
const fileService = client.instantiationService.get(IFileService);
const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource;
await testObject.sync(await client.manifest());
await fileService.createFile(keybindingsResource, VSBuffer.fromString('[]'));
let lastSyncUserData = await testObject.getLastSyncUserData();
const manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
]);
lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.equal(testObject.getKeybindingsContentFromSyncContent(lastSyncUserData!.syncData!.content!), '[]');
});
});
......@@ -43,13 +43,67 @@ suite('SettingsSync', () => {
setup(async () => {
client = disposableStore.add(new UserDataSyncClient(server));
await client.setUp();
await client.setUp(true);
testObject = (client.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.Settings) as SettingsSynchroniser;
disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear()));
});
teardown(() => disposableStore.clear());
test('when settings file does not exist', async () => {
const fileService = client.instantiationService.get(IFileService);
const settingResource = client.instantiationService.get(IEnvironmentService).settingsResource;
assert.deepEqual(await testObject.getLastSyncUserData(), null);
let manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
]);
assert.ok(!await fileService.exists(settingResource));
const lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.equal(lastSyncUserData!.syncData, null);
manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
});
test('when settings file is created after first sync', async () => {
const fileService = client.instantiationService.get(IFileService);
const settingsResource = client.instantiationService.get(IEnvironmentService).settingsResource;
await testObject.sync(await client.manifest());
await fileService.createFile(settingsResource, VSBuffer.fromString('{}'));
let lastSyncUserData = await testObject.getLastSyncUserData();
const manifest = await client.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
]);
lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.equal(testObject.parseSettingsSyncContent(lastSyncUserData!.syncData!.content!)?.settings, '{}');
});
test('sync for first time to the server', async () => {
const expected =
`{
......@@ -75,7 +129,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(expected);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -99,7 +153,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -130,7 +184,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -161,7 +215,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -185,7 +239,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -203,7 +257,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -237,7 +291,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -285,7 +339,7 @@ suite('SettingsSync', () => {
}`;
await updateSettings(settingsContent);
await testObject.sync();
await testObject.sync(await client.manifest());
const { content } = await client.read(testObject.resource);
assert.ok(content !== null);
......@@ -333,7 +387,7 @@ suite('SettingsSync', () => {
await updateSettings(expected);
try {
await testObject.sync();
await testObject.sync(await client.manifest());
assert.fail('should fail with invalid content error');
} catch (e) {
assert.ok(e instanceof UserDataSyncError);
......
......@@ -167,11 +167,62 @@ suite('SnippetsSync', () => {
teardown(() => disposableStore.clear());
test('when snippets does not exist', async () => {
const fileService = testClient.instantiationService.get(IFileService);
const snippetsResource = testClient.instantiationService.get(IEnvironmentService).snippetsHome;
assert.deepEqual(await testObject.getLastSyncUserData(), null);
let manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'GET', url: `${server.url}/v1/resource/${testObject.resource}/latest`, headers: {} },
]);
assert.ok(!await fileService.exists(snippetsResource));
const lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.equal(lastSyncUserData!.syncData, null);
manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, []);
});
test('when snippet is created after first sync', async () => {
await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet1, testClient);
let lastSyncUserData = await testObject.getLastSyncUserData();
const manifest = await testClient.manifest();
server.reset();
await testObject.sync(manifest);
assert.deepEqual(server.requests, [
{ type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': lastSyncUserData?.ref } },
]);
lastSyncUserData = await testObject.getLastSyncUserData();
const remoteUserData = await testObject.getRemoteUserData(null);
assert.deepEqual(lastSyncUserData!.ref, remoteUserData.ref);
assert.deepEqual(lastSyncUserData!.syncData, remoteUserData.syncData);
assert.deepEqual(lastSyncUserData!.syncData!.content, JSON.stringify({ 'html.json': htmlSnippet1 }));
});
test('first time sync - outgoing to server (no snippets)', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
await updateSnippet('typescript.json', tsSnippet1, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -186,7 +237,7 @@ suite('SnippetsSync', () => {
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -201,7 +252,7 @@ suite('SnippetsSync', () => {
await client2.sync();
await updateSnippet('typescript.json', tsSnippet1, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -221,7 +272,7 @@ suite('SnippetsSync', () => {
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
......@@ -234,7 +285,7 @@ suite('SnippetsSync', () => {
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
const conflicts = testObject.conflicts;
await testObject.acceptConflict(conflicts[0].local, htmlSnippet1);
......@@ -259,7 +310,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet2, testClient);
await updateSnippet('typescript.json', tsSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
......@@ -278,7 +329,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet2, testClient);
await updateSnippet('typescript.json', tsSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
let conflicts = testObject.conflicts;
await testObject.acceptConflict(conflicts[0].local, htmlSnippet2);
......@@ -299,7 +350,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet2, testClient);
await updateSnippet('typescript.json', tsSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
const conflicts = testObject.conflicts;
await testObject.acceptConflict(conflicts[0].local, htmlSnippet2);
......@@ -324,10 +375,10 @@ suite('SnippetsSync', () => {
test('sync adding a snippet', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
await updateSnippet('typescript.json', tsSnippet1, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -345,12 +396,12 @@ suite('SnippetsSync', () => {
test('sync adding a snippet - accept', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -362,10 +413,10 @@ suite('SnippetsSync', () => {
test('sync updating a snippet', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -381,12 +432,12 @@ suite('SnippetsSync', () => {
test('sync updating a snippet - accept', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -397,13 +448,13 @@ suite('SnippetsSync', () => {
test('sync updating a snippet - conflict', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet3, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
const local = joinPath(environmentService.userDataSyncHome, testObject.resource, PREVIEW_DIR_NAME, 'html.json');
......@@ -413,13 +464,13 @@ suite('SnippetsSync', () => {
test('sync updating a snippet - resolve conflict', async () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet3, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
await testObject.acceptConflict(testObject.conflicts[0].local, htmlSnippet2);
assert.equal(testObject.status, SyncStatus.Idle);
......@@ -437,10 +488,10 @@ suite('SnippetsSync', () => {
test('sync removing a snippet', async () => {
await updateSnippet('html.json', htmlSnippet1, testClient);
await updateSnippet('typescript.json', tsSnippet1, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -459,12 +510,12 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -478,13 +529,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await updateSnippet('html.json', htmlSnippet2, client2);
await client2.sync();
await removeSnippet('html.json', testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -499,13 +550,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.HasConflicts);
const environmentService = testClient.instantiationService.get(IEnvironmentService);
......@@ -517,13 +568,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
await testObject.acceptConflict(testObject.conflicts[0].local, htmlSnippet3);
assert.equal(testObject.status, SyncStatus.Idle);
......@@ -544,13 +595,13 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
await removeSnippet('html.json', client2);
await client2.sync();
await updateSnippet('html.json', htmlSnippet2, testClient);
await testObject.sync();
await testObject.sync(await testClient.manifest());
await testObject.acceptConflict(testObject.conflicts[0].local, '');
assert.equal(testObject.status, SyncStatus.Idle);
......@@ -601,7 +652,7 @@ suite('SnippetsSync', () => {
await updateSnippet('html.json', htmlSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......@@ -622,7 +673,7 @@ suite('SnippetsSync', () => {
await updateSnippet('typescript.json', tsSnippet1, client2);
await client2.sync();
await testObject.sync();
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
......
......@@ -79,7 +79,7 @@ suite('TestSynchronizer', () => {
const promise = Event.toPromise(testObject.onDoSyncCall.event);
testObject.sync();
testObject.sync(await client.manifest());
await promise;
assert.deepEqual(actual, [SyncStatus.Syncing]);
......@@ -94,7 +94,7 @@ suite('TestSynchronizer', () => {
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
await testObject.sync();
await testObject.sync(await client.manifest());
assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]);
assert.deepEqual(testObject.status, SyncStatus.Idle);
......@@ -107,7 +107,7 @@ suite('TestSynchronizer', () => {
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
await testObject.sync();
await testObject.sync(await client.manifest());
assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.HasConflicts]);
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
......@@ -122,7 +122,7 @@ suite('TestSynchronizer', () => {
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
try {
await testObject.sync();
await testObject.sync(await client.manifest());
assert.fail('Should fail');
} catch (e) {
assert.deepEqual(actual, [SyncStatus.Syncing, SyncStatus.Idle]);
......@@ -134,12 +134,12 @@ suite('TestSynchronizer', () => {
const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings);
const promise = Event.toPromise(testObject.onDoSyncCall.event);
testObject.sync();
testObject.sync(await client.manifest());
await promise;
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
await testObject.sync();
await testObject.sync(await client.manifest());
assert.deepEqual(actual, []);
assert.deepEqual(testObject.status, SyncStatus.Syncing);
......@@ -154,7 +154,7 @@ suite('TestSynchronizer', () => {
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
await testObject.sync();
await testObject.sync(await client.manifest());
assert.deepEqual(actual, []);
assert.deepEqual(testObject.status, SyncStatus.Idle);
......@@ -164,11 +164,11 @@ suite('TestSynchronizer', () => {
const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings);
testObject.syncResult = { status: SyncStatus.HasConflicts };
testObject.syncBarrier.open();
await testObject.sync();
await testObject.sync(await client.manifest());
const actual: SyncStatus[] = [];
disposableStore.add(testObject.onDidChangeStatus(status => actual.push(status)));
await testObject.sync();
await testObject.sync(await client.manifest());
assert.deepEqual(actual, []);
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
......@@ -178,7 +178,7 @@ suite('TestSynchronizer', () => {
const testObject: TestSynchroniser = client.instantiationService.createInstance(TestSynchroniser, SyncResource.Settings);
// Sync once
testObject.syncBarrier.open();
await testObject.sync();
await testObject.sync(await client.manifest());
testObject.syncBarrier = new Barrier();
// update remote data before syncing so that 412 is thrown by server
......@@ -190,8 +190,9 @@ suite('TestSynchronizer', () => {
});
// Start sycing
const { ref } = await userDataSyncStoreService.read(testObject.resource, null);
await testObject.sync(ref);
const manifest = await client.manifest();
const ref = manifest!.latest![testObject.resource];
await testObject.sync(await client.manifest());
assert.deepEqual(server.requests, [
{ type: 'POST', url: `${server.url}/v1/resource/${testObject.resource}`, headers: { 'If-Match': ref } },
......
......@@ -124,6 +124,10 @@ export class UserDataSyncClient extends Disposable {
return this.instantiationService.get(IUserDataSyncStoreService).read(resource, null);
}
manifest(): Promise<IUserDataManifest | null> {
return this.instantiationService.get(IUserDataSyncStoreService).manifest();
}
}
export class UserDataSyncTestServer implements IRequestService {
......
......@@ -45,7 +45,6 @@ suite('UserDataSyncService', () => {
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
......@@ -71,13 +70,10 @@ suite('UserDataSyncService', () => {
{ type: 'GET', url: `${target.url}/v1/resource/keybindings/latest`, headers: {} },
// Snippets
{ type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '0' } },
// Global state
{ type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
......@@ -384,7 +380,6 @@ suite('UserDataSyncService', () => {
{ type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } },
// Extensions
{ type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} },
{ type: 'POST', url: `${target.url}/v1/resource/extensions`, headers: { 'If-Match': '0' } },
// Manifest
{ type: 'GET', url: `${target.url}/v1/manifest`, headers: {} },
]);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册