Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.util.List;

public interface DocumentRepository {
void save(DocumentEntity entity);
DocumentEntity save(DocumentEntity entity);

DocumentEntity getByIdAndStaffId(Long id, Long staffId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,20 @@ public List<ContractEntity> findAllByStoreId(final Long storeId) {
return contractRepository.findAllByStoreId(storeId);
}

private byte[] generateContractPdf(final ContractData contractData, final String bossSignatureKey) {
public byte[] generateContractPdf(final ContractData contractData, final String bossSignatureKey) {
final String bossSignatureBase64 = fetchSignatureBase64(bossSignatureKey);
final String html = contractHtmlGenerator.generateHtmlWithBossSignature(contractData, bossSignatureBase64);
return pdfGenerator.generatePdfFromHtml(html);
}

private byte[] generateStaffSignedContractPdf(final ContractData data, final String bossSignatureKey, final String staffSignatureKey) {
public byte[] generateStaffSignedContractPdf(final ContractData data, final String bossSignatureKey, final String staffSignatureKey) {
final String bossSignatureBase64 = fetchSignatureBase64(bossSignatureKey);
final String staffSignatureBase64 = fetchSignatureBase64(staffSignatureKey);
final String html = contractHtmlGenerator.generateHtmlWithStaffSignature(data, bossSignatureBase64, staffSignatureBase64);
return pdfGenerator.generatePdfFromHtml(html);
}

private String fetchSignatureBase64(final String signatureKey) {
public String fetchSignatureBase64(final String signatureKey) {
return s3FileManager.fetchAsBase64(signatureKey, ContentType.PNG.getMimeType());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public class DocumentRepositoryImpl implements DocumentRepository {
private final DocumentJpaRepository documentJpaRepository;

@Override
public void save(DocumentEntity entity) {
documentJpaRepository.save(entity);
public DocumentEntity save(DocumentEntity entity) {
return documentJpaRepository.save(entity);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.mangoboss.app.domain.service.attendance.context;

import com.mangoboss.app.common.exception.CustomErrorInfo;
import com.mangoboss.app.common.exception.CustomException;
import com.mangoboss.app.domain.service.attendance.strategy.AttendanceValidationStrategy;
import com.mangoboss.app.dto.attendance.base.AttendanceBaseRequest;
import com.mangoboss.storage.store.AttendanceMethod;
import com.mangoboss.storage.store.StoreEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.*;

class AttendanceStrategyContextTest {

private AttendanceStrategyContext context;
private AttendanceValidationStrategy<TestRequest> mockStrategy;

@BeforeEach
void setUp() {
mockStrategy = mock(AttendanceValidationStrategy.class);
context = new AttendanceStrategyContext(List.of(mockStrategy));
}

@Test
void 전략이_정상적으로_선택되면_검증이_수행된다() {
// given
StoreEntity store = mock(StoreEntity.class);
TestRequest request = new TestRequest();

when(mockStrategy.supports(store, request.attendanceMethod())).thenReturn(true);
when(mockStrategy.getRequestType()).thenReturn(TestRequest.class);

// when
context.validate(store, request);

// then
verify(mockStrategy).executionValidate(store, request);
}

@Test
void 지원하는_전략이_없으면_예외가_발생한다() {
// given
StoreEntity store = mock(StoreEntity.class);
TestRequest request = new TestRequest();

when(mockStrategy.supports(store, request.attendanceMethod())).thenReturn(false);
when(mockStrategy.getRequestType()).thenReturn(TestRequest.class);

// when & then
assertThatThrownBy(() -> context.validate(store, request))
.isInstanceOf(CustomException.class)
.satisfies(e -> assertThat(((CustomException) e).getErrorCode())
.isEqualTo(CustomErrorInfo.INVALID_ATTENDANCE_REQUEST_TYPE));

verify(mockStrategy, never()).executionValidate(any(), any());
}

@Test
void request_타입이_전략에서_요구하는_타입과_다르면_예외가_발생한다() {
// given
StoreEntity store = mock(StoreEntity.class);
WrongRequest request = new WrongRequest();

when(mockStrategy.supports(store, request.attendanceMethod())).thenReturn(true);
when(mockStrategy.getRequestType()).thenReturn(TestRequest.class); // 타입 불일치

// when & then
assertThatThrownBy(() -> context.validate(store, request))
.isInstanceOf(CustomException.class)
.satisfies(e -> assertThat(((CustomException) e).getErrorCode())
.isEqualTo(CustomErrorInfo.INVALID_ATTENDANCE_REQUEST_TYPE));

verify(mockStrategy, never()).executionValidate(any(), any());
}

// 테스트용 request 구현체들
private static class TestRequest implements AttendanceBaseRequest {
@Override
public AttendanceMethod attendanceMethod() {
return AttendanceMethod.GPS;
}

@Override
public Long scheduleId() {
return 1L;
}
}

private static class WrongRequest implements AttendanceBaseRequest {
@Override
public AttendanceMethod attendanceMethod() {
return AttendanceMethod.GPS;
}

@Override
public Long scheduleId() {
return 2L;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.mangoboss.app.domain.service.attendance.strategy;

import com.mangoboss.app.common.exception.CustomErrorInfo;
import com.mangoboss.app.common.exception.CustomException;
import com.mangoboss.app.dto.attendance.request.AttendanceBothRequest;
import com.mangoboss.storage.store.AttendanceMethod;
import com.mangoboss.storage.store.StoreEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;

import static org.junit.jupiter.api.Assertions.*;

class BothValidationStrategyTest {

private BothValidationStrategy bothValidationStrategy;
private StoreEntity store;

@BeforeEach
void setUp() {
bothValidationStrategy = new BothValidationStrategy();
store = StoreEntity.builder()
.qrCode("VALID_QR")
.gpsLatitude(37.123)
.gpsLongitude(127.123)
.gpsRangeMeters(100)
.attendanceMethod(AttendanceMethod.BOTH)
.build();
}

@Test
void 유효한_요청이면_검증을_통과한다() {
var request = new AttendanceBothRequest(AttendanceMethod.BOTH, 1L, "VALID_QR", 37.123, 127.123, LocalDateTime.now());
assertDoesNotThrow(() -> bothValidationStrategy.executionValidate(store, request));
}

@Test
void 잘못된_qr이면_검증에서_예외가_발생한다() {
var request = new AttendanceBothRequest(AttendanceMethod.BOTH, 1L, "WRONG_QR", 37.123, 127.123, LocalDateTime.now());
var ex = assertThrows(CustomException.class, () -> bothValidationStrategy.executionValidate(store, request));
assertEquals(CustomErrorInfo.INVALID_QR_CODE, ex.getErrorCode());
}

@Test
void gps_범위를_벗어나면_예외가_발생한다() {
var request = new AttendanceBothRequest(AttendanceMethod.BOTH, 1L, "VALID_QR", 36.0, 126.0, LocalDateTime.now());
var ex = assertThrows(CustomException.class, () -> bothValidationStrategy.executionValidate(store, request));
assertEquals(CustomErrorInfo.GPS_OUT_OF_RANGE, ex.getErrorCode());
}

@Test
void gps_시간차이가_10초를_초과하면_예외가_발생한다() {
var request = new AttendanceBothRequest(AttendanceMethod.BOTH, 1L, "VALID_QR", 37.123, 127.123, LocalDateTime.now().minusSeconds(20));
var ex = assertThrows(CustomException.class, () -> bothValidationStrategy.executionValidate(store, request));
assertEquals(CustomErrorInfo.INVALID_GPS_TIME, ex.getErrorCode());
}

@Test
void 지원하는_가게_설정과_출결방식이면_true를_반환한다() {
assertTrue(bothValidationStrategy.supports(store, AttendanceMethod.BOTH));
}

@Test
void 출결방식이나_가게설정이_다르면_false를_반환한다() {
assertFalse(bothValidationStrategy.supports(store, AttendanceMethod.QR));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.mangoboss.app.domain.service.attendance.strategy;

import com.mangoboss.app.common.exception.CustomErrorInfo;
import com.mangoboss.app.common.exception.CustomException;
import com.mangoboss.app.dto.attendance.base.AttendanceBaseRequest;
import com.mangoboss.app.dto.attendance.request.AttendanceGpsRequest;
import com.mangoboss.storage.store.AttendanceMethod;
import com.mangoboss.storage.store.StoreEntity;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.LocalDateTime;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
class GpsValidationStrategyTest {

private final GpsValidationStrategy gpsValidationStrategy = new GpsValidationStrategy();

private final StoreEntity store = StoreEntity.builder()
.attendanceMethod(AttendanceMethod.GPS)
.gpsLatitude(37.1234)
.gpsLongitude(127.1234)
.gpsRangeMeters(100)
.qrCode("QR")
.build();

@Test
void gps_범위_내_정상시간이면_예외_없음() {
AttendanceGpsRequest request = new AttendanceGpsRequest(
AttendanceMethod.GPS,
1L,
37.1234, 127.1234,
LocalDateTime.now()
);

assertDoesNotThrow(() -> gpsValidationStrategy.executionValidate(store, request));
}

@Test
void gps_범위_초과시_예외() {
AttendanceGpsRequest request = new AttendanceGpsRequest(
AttendanceMethod.GPS,
1L,
36.0, 126.0, // 거리 매우 멀 때
LocalDateTime.now()
);

CustomException ex = assertThrows(CustomException.class, () ->
gpsValidationStrategy.executionValidate(store, request));

assertEquals(CustomErrorInfo.GPS_OUT_OF_RANGE, ex.getCustomErrorInfo());
}

@Test
void gps_시간_지연되면_예외() {
AttendanceGpsRequest request = new AttendanceGpsRequest(
AttendanceMethod.GPS,
1L,
37.1234, 127.1234,
LocalDateTime.now().minusSeconds(20)
);

CustomException ex = assertThrows(CustomException.class, () ->
gpsValidationStrategy.executionValidate(store, request));

assertEquals(CustomErrorInfo.INVALID_GPS_TIME, ex.getCustomErrorInfo());
}

@Test
void 지원하지않는_request_type이면_예외발생() {
AttendanceBaseRequest wrongRequest = new AttendanceBaseRequest() {
@Override
public AttendanceMethod attendanceMethod() {
return AttendanceMethod.GPS;
}

@Override
public Long scheduleId() {
return 1L;
}
};

CustomException ex = assertThrows(CustomException.class,
() -> gpsValidationStrategy.executionValidate(store, wrongRequest));

assertEquals(CustomErrorInfo.INVALID_ATTENDANCE_REQUEST_TYPE, ex.getCustomErrorInfo());
}

@Test
void gps방식과_설정이_일치하면_supports는_true를_반환한다() {
assertTrue(gpsValidationStrategy.supports(store, AttendanceMethod.GPS));
}

@Test
void gps방식과_설정이_일치하지_않으면_supports는_false를_반환한다() {
assertFalse(gpsValidationStrategy.supports(store, AttendanceMethod.QR));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.mangoboss.app.domain.service.attendance.strategy;

import com.mangoboss.app.common.exception.CustomErrorInfo;
import com.mangoboss.app.common.exception.CustomException;
import com.mangoboss.app.dto.attendance.request.AttendanceQrRequest;
import com.mangoboss.storage.store.AttendanceMethod;
import com.mangoboss.storage.store.StoreEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class QrValidationStrategyTest {

private QrValidationStrategy qrValidationStrategy;
private StoreEntity store;

@BeforeEach
void setUp() {
qrValidationStrategy = new QrValidationStrategy();
store = StoreEntity.builder()
.qrCode("VALID_QR")
.attendanceMethod(AttendanceMethod.QR)
.build();
}

@Test
void 올바른_qr이면_통과() {
var request = new AttendanceQrRequest(AttendanceMethod.QR, 1L, "VALID_QR");

assertDoesNotThrow(() -> qrValidationStrategy.executionValidate(store, request));
}

@Test
void 잘못된_qr이면_예외() {
var request = new AttendanceQrRequest(AttendanceMethod.QR, 1L, "WRONG_QR");

CustomException ex = assertThrows(CustomException.class, () ->
qrValidationStrategy.executionValidate(store, request)
);

assertEquals(CustomErrorInfo.INVALID_QR_CODE, ex.getErrorCode());
}

@Test
void qr방식과_설정이_일치하면_supports는_true를_반환한다() {
assertTrue(qrValidationStrategy.supports(store, AttendanceMethod.QR));
}

@Test
void qr방식과_설정이_일치하지_않으면_supports는_false를_반환한다() {
assertFalse(qrValidationStrategy.supports(store, AttendanceMethod.GPS));
}
}
Loading