feat: add batched recipe import queue
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createImportBatches,
|
||||
ImportJobProcessorService,
|
||||
} from '../src/services/import-job-processor.service.js';
|
||||
import { FakeImportJobRepository, recipeDraft, source } from './helpers/fakes.js';
|
||||
|
||||
function extractedItem(jobId, rawText) {
|
||||
return {
|
||||
jobId,
|
||||
source: { rawText },
|
||||
};
|
||||
}
|
||||
|
||||
test('가져오기 본문은 최대 3개와 합계 100,000자 단위로 묶는다', () => {
|
||||
const batches = createImportBatches([
|
||||
extractedItem('job-1', 'a'.repeat(40_000)),
|
||||
extractedItem('job-2', 'b'.repeat(40_000)),
|
||||
extractedItem('job-3', 'c'.repeat(30_000)),
|
||||
extractedItem('job-4', 'd'),
|
||||
extractedItem('job-5', 'e'),
|
||||
]);
|
||||
|
||||
assert.deepEqual(batches.map((batch) => batch.map(({ jobId }) => jobId)), [
|
||||
['job-1', 'job-2'],
|
||||
['job-3', 'job-4', 'job-5'],
|
||||
]);
|
||||
});
|
||||
|
||||
test('처리 요청 하나가 대기열 전체를 3개씩 MiniMax 배치 처리한다', async () => {
|
||||
const importJobRepository = new FakeImportJobRepository();
|
||||
for (let index = 1; index <= 4; index += 1) {
|
||||
await importJobRepository.enqueue({
|
||||
ownerGoogleSub: 'google-user-1',
|
||||
url: `https://www.instagram.com/reel/Recipe${index}/`,
|
||||
platform: 'instagram',
|
||||
sourceId: `Recipe${index}`,
|
||||
});
|
||||
}
|
||||
|
||||
const batchJobIds = [];
|
||||
const processor = new ImportJobProcessorService({
|
||||
importJobRepository,
|
||||
sourceExtractor: {
|
||||
async extract(url) {
|
||||
const sourceId = new URL(url).pathname.split('/').filter(Boolean)[1];
|
||||
return {
|
||||
...source,
|
||||
sourceUrl: url,
|
||||
sourceId,
|
||||
rawText: `${sourceId} 본문`,
|
||||
};
|
||||
},
|
||||
},
|
||||
recipeParser: {
|
||||
async parseMany(entries) {
|
||||
batchJobIds.push(entries.map(({ jobId }) => jobId));
|
||||
return entries.map(({ jobId }) => ({ jobId, recipe: recipeDraft }));
|
||||
},
|
||||
},
|
||||
recipeCreator: {
|
||||
async create({ source: recipeSource }) {
|
||||
return { _id: `recipe-${recipeSource.sourceId}` };
|
||||
},
|
||||
},
|
||||
logger: { error() {} },
|
||||
});
|
||||
|
||||
processor.start('google-user-1');
|
||||
await processor.waitForIdle('google-user-1');
|
||||
|
||||
assert.deepEqual(batchJobIds.map((ids) => ids.length), [3, 1]);
|
||||
assert.deepEqual(await importJobRepository.listVisible('google-user-1'), []);
|
||||
assert.ok([...importJobRepository.jobs.values()].every(({ status }) => status === 'completed'));
|
||||
});
|
||||
Reference in New Issue
Block a user