Enforce exact published form version contracts
Local review candidate only. Explicitly selected inherited prerequisites and prior repair patches; unrelated WIP and independent MQ fixes are excluded. No push or deployment authorized.
This commit is contained in:
@@ -82,6 +82,11 @@ public class OapiFormService {
|
||||
}
|
||||
requireSchId(schId);
|
||||
FormDefService.Published published = defService.resolvePublishedByUuid(schId, uuid, version);
|
||||
// 精确 schema 用于跨端发布绑定,只允许发布过的不可变版本;不能把草稿签成发布契约。
|
||||
// 历史答卷读取仍使用既有 resolver,不在此处收紧其生命周期语义。
|
||||
if (version != null && (version < 1 || published.versionRow().getPublishTime() == null)) {
|
||||
throw new BizException(ErrorCode.PARAM_ERROR, "指定发布版本不存在或不可读");
|
||||
}
|
||||
return toSchemaView(published);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.yunhai.formcenter.service;
|
||||
|
||||
import com.yunhai.formcenter.entity.Form;
|
||||
import com.yunhai.formcenter.entity.FormVersion;
|
||||
import com.yunhai.formcenter.model.FormAccessModels.ConsumerPrincipal;
|
||||
import com.yunhai.formcenter.repository.AnswerRepository;
|
||||
import com.yunhai.formcenter.repository.FormRepository;
|
||||
import com.yunhai.platform.apistd.BizException;
|
||||
import com.yunhai.platform.utils.exception.ErrorCode;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/** 真实服务层发布版本守卫单测;resolver/仓储是桩,不作为真库验收。 */
|
||||
class OapiSchemaPublishedVersionTest {
|
||||
private FormDefService defs;
|
||||
private FormDictService dicts;
|
||||
private FormConsumerAccessService access;
|
||||
private OapiFormService service;
|
||||
private Form form;
|
||||
private FormVersion row;
|
||||
private final ConsumerPrincipal caller = new ConsumerPrincipal("100009");
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
defs = mock(FormDefService.class);
|
||||
dicts = mock(FormDictService.class);
|
||||
access = mock(FormConsumerAccessService.class);
|
||||
service = new OapiFormService(defs, dicts, mock(AnswerRepository.class), mock(FormRepository.class), access);
|
||||
form = new Form();
|
||||
form.setSchId(27L);
|
||||
form.setUuid("FU1");
|
||||
form.setAppCode("svc-hall");
|
||||
form.setCode("leave");
|
||||
row = new FormVersion();
|
||||
row.setVersion(3);
|
||||
row.setSchemaJson("{\"groups\":[]}");
|
||||
when(defs.resolvePublishedByUuid(27L, "FU1", 3)).thenReturn(new FormDefService.Published(form, row));
|
||||
when(dicts.expandSchema(anyLong(), anyString())).thenAnswer(inv -> inv.getArgument(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exactDraftCannotBecomeCrossServicePublishedContract() {
|
||||
BizException ex = assertThrows(BizException.class,
|
||||
() -> service.schema(caller, 27L, null, null, "FU1", 3));
|
||||
assertEquals(ErrorCode.PARAM_ERROR.getCode(), ex.getErrorCode().getCode());
|
||||
verify(access).requireRead(caller, 27L, "FU1");
|
||||
verifyNoInteractions(dicts);
|
||||
}
|
||||
|
||||
@Test
|
||||
void historicalPublishedVersionStillReadableWhenHeadIsDisabledOrDeleted() {
|
||||
row.setPublishTime(LocalDateTime.now().minusDays(1));
|
||||
form.setStatus(2);
|
||||
form.setIsDeleted(1);
|
||||
form.setCurrentVersion(4);
|
||||
assertEquals(3, service.schema(caller, 27L, null, null, "FU1", 3).getVersion());
|
||||
verify(defs).resolvePublishedByUuid(27L, "FU1", 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingVersionRowPreservesResourceRejectionCode() {
|
||||
when(defs.resolvePublishedByUuid(27L, "FU1", 3))
|
||||
.thenThrow(new BizException(ErrorCode.PARAM_ERROR, "opaque"));
|
||||
BizException ex = assertThrows(BizException.class,
|
||||
() -> service.schema(caller, 27L, null, null, "FU1", 3));
|
||||
assertEquals(2, ex.getErrorCode().getCode());
|
||||
verifyNoInteractions(dicts);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readAuthorizationRunsBeforeVersionResolution() {
|
||||
doThrow(new BizException(ErrorCode.PARAM_ERROR, "opaque"))
|
||||
.when(access).requireRead(caller, 27L, "FU1");
|
||||
assertThrows(BizException.class, () -> service.schema(caller, 27L, null, null, "FU1", 3));
|
||||
verifyNoInteractions(defs, dicts);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user