forked from wangziqi/gongxue-base
feat: resolve public bank conflicts
This commit is contained in:
@@ -4088,6 +4088,126 @@ async function testPublicQuestionBankAdoption() {
|
||||
const conflictTargetAfter = collectionAfterConflict.items?.find(item => item.id === conflictTarget.id);
|
||||
assert.equal(conflictTargetAfter?.content, tenantCustomContent, 'public bank sync must not overwrite tenant-customized copied question');
|
||||
|
||||
const crossTenantResolveDenied = await request('/api/tenant-content/public-question-banks/conflicts/resolve', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
adoptionId: adopted.item.id,
|
||||
sourceQuestionId: ids.question,
|
||||
resolution: 'accept_platform',
|
||||
},
|
||||
expectStatus: 404,
|
||||
});
|
||||
assert.equal(crossTenantResolveDenied.code, 'QUESTION_BANK_ADOPTION_NOT_FOUND', 'public bank conflict resolution must be tenant isolated');
|
||||
|
||||
const acceptedConflict = await request('/api/tenant-content/public-question-banks/conflicts/resolve', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: PARTNER_TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
adoptionId: adopted.item.id,
|
||||
sourceQuestionId: ids.question,
|
||||
resolution: 'accept_platform',
|
||||
},
|
||||
});
|
||||
assert.equal(acceptedConflict.item?.resolution, 'accept_platform', 'tenant admin should accept platform version for a conflict');
|
||||
assert.equal(acceptedConflict.item?.remainingConflictCount, 0, 'accepting the only conflict should clear conflict count');
|
||||
assert.equal(acceptedConflict.item?.syncStatus, 'synced', 'accepted conflict should mark adoption synced when no conflicts remain');
|
||||
|
||||
const collectionAfterAccept = await request('/api/catalog/question-collections/questions', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: false,
|
||||
query: { collectionId: adopted.item.targetCollectionId, limit: 50 },
|
||||
});
|
||||
const acceptedTarget = collectionAfterAccept.items?.find(item => item.id === conflictTarget.id);
|
||||
assert.equal(acceptedTarget?.content, '平台公共题库更新:1 + 1 = ?', 'accepting platform conflict should update tenant copy to platform content');
|
||||
|
||||
const conflictListAfterAccept = await request('/api/tenant-content/public-question-banks/conflicts', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: PARTNER_TENANT_ADMIN_USER_ID,
|
||||
query: { adoptionId: adopted.item.id },
|
||||
});
|
||||
assert.equal(conflictListAfterAccept.item?.conflictCount, 0, 'resolved conflict list should have no remaining conflicts');
|
||||
|
||||
const keepLocalContent = `租户再次保留本地公共题副本 ${Date.now()}`;
|
||||
await request('/api/tenant-content/questions', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: PARTNER_TENANT_ADMIN_USER_ID,
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
questionId: conflictTarget.id,
|
||||
createVersion: true,
|
||||
content: keepLocalContent,
|
||||
options: acceptedTarget.options || [],
|
||||
correctOptionIndex: acceptedTarget.correctOptionIndex,
|
||||
correctOptionIndices: acceptedTarget.correctOptionIndices || [],
|
||||
answerText: acceptedTarget.answerText,
|
||||
explanation: '租户这一次选择保留本地版本。',
|
||||
sourceHash: `tenant-keep-local-${Date.now()}`,
|
||||
},
|
||||
});
|
||||
|
||||
const platformSecondChangedHash = `platform-source-update-keep-local-${Date.now()}`;
|
||||
await request('/api/tenant-content/questions', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
questionId: ids.question,
|
||||
createVersion: true,
|
||||
content: '平台公共题库二次更新:1 + 2 = ?',
|
||||
options: [
|
||||
{ label: 'A', text: '2' },
|
||||
{ label: 'B', text: '3' },
|
||||
],
|
||||
correctOptionIndex: 1,
|
||||
correctOptionIndices: [1],
|
||||
answerText: '3',
|
||||
explanation: '平台公共题库发布了第二个新版本。',
|
||||
sourceHash: platformSecondChangedHash,
|
||||
},
|
||||
});
|
||||
|
||||
const secondConflictSync = await request('/api/tenant-content/public-question-banks/sync', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: PARTNER_TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { adoptionId: adopted.item.id, copyLimit: 20 },
|
||||
});
|
||||
assert.equal(secondConflictSync.sync?.status, 'conflict', 'second platform update should conflict with tenant-local edit');
|
||||
|
||||
const keptLocalConflict = await request('/api/tenant-content/public-question-banks/conflicts/resolve', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: PARTNER_TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: {
|
||||
adoptionId: adopted.item.id,
|
||||
sourceQuestionId: ids.question,
|
||||
resolution: 'keep_local',
|
||||
},
|
||||
});
|
||||
assert.equal(keptLocalConflict.item?.resolution, 'keep_local', 'tenant admin should keep local version for a conflict');
|
||||
assert.equal(keptLocalConflict.item?.syncStatus, 'synced', 'keeping the only conflict should mark adoption synced');
|
||||
|
||||
const afterKeepLocalSync = await request('/api/tenant-content/public-question-banks/sync', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: PARTNER_TENANT_ADMIN_USER_ID,
|
||||
method: 'POST',
|
||||
body: { adoptionId: adopted.item.id, copyLimit: 20 },
|
||||
});
|
||||
assert.equal(afterKeepLocalSync.sync?.status, 'synced', 'future sync should not repeat a keep-local conflict for the same platform hash');
|
||||
assert.ok(
|
||||
afterKeepLocalSync.sync?.results?.some(item => item.sourceQuestionId === ids.question && item.action === 'skipped' && item.resolution === 'keep_local'),
|
||||
'future sync should expose keep-local skip decision',
|
||||
);
|
||||
|
||||
const collectionAfterKeepLocal = await request('/api/catalog/question-collections/questions', {
|
||||
tenantId: PARTNER_TENANT_ID,
|
||||
userId: false,
|
||||
query: { collectionId: adopted.item.targetCollectionId, limit: 50 },
|
||||
});
|
||||
const keptLocalTarget = collectionAfterKeepLocal.items?.find(item => item.id === conflictTarget.id);
|
||||
assert.equal(keptLocalTarget?.content, keepLocalContent, 'keep-local resolution should preserve tenant customized content');
|
||||
|
||||
const mainTenantNotAdopted = await request('/api/tenant-content/public-question-banks', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
query: { onlyNotAdopted: 'true' },
|
||||
|
||||
Reference in New Issue
Block a user