Files
myworkspace/Linkding Browser Extension/LinkdingSync/tests/test-conflicts.js

196 lines
6.0 KiB
JavaScript

/*
* Test Module: Conflict Resolution
* Tests scenarios 3 and 4
*/
'use strict';
const utils = require('../utils.js').LinkdingSyncTests;
const SCENARIO_NAME = 'Conflict Resolution Tests';
// Test 3: Conflict Resolution - Different Paths
async function test3_ConflictResolution() {
console.log('\n=== Test 3: Conflict Resolution - Different Paths ===');
console.log('Purpose: How server handles same URL in different paths with different API keys');
try {
// Create with work API key
utils.SessionManager.setContext(
CONFIG.serverUrl,
CONFIG.workApiKey,
CONFIG.workUser,
CONFIG.workBundle
);
const workUrl = 'https://conflict-resolution.example.com';
const workBookmark = await utils.Helpers.createBookmark(workUrl, {
title: 'Conflict Resolution Test',
path: 'Work/Development',
notes: 'Work Development Notes'
});
console.log(` Work bookmark ID: ${workBookmark.id}`);
// Create same URL with personal API key
utils.SessionManager.setContext(
CONFIG.serverUrl,
CONFIG.personalApiKey,
CONFIG.personalUser,
CONFIG.personalBundle
);
const personalBookmark = await utils.Helpers.createBookmark(workUrl, {
title: 'Conflict Resolution Test',
path: 'Personal/Notes',
notes: 'Personal Notes'
});
console.log(` Personal bookmark ID: ${personalBookmark.id}`);
// Compare
console.log(`\nComparing bookmark IDs: work=${workBookmark.id}, personal=${personalBookmark.id}`);
if (workBookmark.id === personalBookmark.id) {
utils.Formatters.consoleResult('Test 3', 'FAIL', 'Same bookmark ID');
console.log(' → Server merges bookmarks by URL');
console.log(' → Need path merge strategy');
const state = await utils.Helpers.fetchBookmark(workBookmark.id);
const parsed = utils.Helpers.parseNotes(state.notes);
console.log(` → Current path: ${parsed.path}`);
console.log(` → Current notes: ${parsed.userNotes}`);
return { pass: false, sameId: true, path: parsed.path };
} else {
utils.Formatters.consoleResult('Test 3', 'PASS', 'Different bookmark IDs');
console.log(' → Server creates separate bookmarks per API key');
console.log(' → Can use different API keys for isolation');
return { pass: true, sameId: false, workId: workBookmark.id, personalId: personalBookmark.id };
}
} catch (error) {
utils.Formatters.consoleResult('Test 3', 'FAIL', error.message);
throw error;
}
}
// Test 4: Title/Description Conflict
async function test4_TitleDescriptionConflict() {
console.log('\n=== Test 4: Title/Description Conflict ===');
console.log('Purpose: How server resolves conflicts for title/description fields');
try {
utils.SessionManager.setContext(
CONFIG.serverUrl,
CONFIG.workApiKey,
CONFIG.workUser,
CONFIG.workBundle
);
const testUrl = 'https://title-conflict.example.com';
// Create initial
const bookmark = await utils.Helpers.createBookmark(testUrl, {
title: 'Initial Title',
description: 'Initial Description',
path: 'Initial'
});
// Update via work
await utils.Helpers.updateBookmark(bookmark.id, {
title: 'Work Title',
description: 'Work Description',
notes: JSON.stringify({
path: 'Work/Dev',
userNotes: 'Work notes',
autoTags: [{name: 'Work'}]
})
});
console.log(' Updated via Work: Work Title');
// Update via personal
utils.SessionManager.setContext(
CONFIG.serverUrl,
CONFIG.personalApiKey,
CONFIG.personalUser,
CONFIG.personalBundle
);
await utils.Helpers.updateBookmark(bookmark.id, {
title: 'Personal Title',
description: 'Personal Description',
notes: JSON.stringify({
path: 'Personal/Notes',
userNotes: 'Personal notes',
autoTags: [{name: 'Personal'}]
})
});
console.log(' Updated via Personal: Personal Title');
// Fetch final state
const final = await utils.Helpers.fetchBookmark(bookmark.id);
const parsed = utils.Helpers.parseNotes(final.notes);
console.log('\nFinal state:');
console.log(` Title: ${final.title}`);
console.log(` Description: ${final.description}`);
console.log(` Path: ${parsed.path}`);
console.log(` User notes: ${parsed.userNotes}`);
if (final.title === 'Personal Title') {
utils.Formatters.consoleResult('Test 4', 'PASS', 'Last-write-wins (Personal took precedence)');
return { pass: true, strategy: 'last-write-wins', winner: 'personal' };
} else if (final.title === 'Work Title') {
utils.Formatters.consoleResult('Test 4', 'PASS', 'Last-write-wins (Work took precedence)');
return { pass: true, strategy: 'last-write-wins', winner: 'work' };
} else if (final.title.includes('Work') && final.title.includes('Personal')) {
utils.Formatters.consoleResult('Test 4', 'PASS', 'Merged title');
return { pass: true, strategy: 'merge', winner: 'merged' };
} else {
utils.Formatters.consoleResult('Test 4', 'WARN', 'Unexpected title value');
return { pass: null, strategy: 'unknown', winner: final.title };
}
} catch (error) {
utils.Formatters.consoleResult('Test 4', 'FAIL', error.message);
throw error;
}
}
// Run all tests
async function runConflictTests() {
console.log('\n' + '='.repeat(60));
console.log(' ' + SCENARIO_NAME);
console.log('='.repeat(60));
const results = [];
try {
results[0] = await test3_ConflictResolution();
results[1] = await test4_TitleDescriptionConflict();
} catch (error) {
console.error('Test suite error:', error.message);
utils.Helpers.resetBookmarks();
}
console.log('\n' + '='.repeat(60));
console.log(' Conflict Resolution Tests Complete');
console.log('='.repeat(60));
return results;
}
// Export
window.LinkdingSyncTests.TestConflicts = {
run: runConflictTests,
test3: test3_ConflictResolution,
test4: test4_TitleDescriptionConflict
};