UNPKG

fen-queue-processor

Version:

Process and analyzes queue of chess positions in FEN format

191 lines (178 loc) 8.29 kB
const Processor = require('../processor'); describe('processor', () => { const fen = 'pnK...'; const moves = ['d3','d5']; const depth = 40; const item = {fen, depth, moves}; const score = -0.24; const bestMove = 'd4'; const queue = { delete: () => {}, get: ({fen, depth, moves}) => (item), getAllItems: () => ([]) }; const evaluation = {save: () => {}}; const analyzer = {analyze: () => {}}; const evaluationSource1 = {getFen: ()=>{}}; const evaluationSource2 = {getFen: ()=>{}}; const evaluationSources = [evaluationSource1, evaluationSource2]; const strategy = {isInteresting: () => true}; const stubConsole = {error: () => {}, log: () => {}}; let processor; beforeEach(() => { processor = new Processor({queue, evaluation, analyzer, evaluationSources, strategy}); processor.Console = stubConsole; }); it('rejects evaluationSources without getFen function', () => { expect(()=>{ new Processor({queue, evaluation, analyzer, evaluationSources: [{}], strategy}); }).toThrow('evaluation sources should have getFen function'); }); it('go smoothly if getFen function is defined in evaluation sources', () => { expect(new Processor({queue, evaluation, analyzer, evaluationSources: [evaluationSource1], strategy})).toBeDefined(); }); describe('process', () => { it('works asynchronous', () => { expect(processor.process()).toEqual(jasmine.any(Promise)); }); it('does not allow to run 2 processSync in the same time', done => { spyOn(processor, 'processSync'); processor.process().then(() => { expect(processor.processSync).toHaveBeenCalledTimes(1); done(); }); processor.process(); }); it('runs eventually the required number of times', done => { spyOn(processor, 'processSync'); const promises = []; promises.push(processor.process()); promises.push(processor.process()); promises.push(processor.process()); Promise.all(promises).then(() => { expect(processor.processSync).toHaveBeenCalledTimes(3); done(); }).catch(err => { done(err); }); }); it('logs error if processSync throws an error', async () => { spyOn(stubConsole, 'error').and.stub(); spyOn(processor, 'processSync').and.throwError('unexpected failure'); await processor.process(); expect(stubConsole.error).toHaveBeenCalled(); }); }); describe('processItem', () => { it('uses analyzer if no immediate answer in evaluation sources', async () => { spyOn(analyzer, 'analyze').and.stub(); await processor.processItem(item); expect(analyzer.analyze).toHaveBeenCalledWith(item); }); it('get fen from the first evaluation source', async () => { spyOn(evaluationSource1, 'getFen').and.returnValue({fen, bestMove, score, depth}); await processor.processItem(item); expect(evaluationSource1.getFen).toHaveBeenCalledWith(item); }); it('register evaluation for result from the first available source', async () => { spyOn(evaluationSource1, 'getFen').and.returnValue({fen, bestMove, score, depth}); spyOn(evaluationSource2, 'getFen').and.stub(); spyOn(processor, 'registerEvaluation'); await processor.processItem(item); expect(processor.registerEvaluation).toHaveBeenCalledWith({fen, bestMove, score, depth}); }); it('consider an answer is available only if bestMove has been provided', async () => { spyOn(evaluationSource1, 'getFen').and.returnValue({fen, score, depth}); spyOn(evaluationSource2, 'getFen').and.returnValue({fen: 'bbb', bestMove, score: 0.02, depth: 20}); spyOn(processor, 'registerEvaluation'); await processor.processItem(item); expect(processor.registerEvaluation).toHaveBeenCalledWith({fen: 'bbb', bestMove, score: 0.02, depth: 20}); }); it('analyzer is not called if evaluation sources has an answer', async () => { spyOn(evaluationSource2, 'getFen').and.returnValue({fen, bestMove, score, depth}); spyOn(analyzer, 'analyze').and.stub(); await processor.processItem(item); expect(analyzer.analyze).not.toHaveBeenCalled(); }); it('registers evaluation if there is answer in evaluation sources', async () => { spyOn(evaluationSource1, 'getFen').and.returnValue({fen, bestMove, score, depth}); spyOn(processor, 'registerEvaluation').and.stub(); await processor.processItem(item); expect(processor.registerEvaluation).toHaveBeenCalledWith({fen, bestMove, score, depth}); }); it('do not bother evaluation sources if position is not interesting for analysis', async () => { spyOn(evaluationSource1, 'getFen'); spyOn(strategy, 'isInteresting').and.returnValue(false); await processor.processItem({moves: ['h4', 'h5'], fen, depth}); expect(evaluationSource1.getFen).not.toHaveBeenCalled(); }); it('do not bother evaluation sources if position interest is undefined yet', async () => { spyOn(evaluationSource1, 'getFen'); spyOn(strategy, 'isInteresting').and.returnValue(undefined); await processor.processItem({moves: ['h4', 'h5'], fen, depth}); expect(evaluationSource1.getFen).not.toHaveBeenCalled(); }); it('deletes item from queue if it is not interesting for analysis', async () => { spyOn(strategy, 'isInteresting').and.returnValue(false); spyOn(queue, 'delete').and.stub(); await processor.processItem(item); expect(queue.delete).toHaveBeenCalledWith(fen); }); it('leaves item in queue if isInteresting is undefined yet', async () => { spyOn(strategy, 'isInteresting').and.returnValue(undefined); spyOn(queue, 'delete').and.stub(); await processor.processItem(item); expect(queue.delete).not.toHaveBeenCalledWith(fen); }); it('process item if strategy is not defined', async () => { let processor = new Processor({ queue, evaluation, evaluationSources, analyzer }); spyOn(analyzer, 'analyze').and.stub(); await processor.processItem(item); expect(analyzer.analyze).toHaveBeenCalled(); }); }); describe('processSync', () => { it('calls processItem', () => { spyOn(processor, 'processItem').and.stub(); spyOn(queue, 'getAllItems').and.returnValue([item]); processor.processSync(); expect(processor.processItem).toHaveBeenCalledWith(item); }); }); describe('registerEvaluation', () => { it('gets evaluation item from queue', () => { spyOn(queue, 'get').and.returnValue(item); processor.registerEvaluation({fen, bestMove, depth, score}); expect(queue.get).toHaveBeenCalledWith({fen}); }); it('delete item if depth match with item in queue', () => { spyOn(queue, 'delete'); processor.registerEvaluation({fen, depth, score, bestMove}); expect(queue.delete).toHaveBeenCalledWith(fen); }); it('delete item if depth > than depth of item in queue', () => { spyOn(queue, 'delete'); processor.registerEvaluation({fen, depth: depth + 1, score, bestMove}); expect(queue.delete).toHaveBeenCalledWith(fen); }); it('save evaluation if depth match with item in queue', () => { spyOn(queue, 'get').and.returnValue(item); spyOn(evaluation, 'save'); processor.registerEvaluation({fen, depth, score, bestMove}); expect(evaluation.save).toHaveBeenCalledWith({moves, bestMove, score, depth}); }); it('do not delete item from queue if depth < of depth in queue', () => { spyOn(queue, 'get').and.returnValue({fen, depth: 41}); spyOn(queue, 'delete'); processor.registerEvaluation({fen, depth, score, bestMove}); expect(queue.delete).not.toHaveBeenCalled(); }); it('logs error if fen was not in queue', () => { spyOn(processor.Console, 'error').and.stub(); spyOn(queue, 'get').and.returnValue(null); processor.registerEvaluation({ fen: 'unknown '}); expect(processor.Console.error).toHaveBeenCalled(); }); }); });