test(pipeline): 添加人脸匹配流水线单元测试

- 为CustomFaceSearchStage添加完整单元测试覆盖各种匹配模式
- 为人脸识别阶段FaceRecognitionStage编写测试用例
- 为上下文准备阶段PrepareContextStage增加测试验证
- 包含成功、失败、异常等边界情况测试
- 验证不同匹配模式下的结果合并逻辑
- 测试人工选择和自动匹配场景的处理差异
This commit is contained in:
2025-12-03 18:27:53 +08:00
parent b3fa10e8fd
commit 71d6400a1e
4 changed files with 684 additions and 0 deletions

View File

@@ -67,6 +67,12 @@ public class FilterByDevicePhotoLimitStage extends AbstractFaceMatchingStage<Fac
List<Long> sampleListIds = context.getSampleListIds();
Long faceId = context.getFaceId();
// 防御性检查:faceSamples为空
if (faceSamples == null || faceSamples.isEmpty()) {
log.debug("faceSamples为空,跳过设备照片限制筛选,faceId={}", faceId);
return StageResult.skipped("faceSamples为空");
}
try {
// 1. 构建样本ID到实体的映射
Map<Long, FaceSampleEntity> sampleMap = faceSamples.stream()

View File

@@ -0,0 +1,299 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.service.pc.helper.SearchResultMerger;
import com.ycwl.basic.service.task.TaskFaceService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
/**
* CustomFaceSearchStage 单元测试
*/
@ExtendWith(MockitoExtension.class)
class CustomFaceSearchStageTest {
@Mock
private TaskFaceService taskFaceService;
@Mock
private SearchResultMerger resultMerger;
@Mock
private IFaceBodyAdapter faceBodyAdapter;
@InjectMocks
private CustomFaceSearchStage stage;
private FaceMatchingContext context;
private FaceEntity face;
@BeforeEach
void setUp() {
context = FaceMatchingContext.forCustomMatching(1L, Arrays.asList(101L, 102L));
face = new FaceEntity();
face.setId(1L);
face.setScenicId(10L);
face.setFaceUrl("http://example.com/face.jpg");
context.setFace(face);
context.setFaceBodyAdapter(faceBodyAdapter);
}
@Test
void testExecute_Mode2_DirectUse() {
// Given: 模式2,直接使用用户选择的样本
context.setFaceSelectPostMode(2);
context.setFaceSampleIds(Arrays.asList(101L, 102L, 103L));
SearchFaceRespVo directResult = new SearchFaceRespVo();
directResult.setSampleListIds(Arrays.asList(101L, 102L, 103L));
when(resultMerger.createDirectResult(Arrays.asList(101L, 102L, 103L)))
.thenReturn(directResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(directResult, context.getSearchResult());
assertEquals(3, context.getSampleListIds().size());
verify(resultMerger, times(1)).createDirectResult(anyList());
verify(taskFaceService, never()).searchFace(any(), anyString(), anyString(), anyString());
}
@Test
void testExecute_Mode2_WithMatchResult() {
// Given: 模式2,保留原始matchResult
context.setFaceSelectPostMode(2);
context.setFaceSampleIds(Arrays.asList(101L, 102L));
face.setMatchResult("{\"score\":0.85}");
SearchFaceRespVo directResult = new SearchFaceRespVo();
directResult.setSampleListIds(Arrays.asList(101L, 102L));
when(resultMerger.createDirectResult(anyList())).thenReturn(directResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals("{\"score\":0.85}", directResult.getSearchResultJson());
}
@Test
void testExecute_Mode0_Union() throws Exception {
// Given: 模式0(并集),搜索多个样本并合并
context.setFaceSelectPostMode(0);
FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
context.setFaceSamples(Arrays.asList(sample1, sample2));
context.setFaceSampleIds(Arrays.asList(101L, 102L));
SearchFaceRespVo searchResult1 = new SearchFaceRespVo();
searchResult1.setSampleListIds(Arrays.asList(201L, 202L));
SearchFaceRespVo searchResult2 = new SearchFaceRespVo();
searchResult2.setSampleListIds(Arrays.asList(202L, 203L));
when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s1.jpg"), anyString()))
.thenReturn(searchResult1);
when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s2.jpg"), anyString()))
.thenReturn(searchResult2);
SearchFaceRespVo mergedResult = new SearchFaceRespVo();
mergedResult.setSampleListIds(Arrays.asList(201L, 202L, 203L)); // 并集
when(resultMerger.merge(anyList(), eq(0))).thenReturn(mergedResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(mergedResult, context.getSearchResult());
assertEquals(3, context.getSampleListIds().size());
verify(taskFaceService, times(2)).searchFace(any(), anyString(), anyString(), anyString());
verify(resultMerger, times(1)).merge(anyList(), eq(0));
}
@Test
void testExecute_Mode1_Intersection() throws Exception {
// Given: 模式1(交集)
context.setFaceSelectPostMode(1);
FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
context.setFaceSamples(Arrays.asList(sample1, sample2));
context.setFaceSampleIds(Arrays.asList(101L, 102L));
SearchFaceRespVo searchResult1 = new SearchFaceRespVo();
searchResult1.setSampleListIds(Arrays.asList(201L, 202L, 203L));
SearchFaceRespVo searchResult2 = new SearchFaceRespVo();
searchResult2.setSampleListIds(Arrays.asList(202L, 203L, 204L));
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(searchResult1, searchResult2);
SearchFaceRespVo mergedResult = new SearchFaceRespVo();
mergedResult.setSampleListIds(Arrays.asList(202L, 203L)); // 交集
when(resultMerger.merge(anyList(), eq(1))).thenReturn(mergedResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(2, context.getSampleListIds().size());
verify(resultMerger, times(1)).merge(anyList(), eq(1));
}
@Test
void testExecute_DefaultMode() throws Exception {
// Given: 未设置faceSelectPostMode,默认为0
context.setFaceSelectPostMode(null);
FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
context.setFaceSamples(Arrays.asList(sample1));
context.setFaceSampleIds(Arrays.asList(101L));
SearchFaceRespVo searchResult = new SearchFaceRespVo();
searchResult.setSampleListIds(Arrays.asList(201L));
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(searchResult);
SearchFaceRespVo mergedResult = new SearchFaceRespVo();
mergedResult.setSampleListIds(Arrays.asList(201L));
when(resultMerger.merge(anyList(), eq(0))).thenReturn(mergedResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
verify(resultMerger, times(1)).merge(anyList(), eq(0)); // 默认模式0
}
@Test
void testExecute_PartialSearchFailure_Success() throws Exception {
// Given: 部分样本搜索失败,但至少有一个成功
context.setFaceSelectPostMode(0);
FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
FaceSampleEntity sample3 = createSample(103L, "http://example.com/s3.jpg");
context.setFaceSamples(Arrays.asList(sample1, sample2, sample3));
SearchFaceRespVo searchResult1 = new SearchFaceRespVo();
searchResult1.setSampleListIds(Arrays.asList(201L));
when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s1.jpg"), anyString()))
.thenReturn(searchResult1);
when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s2.jpg"), anyString()))
.thenThrow(new RuntimeException("Network error"));
when(taskFaceService.searchFace(eq(faceBodyAdapter), eq("10"), eq("http://example.com/s3.jpg"), anyString()))
.thenReturn(null);
SearchFaceRespVo mergedResult = new SearchFaceRespVo();
mergedResult.setSampleListIds(Arrays.asList(201L));
when(resultMerger.merge(anyList(), eq(0))).thenReturn(mergedResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
verify(taskFaceService, times(3)).searchFace(any(), anyString(), anyString(), anyString());
}
@Test
void testExecute_AllSearchFailure_ThrowException() throws Exception {
// Given: 所有样本搜索都失败
context.setFaceSelectPostMode(0);
FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
FaceSampleEntity sample2 = createSample(102L, "http://example.com/s2.jpg");
context.setFaceSamples(Arrays.asList(sample1, sample2));
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenThrow(new RuntimeException("Network error"));
// When & Then
assertThrows(BaseException.class, () -> {
stage.execute(context);
});
}
@Test
void testExecute_BaseException_Rethrow() throws Exception {
// Given
context.setFaceSelectPostMode(0);
FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
context.setFaceSamples(Arrays.asList(sample1));
SearchFaceRespVo searchResult = new SearchFaceRespVo();
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(searchResult);
BaseException baseException = new BaseException("服务不可用");
when(resultMerger.merge(anyList(), anyInt())).thenThrow(baseException);
// When & Then
assertThrows(BaseException.class, () -> {
stage.execute(context);
});
}
@Test
void testExecute_GenericException_Failed() throws Exception {
// Given
context.setFaceSelectPostMode(0);
FaceSampleEntity sample1 = createSample(101L, "http://example.com/s1.jpg");
context.setFaceSamples(Arrays.asList(sample1));
SearchFaceRespVo searchResult = new SearchFaceRespVo();
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(searchResult);
when(resultMerger.merge(anyList(), anyInt())).thenThrow(new RuntimeException("Merge error"));
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
assertTrue(result.getMessage().contains("自定义人脸搜索失败"));
}
private FaceSampleEntity createSample(Long id, String faceUrl) {
FaceSampleEntity sample = new FaceSampleEntity();
sample.setId(id);
sample.setFaceUrl(faceUrl);
return sample;
}
}

View File

@@ -0,0 +1,191 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.exception.BaseException;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.model.task.resp.SearchFaceRespVo;
import com.ycwl.basic.service.task.TaskFaceService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
/**
* FaceRecognitionStage 单元测试
*/
@ExtendWith(MockitoExtension.class)
class FaceRecognitionStageTest {
@Mock
private TaskFaceService taskFaceService;
@Mock
private IFaceBodyAdapter faceBodyAdapter;
@InjectMocks
private FaceRecognitionStage stage;
private FaceMatchingContext context;
private FaceEntity face;
@BeforeEach
void setUp() {
context = FaceMatchingContext.forAutoMatching(1L, true);
face = new FaceEntity();
face.setId(1L);
face.setScenicId(10L);
face.setFaceUrl("http://example.com/face.jpg");
context.setFace(face);
context.setFaceBodyAdapter(faceBodyAdapter);
}
@Test
void testExecute_Success() throws Exception {
// Given
SearchFaceRespVo searchResult = new SearchFaceRespVo();
searchResult.setScore(0.85f);
searchResult.setSampleListIds(Arrays.asList(101L, 102L, 103L));
when(taskFaceService.searchFace(
eq(faceBodyAdapter),
eq("10"),
eq("http://example.com/face.jpg"),
eq("人脸识别")
)).thenReturn(searchResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(searchResult, context.getSearchResult());
assertEquals(0.85f, context.getSearchResult().getScore(), 0.0001);
assertEquals(3, context.getSearchResult().getSampleListIds().size());
verify(taskFaceService, times(1)).searchFace(any(), anyString(), anyString(), anyString());
}
@Test
void testExecute_SearchResultNull_Failed() throws Exception {
// Given
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(null);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
assertTrue(result.getMessage().contains("人脸识别失败"));
assertNull(context.getSearchResult());
}
@Test
void testExecute_SearchResultEmptyList() throws Exception {
// Given
SearchFaceRespVo searchResult = new SearchFaceRespVo();
searchResult.setScore(0.45f); // 低分
searchResult.setSampleListIds(Arrays.asList()); // 空列表
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(searchResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess()); // 即使没有匹配也成功
assertEquals(searchResult, context.getSearchResult());
assertEquals(0, context.getSearchResult().getSampleListIds().size());
}
@Test
void testExecute_BaseException_Throw() throws Exception {
// Given
BaseException baseException = new BaseException("识别服务异常");
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenThrow(baseException);
// When & Then
assertThrows(BaseException.class, () -> {
stage.execute(context);
});
}
@Test
void testExecute_GenericException_Failed() throws Exception {
// Given
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenThrow(new RuntimeException("Network error"));
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
assertTrue(result.getMessage().contains("人脸识别失败"));
assertNotNull(result.getException());
}
@Test
void testExecute_NullFaceUrl() throws Exception {
// Given
face.setFaceUrl(null);
when(taskFaceService.searchFace(any(), anyString(), isNull(), anyString()))
.thenThrow(new IllegalArgumentException("Face URL is null"));
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
}
@Test
void testExecute_HighScoreResult() throws Exception {
// Given: 高分匹配
SearchFaceRespVo searchResult = new SearchFaceRespVo();
searchResult.setScore(0.95f);
searchResult.setSampleListIds(Arrays.asList(101L));
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(searchResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(0.95f, context.getSearchResult().getScore(), 0.0001);
}
@Test
void testExecute_MultipleMatches() throws Exception {
// Given: 多个匹配结果
SearchFaceRespVo searchResult = new SearchFaceRespVo();
searchResult.setScore(0.78f);
searchResult.setSampleListIds(Arrays.asList(101L, 102L, 103L, 104L, 105L));
when(taskFaceService.searchFace(any(), anyString(), anyString(), anyString()))
.thenReturn(searchResult);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(5, context.getSearchResult().getSampleListIds().size());
assertTrue(result.getMessage().contains("匹配数=5"));
}
}

View File

@@ -0,0 +1,188 @@
package com.ycwl.basic.face.pipeline.stages;
import com.ycwl.basic.face.pipeline.core.FaceMatchingContext;
import com.ycwl.basic.face.pipeline.core.StageResult;
import com.ycwl.basic.face.pipeline.enums.FaceMatchingScene;
import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter;
import com.ycwl.basic.integration.common.manager.ScenicConfigManager;
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
import com.ycwl.basic.repository.FaceRepository;
import com.ycwl.basic.repository.ScenicRepository;
import com.ycwl.basic.service.pc.ScenicService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* PrepareContextStage 单元测试
*/
@ExtendWith(MockitoExtension.class)
class PrepareContextStageTest {
@Mock
private FaceRepository faceRepository;
@Mock
private ScenicRepository scenicRepository;
@Mock
private ScenicService scenicService;
@Mock
private ScenicConfigManager scenicConfig;
@Mock
private IFaceBodyAdapter faceBodyAdapter;
@InjectMocks
private PrepareContextStage stage;
private FaceMatchingContext context;
@BeforeEach
void setUp() {
context = FaceMatchingContext.forAutoMatching(1L, true);
}
@Test
void testExecute_Success() {
// Given
FaceEntity face = createFace(1L, 100L, 10L, 0); // isManual=0
when(faceRepository.getFace(1L)).thenReturn(face);
when(scenicRepository.getScenicConfigManager(10L)).thenReturn(scenicConfig);
when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(face, context.getFace());
assertEquals(scenicConfig, context.getScenicConfig());
assertEquals(faceBodyAdapter, context.getFaceBodyAdapter());
verify(faceRepository, times(1)).getFace(1L);
verify(scenicRepository, times(1)).getScenicConfigManager(10L);
verify(scenicService, times(1)).getScenicFaceBodyAdapter(10L);
}
@Test
void testExecute_FaceNotFound_Failed() {
// Given
when(faceRepository.getFace(1L)).thenReturn(null);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
assertTrue(result.getMessage().contains("人脸不存在"));
assertNull(context.getFace());
}
@Test
void testExecute_ManualFaceAndNotNew_Skipped() {
// Given: isManual=1, isNew=false
context = FaceMatchingContext.forAutoMatching(1L, false);
FaceEntity face = createFace(1L, 100L, 10L, 1); // isManual=1
when(faceRepository.getFace(1L)).thenReturn(face);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSkipped());
assertTrue(result.getMessage().contains("人工选择的人脸"));
assertEquals(face, context.getFace());
// 人工选择的人脸跳过,不加载景区配置
verify(scenicRepository, never()).getScenicConfigManager(anyLong());
verify(scenicService, never()).getScenicFaceBodyAdapter(anyLong());
}
@Test
void testExecute_ManualFaceButIsNew_Success() {
// Given: isManual=1, isNew=true (新用户即使人工选择也需要匹配)
context = FaceMatchingContext.forAutoMatching(1L, true);
FaceEntity face = createFace(1L, 100L, 10L, 1); // isManual=1
when(faceRepository.getFace(1L)).thenReturn(face);
when(scenicRepository.getScenicConfigManager(10L)).thenReturn(scenicConfig);
when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(face, context.getFace());
assertEquals(scenicConfig, context.getScenicConfig());
assertEquals(faceBodyAdapter, context.getFaceBodyAdapter());
}
@Test
void testExecute_FaceBodyAdapterNotAvailable_Failed() {
// Given
FaceEntity face = createFace(1L, 100L, 10L, 0);
when(faceRepository.getFace(1L)).thenReturn(face);
when(scenicRepository.getScenicConfigManager(10L)).thenReturn(scenicConfig);
when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(null); // 无适配器
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isFailed());
assertTrue(result.getMessage().contains("人脸识别服务不可用"));
assertEquals(face, context.getFace());
assertEquals(scenicConfig, context.getScenicConfig());
assertNull(context.getFaceBodyAdapter());
}
@Test
void testExecute_AutoMatchingOldUser() {
// Given
context = FaceMatchingContext.forAutoMatching(1L, false); // 老用户
FaceEntity face = createFace(1L, 100L, 10L, 0);
when(faceRepository.getFace(1L)).thenReturn(face);
when(scenicRepository.getScenicConfigManager(10L)).thenReturn(scenicConfig);
when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertFalse(context.isNew());
}
@Test
void testExecute_RecognitionOnlyScene() {
// Given
context = FaceMatchingContext.forRecognitionOnly(1L);
FaceEntity face = createFace(1L, 100L, 10L, 0);
when(faceRepository.getFace(1L)).thenReturn(face);
when(scenicRepository.getScenicConfigManager(10L)).thenReturn(scenicConfig);
when(scenicService.getScenicFaceBodyAdapter(10L)).thenReturn(faceBodyAdapter);
// When
StageResult<FaceMatchingContext> result = stage.execute(context);
// Then
assertTrue(result.isSuccess());
assertEquals(FaceMatchingScene.RECOGNITION_ONLY, context.getScene());
}
private FaceEntity createFace(Long id, Long memberId, Long scenicId, Integer isManual) {
FaceEntity face = new FaceEntity();
face.setId(id);
face.setMemberId(memberId);
face.setScenicId(scenicId);
face.setIsManual(isManual);
face.setFaceUrl("http://example.com/face.jpg");
return face;
}
}