forked from wangziqi/gongxue-base
feat: add import post-check templates
This commit is contained in:
@@ -2659,6 +2659,33 @@ async function testTenantContentAssetsAndImports() {
|
||||
});
|
||||
assert.equal(deniedImport.code, 'TENANT_CONTENT_EDITOR_REQUIRED', 'student should not preview content import');
|
||||
|
||||
const deniedTemplate = await request('/api/tenant-content/imports/templates', {
|
||||
query: { importType: 'questions', format: 'csv' },
|
||||
expectStatus: 403,
|
||||
});
|
||||
assert.equal(deniedTemplate.code, 'TENANT_CONTENT_EDITOR_REQUIRED', 'student should not download import templates');
|
||||
|
||||
const questionFieldMapping = await request('/api/tenant-content/imports/field-mapping', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
query: { importType: 'questions' },
|
||||
});
|
||||
assert.ok(
|
||||
questionFieldMapping.item?.fields?.some(field => field.field === 'content' && field.required === true),
|
||||
'question field mapping should describe required content field',
|
||||
);
|
||||
assert.ok(
|
||||
questionFieldMapping.item?.fields?.some(field => field.aliases?.includes('题干')),
|
||||
'question field mapping should include Chinese aliases for old import operators',
|
||||
);
|
||||
|
||||
const scorelineTemplate = await request('/api/tenant-content/imports/templates', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
query: { importType: 'scoreline', format: 'csv' },
|
||||
});
|
||||
assert.equal(scorelineTemplate.item?.fileName, 'scoreline-import-template.csv', 'template endpoint should return deterministic filename');
|
||||
const scorelineTemplateText = Buffer.from(scorelineTemplate.item?.contentBase64 || '', 'base64').toString('utf8');
|
||||
assert.ok(scorelineTemplateText.includes('kind,legacyId'), 'CSV template should include scoreline headers');
|
||||
|
||||
const oversizedNormalJson = await request('/api/auth/sms/send', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
@@ -2781,11 +2808,29 @@ async function testTenantContentAssetsAndImports() {
|
||||
'valid import should process all valid questions idempotently',
|
||||
);
|
||||
|
||||
const questionPostCheck = await request('/api/tenant-content/imports/post-check', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { jobId: validPreview.job.id },
|
||||
});
|
||||
assert.equal(questionPostCheck.item?.status, 'passed', 'question import post-check should pass after completed import');
|
||||
assert.equal(questionPostCheck.item?.counts?.processedItemCount, 2, 'question post-check should count processed rows');
|
||||
assert.equal(questionPostCheck.item?.counts?.collectionBindingCount, 2, 'question post-check should verify collection bindings');
|
||||
|
||||
const questionPostCheckStatus = await request('/api/tenant-content/imports/post-check', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
query: { jobId: validPreview.job.id },
|
||||
});
|
||||
assert.equal(questionPostCheckStatus.item?.importPostCheck?.status, 'passed', 'post-check result should be readable from job summary');
|
||||
|
||||
const jobs = await request('/api/tenant-content/imports', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
query: { importType: 'questions', limit: 10 },
|
||||
});
|
||||
assert.ok(jobs.items?.some(item => item.id === validPreview.job.id && item.status === 'completed'), 'import job list should include completed job');
|
||||
assert.ok(
|
||||
jobs.items?.some(item => item.id === validPreview.job.id && item.status === 'completed' && item.summary?.importPostCheck?.status === 'passed'),
|
||||
'import job list should include completed job with post-check summary',
|
||||
);
|
||||
|
||||
const importedQuestions = await request('/api/catalog/questions', {
|
||||
query: { collectionId: ids.questionCollection, limit: 100 },
|
||||
@@ -2891,6 +2936,14 @@ async function testTenantContentAssetsAndImports() {
|
||||
});
|
||||
assert.equal(queuedSyncExecution.code, 'IMPORT_JOB_QUEUED', 'queued import job should not be executed synchronously');
|
||||
|
||||
const queuedPostCheck = await request('/api/tenant-content/imports/post-check', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { jobId: asyncQuestionPreview.job.id },
|
||||
expectStatus: 409,
|
||||
});
|
||||
assert.equal(queuedPostCheck.code, 'IMPORT_JOB_NOT_COMPLETED', 'queued import job should not allow post-check before worker completion');
|
||||
|
||||
const vocabEntry = await request('/api/tenant-content/content-entries', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'PUT',
|
||||
@@ -2997,6 +3050,14 @@ async function testTenantContentAssetsAndImports() {
|
||||
(vocabularyImport.item?.insertedCount || 0) + (vocabularyImport.item?.updatedCount || 0) + (vocabularyImport.item?.skippedCount || 0) >= 2,
|
||||
'vocabulary import should process unit and word idempotently',
|
||||
);
|
||||
const vocabularyPostCheck = await request('/api/tenant-content/imports/post-check', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { jobId: vocabularyPreview.job.id },
|
||||
});
|
||||
assert.equal(vocabularyPostCheck.item?.status, 'passed', 'vocabulary import post-check should pass');
|
||||
assert.equal(vocabularyPostCheck.item?.counts?.processedItemCount, 1, 'vocabulary post-check should count unit import items');
|
||||
assert.equal(vocabularyPostCheck.item?.counts?.vocabularyWordCount >= 1, true, 'vocabulary post-check should verify imported words');
|
||||
|
||||
const vocabularyUnits = await request('/api/catalog/vocabulary-units', {
|
||||
query: { regionId: ids.region },
|
||||
@@ -3124,6 +3185,13 @@ async function testTenantContentAssetsAndImports() {
|
||||
(handbookImport.item?.insertedCount || 0) + (handbookImport.item?.updatedCount || 0) + (handbookImport.item?.skippedCount || 0) >= 3,
|
||||
'handbook import should process subject, chapter, and entry idempotently',
|
||||
);
|
||||
const handbookPostCheck = await request('/api/tenant-content/imports/post-check', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { jobId: handbookPreview.job.id },
|
||||
});
|
||||
assert.equal(handbookPostCheck.item?.status, 'passed', 'handbook import post-check should pass');
|
||||
assert.equal(handbookPostCheck.item?.counts?.handbookEntryCount >= 1, true, 'handbook post-check should verify entries');
|
||||
|
||||
const handbookSubjects = await request('/api/catalog/handbook-subjects', {
|
||||
query: { regionId: ids.region },
|
||||
@@ -3220,6 +3288,13 @@ async function testTenantContentAssetsAndImports() {
|
||||
(scorelineImport.item?.insertedCount || 0) + (scorelineImport.item?.updatedCount || 0) + (scorelineImport.item?.skippedCount || 0) >= 4,
|
||||
'scoreline import should process all valid rows',
|
||||
);
|
||||
const scorelinePostCheck = await request('/api/tenant-content/imports/post-check', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { jobId: scorelinePreview.job.id },
|
||||
});
|
||||
assert.equal(scorelinePostCheck.item?.status, 'passed', 'scoreline import post-check should pass');
|
||||
assert.equal(scorelinePostCheck.item?.counts?.scorelineRecordExistingCount, 1, 'scoreline post-check should verify record target');
|
||||
|
||||
const importedScorelineRecords = await request('/api/scoreline/records', {
|
||||
query: { regionId: ids.region, year: 2025, pageSize: 50 },
|
||||
@@ -3374,6 +3449,13 @@ async function testTenantContentAssetsAndImports() {
|
||||
(videoImport.item?.insertedCount || 0) + (videoImport.item?.updatedCount || 0) + (videoImport.item?.skippedCount || 0) >= 2,
|
||||
'video import should process video and question binding',
|
||||
);
|
||||
const videoPostCheck = await request('/api/tenant-content/imports/post-check', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { jobId: videoPreview.job.id },
|
||||
});
|
||||
assert.equal(videoPostCheck.item?.status, 'passed', 'video import post-check should pass');
|
||||
assert.equal(videoPostCheck.item?.counts?.videoBindingCount >= 1, true, 'video post-check should verify question video bindings');
|
||||
|
||||
const questionVideosAfterImport = await request(`/api/questions/${ids.question}/videos`);
|
||||
assert.ok(
|
||||
|
||||
Reference in New Issue
Block a user