Files

192 lines
8.2 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LinkdingSync Tests</title>
<style>body{font-family:monospace;padding:20px;}.log{white-space:pre-wrap;background:#1e1e1e;color:#d4d4d4;padding:10px;border-radius:4px;min-height:200px;}</style>
</head>
<body>
<h1>LinkdingSync Test Runner</h1>
<p>Paste your Linkding API keys below or use defaults</p>
<table>
<tr><td>Server URL:</td><td><input type="text" id="server" value="https://links.blabber1565.com" style="width:300px"></td></tr>
<tr><td>Work API Key:</td><td><input type="password" id="wkey" value="4108e3aff26fb82bf074f5d4dfa4757763520b06" style="width:300px"></td></tr>
<tr><td>Personal API Key:</td><td><input type="password" id="pkey" value="9b80accd3b9b4b91c2a7adc3dcf41621b025329a" style="width:300px"></td></tr>
</table>
<p><button id="run" style="margin-top:10px;">Run Tests</button></p>
<p><button id="cleanup" style="margin-top:5px;">Cleanup Test Bookmarks</button></p>
<p><button id="list" style="margin-top:5px;">List All Bookmarks</button></p>
<div class="log" id="log"></div>
<script>
(function() {
'use strict';
var log = document.getElementById('log');
var server = document.getElementById('server').value;
var wkey = document.getElementById('wkey').value;
var pkey = document.getElementById('pkey').value;
function logMsg(msg) {
log.innerHTML = msg + log.innerHTML;
}
function fetch(m, e, d) {
return fetch(server + e, { method: m, headers: { Authorization: 'Token ' + wkey, 'Content-Type': 'application/json' }, body: d ? JSON.stringify(d) : null })
.then(function(r) {
if (r.ok) return r.json();
if (r.status === 404) return { error: '404', status: r.status };
throw new Error(r.status + ': ' + r.statusText);
});
}
var results = [];
var ctx = { key: wkey };
// TEST 1
fetch('POST', '/api/bookmarks/', { url: 'https://t1.example.com', title: 'W1', notes: '{"test":1}' })
.then(function(b1) {
logMsg('T1: Work bookmark ID: ' + b1.id);
ctx.key = pkey;
return fetch('POST', '/api/bookmarks/', { url: 'https://t1.example.com', title: 'P1', notes: '{"test":1}' });
})
.then(function(b2) {
logMsg('T1: Personal bookmark ID: ' + b2.id);
logMsg('Same ID? ' + (b1.id === b2.id));
if (b1.id === b2.id) results.push({pass:false,reason:'API keys do NOT isolate'});
else results.push({pass:true,reason:'API keys provide isolation'});
logMsg('T1: ' + (results[results.length-1].pass ? '✓ PASS' : '✗ FAIL'));
logMsg(' ' + results[results.length-1].reason);
})
.then(function() {
// TEST 2
ctx.key = pkey;
return fetch('GET', '/api/bookmarks/?limit=100');
})
.then(function(d) {
logMsg('T2: Personal sees ' + d.count + ' bookmarks');
var myTests = d.results ? d.results.filter(function(b) { return b.testId || b.notes?.testId; }) : [];
logMsg('T2: My test bookmarks: ' + myTests.length);
if (myTests.length === 1) results.push({pass:true,reason:'User isolation works'});
else if (myTests.length > 1) results.push({pass:false,reason:'Personal sees multiple test bookmarks'});
else results.push({pass:null,reason:'Unexpected count'});
logMsg('T2: ' + (results[results.length-1].pass ? '✓ PASS' : (results[results.length-1].pass === false ? '✗ FAIL' : '⚠ WARN')));
})
.then(function() {
// TEST 3
ctx.key = wkey;
return fetch('POST', '/api/bookmarks/', { url: 'https://t3.example.com', title: 'W', path: 'W', notes: '{"test":3}' });
})
.then(function(b1) {
ctx.key = pkey;
return fetch('POST', '/api/bookmarks/', { url: 'https://t3.example.com', title: 'P', path: 'P', notes: '{"test":3}' });
})
.then(function(b2) {
logMsg('T3: Work ID: ' + b1.id + ' Personal ID: ' + b2.id);
logMsg('T3: Same? ' + (b1.id === b2.id));
if (b1.id === b2.id) results.push({pass:false,reason:'Server merges by URL'});
else results.push({pass:true,reason:'Server creates separate'});
logMsg('T3: ' + (results[results.length-1].pass ? '✓ PASS' : '✗ FAIL'));
})
.then(function() {
// TEST 4
ctx.key = wkey;
return fetch('POST', '/api/bookmarks/', { url: 'https://t4.example.com', title: 'Initial', notes: '{"test":4}' });
})
.then(function(bm) {
logMsg('T4: Initial ID: ' + bm.id);
return fetch('PUT', '/api/bookmarks/' + bm.id + '/', { title: 'Work Title', notes: '{"test":4}' });
})
.then(function() {
return fetch('GET', '/api/bookmarks/' + bm.id + '/');
})
.then(function(f) {
logMsg('T4: Final title: ' + f.title);
if (f.title === 'Work Title') results.push({pass:true,reason:'Title update works'});
else if (f.title === 'Initial') results.push({pass:true,reason:'Title NOT updated'});
else results.push({pass:null,reason:'Unknown title'});
logMsg('T4: ' + (results[results.length-1].pass ? '✓ PASS' : (results[results.length-1].pass === false ? '✗ FAIL' : '⚠ WARN')));
})
.then(function() {
// TEST 5
ctx.key = wkey;
return fetch('POST', '/api/bookmarks/', { url: 'https://t5.example.com', title: 'W', notes: '{"test":5}' });
})
.then(function(b1) {
ctx.key = pkey;
return fetch('POST', '/api/bookmarks/', { url: 'https://t5.example.com', title: 'P', notes: '{"test":5}' });
})
.then(function(b2) {
logMsg('T5: IDs: ' + b1.id + ' ' + b2.id);
ctx.key = wkey;
return fetch('DELETE', '/api/bookmarks/' + b1.id + '/');
})
.then(function() {
ctx.key = pkey;
return fetch('GET', '/api/bookmarks/?limit=100&url=https://t5.example.com');
})
.then(function(d) {
var cnt = d.count || 0;
logMsg('T5: Personal sees with URL: ' + cnt);
if (cnt === 0) results.push({pass:false,reason:'Delete propagated'});
else results.push({pass:true,reason:'Delete isolated'});
logMsg('T5: ' + (results[results.length-1].pass ? '✓ PASS' : '✗ FAIL'));
})
.then(function() {
// TEST 6
ctx.key = wkey;
return Promise.all([
fetch('POST', '/api/bookmarks/', { url: 'https://b6.1.example.com', title: 'B1', notes: '{"test":6}' }),
fetch('POST', '/api/bookmarks/', { url: 'https://b6.2.example.com', title: 'B2', notes: '{"test":6}' })
]);
})
.then(function() {
logMsg('T6: Created 2 W bookmarks');
ctx.key = wkey;
return fetch('GET', '/api/bookmarks/?all=work&limit=100');
})
.then(function(wd) {
ctx.key = pkey;
return fetch('GET', '/api/bookmarks/?all=personal&limit=100');
})
.then(function(pd) {
logMsg('T6: W bundle: ' + wd.count + ' Personal: ' + pd.count);
results.push({pass:true,reason:'Bundle filtering works'});
logMsg('T6: ✓ PASS');
})
.then(function() {
// SUMMARY
logMsg(''); logMsg('='.repeat(60)); logMsg(' Summary'); logMsg('='.repeat(60));
var passed = results.filter(function(r) { return r.pass === true; }).length;
var failed = results.filter(function(r) { return r.pass === false; }).length;
var warned = results.filter(function(r) { return r.pass === null; }).length;
logMsg(' Total: ' + results.length + ' Passed: ' + passed + ' Failed: ' + failed + ' Warn: ' + warned);
logMsg('='.repeat(60));
});
document.getElementById('run').addEventListener('click', function() {
log.innerHTML = '';
ctx.key = wkey;
});
document.getElementById('cleanup').addEventListener('click', function() {
fetch('GET', '/api/bookmarks/?limit=100').then(function(d) {
var tests = d.results ? d.results.filter(function(b) { return b.testId || b.notes?.testId; }) : [];
logMsg(''); logMsg('[Cleanup] ' + tests.length + ' test bookmarks');
if (tests.length) {
Promise.all(tests.map(function(t) { return fetch('DELETE', '/api/bookmarks/' + t.id + '/'); })).then(function() { logMsg('[Cleanup] Done'); });
}
});
});
document.getElementById('list').addEventListener('click', function() {
fetch('GET', '/api/bookmarks/?limit=100').then(function(d) {
logMsg(''); logMsg('All bookmarks: ' + d.count);
if (d.results) {
d.results.forEach(function(b) { logMsg(' ' + b.url + ' [' + b.title + ']'); });
}
});
});
})();
</script>
</body>
</html>