Files

41 lines
3.2 KiB
TypeScript

import assert from 'node:assert/strict';
import {test} from 'node:test';
import {database} from '../lib/store.ts';
import {dashboard,getRun,start,step} from '../lib/scanner.ts';
import type {Run} from '../lib/model.ts';
import {result} from '../lib/access.ts';
import {useTestDatabase} from './test-database.ts';
useTestDatabase('scanner');
test('only one shared manual run can be started',async()=>{
const [first,second]=await Promise.all([start(false),start(false)]);
assert.equal(first?.id,second?.id);assert.equal(second?.force,false);
});
test('a failed record transaction preserves its queue and can resume',async()=>{
const run=(await getRun())!;run.phase='links';run.links=['http://127.0.0.1/'];run.totalLinks=1;run.checked=0;
const db=await database();await db.query("UPDATE scan SET data=$1,lease=0,token='' WHERE id=1",[JSON.stringify(run)]);
await db.query("CREATE FUNCTION reject_record() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'simulated disk failure'; END $$");
await db.query('CREATE TRIGGER fail_record BEFORE INSERT ON records FOR EACH ROW EXECUTE FUNCTION reject_record()');
try{await assert.rejects(step(),/simulated disk failure/);const afterFailure=(await getRun())!;assert.deepEqual(afterFailure.links,run.links);assert.equal(afterFailure.checked,0);assert.equal((await dashboard()).checks['http://127.0.0.1/'],undefined);}
finally{await db.query('DROP TRIGGER fail_record ON records');await db.query('DROP FUNCTION reject_record()');}
const resumed=(await step())!;assert.equal(resumed.status,'complete');assert.equal(resumed.checked,1);assert.equal((await dashboard()).checks['http://127.0.0.1/'].status,'network');
});
test('an active lease prevents two workers from processing the same queue',async()=>{
const run=(await start(false))!;run.phase='links';run.links=['http://127.0.0.1/'];
await (await database()).query('UPDATE scan SET data=$1,lease=$2,token=$3 WHERE id=1',[JSON.stringify(run),Date.now()+60000,'another-worker']);
const unchanged=(await step()) as Run;assert.deepEqual(unchanged.links,run.links);assert.equal(unchanged.checked,0);
});
test('a step consumes only one bibliography link',async()=>{
const db=await database(),run=(await getRun())!;run.phase='links';run.links=['http://127.0.0.1/a','http://127.0.0.1/b'];run.checked=0;
await db.query("UPDATE scan SET data=$1,lease=0,token='' WHERE id=1",[JSON.stringify(run)]);
const after=(await step())!;assert.equal(after.checked,1);assert.deepEqual(after.links,['http://127.0.0.1/b']);assert.equal(after.status,'running');
});
test('previous HTTP-only results are not presented as available after a rules upgrade',async()=>{
const db=await database(),old={url:'https://books.org/old',status:'ok',reason:'HTTP 200',checkedAt:new Date().toISOString()};
await db.query('INSERT INTO records(key,value) VALUES($1,$2)', ['link:'+old.url,JSON.stringify(old)]);
assert.equal((await dashboard()).checks[old.url].status,'unknown');
const current=result(old.url,'auth','Для чтения книги требуется авторизация.');await db.query('UPDATE records SET value=$1 WHERE key=$2',[JSON.stringify(current),'link:'+old.url]);
assert.equal((await dashboard()).checks[old.url].status,'auth');
});