ValidationExceptionHandler.java

1
package com.project.auth.presentation.support.exception;
2
3
import com.project.auth.presentation.support.response.ApiResult;
4
import com.project.auth.presentation.support.response.ApiResultFactory;
5
import jakarta.validation.ConstraintViolation;
6
import jakarta.validation.ConstraintViolationException;
7
import jakarta.validation.Path;
8
import org.slf4j.Logger;
9
import org.slf4j.LoggerFactory;
10
import org.springframework.core.Ordered;
11
import org.springframework.core.annotation.Order;
12
import org.springframework.context.MessageSourceResolvable;
13
import org.springframework.http.ResponseEntity;
14
import org.springframework.validation.FieldError;
15
import org.springframework.validation.ObjectError;
16
import org.springframework.web.bind.MethodArgumentNotValidException;
17
import org.springframework.web.bind.annotation.ExceptionHandler;
18
import org.springframework.web.bind.annotation.RestControllerAdvice;
19
import org.springframework.web.method.annotation.HandlerMethodValidationException;
20
21
import java.util.ArrayList;
22
import java.util.LinkedHashMap;
23
import java.util.List;
24
import java.util.Map;
25
26
@RestControllerAdvice
27
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
28
public class ValidationExceptionHandler {
29
30
    /**
31
     * 필드와 연결되지 않은 클래스 레벨/cross-field 검증 실패(예: DTO의 {@code @AssertTrue},
32
     * 커스텀 클래스 레벨 {@code ConstraintValidator})를 담는 버킷 키.
33
     *
34
     * 이 버킷이 없으면 ObjectError가 조용히 폐기되어 클라이언트는 {@code errors: {}}만 받고
35
     * 어디가 잘못됐는지 알 수 없다.
36
     */
37
    public static final String GLOBAL_ERROR_KEY = "__global__";
38
39
    /**
40
     * {@code getDefaultMessage()}와 {@code error.code()}가 모두 null/공백인 경우의 폴백 메시지.
41
     *
42
     * Bean Validation은 MessageSource로 해석되는 메시지 코드만 정의된 케이스를 허용하며,
43
     * 이때 getDefaultMessage()는 null을 반환할 수 있다. 이 sentinel이 없으면 응답 errors에
44
     * {@code [null]} 항목이 그대로 들어간다.
45
     */
46
    static final String UNRESOLVED_VIOLATION_MESSAGE = "validation failed";
47
48
    private static final Logger log = LoggerFactory.getLogger(ValidationExceptionHandler.class);
49
50
    private final ApiResultFactory apiResultFactory;
51
52
    public ValidationExceptionHandler(ApiResultFactory apiResultFactory) {
53
        this.apiResultFactory = apiResultFactory;
54
    }
55
56
    @ExceptionHandler(MethodArgumentNotValidException.class)
57
    public ResponseEntity<ApiResult<Void>> handleValidationException(
58
            MethodArgumentNotValidException exception
59
    ) {
60
        Map<String, List<String>> errors = new LinkedHashMap<>();
61
        for (FieldError fieldError : exception.getBindingResult().getFieldErrors()) {
62 1 1. lambda$handleValidationException$0 : replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleValidationException$0 → NO_COVERAGE
            errors.computeIfAbsent(fieldFieldToJsonPointer(fieldError.getField()), k -> new ArrayList<>())
63
                    .add(resolveMessage(fieldError.getDefaultMessage(), fieldError.getCode()));
64
        }
65
        for (ObjectError globalError : exception.getBindingResult().getGlobalErrors()) {
66 1 1. lambda$handleValidationException$1 : replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleValidationException$1 → NO_COVERAGE
            errors.computeIfAbsent(GLOBAL_ERROR_KEY, k -> new ArrayList<>())
67
                    .add(resolveMessage(globalError.getDefaultMessage(), globalError.getCode()));
68
        }
69
70
        log.warn("Validation failed: {}", errors);
71
72 1 1. handleValidationException : replaced return value with null for com/project/auth/presentation/support/exception/ValidationExceptionHandler::handleValidationException → NO_COVERAGE
        return ResponseEntity.status(ApiErrorHttpStatusMapper.map(PresentationErrorCode.INVALID_INPUT))
73
                .body(apiResultFactory.failure(
74
                        PresentationErrorCode.INVALID_INPUT.code(),
75
                        PresentationErrorCode.INVALID_INPUT.message(),
76
                        errors
77
                ));
78
    }
79
80
    @ExceptionHandler(ConstraintViolationException.class)
81
    public ResponseEntity<ApiResult<Void>> handleConstraintViolationException(
82
            ConstraintViolationException exception
83
    ) {
84
        Map<String, List<String>> errors = new LinkedHashMap<>();
85
        for (ConstraintViolation<?> violation : exception.getConstraintViolations()) {
86
            String pointer = toJsonPointer(violation.getPropertyPath());
87 1 1. lambda$handleConstraintViolationException$2 : replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleConstraintViolationException$2 → NO_COVERAGE
            errors.computeIfAbsent(pointer, k -> new ArrayList<>())
88
                    .add(resolveMessage(violation.getMessage(), null));
89
        }
90
91
        log.warn("Constraint violation: {}", errors);
92
93 1 1. handleConstraintViolationException : replaced return value with null for com/project/auth/presentation/support/exception/ValidationExceptionHandler::handleConstraintViolationException → NO_COVERAGE
        return ResponseEntity.status(ApiErrorHttpStatusMapper.map(PresentationErrorCode.CONSTRAINT_VIOLATION))
94
                .body(apiResultFactory.failure(
95
                        PresentationErrorCode.CONSTRAINT_VIOLATION.code(),
96
                        PresentationErrorCode.CONSTRAINT_VIOLATION.message(),
97
                        errors
98
                ));
99
    }
100
101
    @ExceptionHandler(HandlerMethodValidationException.class)
102
    public ResponseEntity<ApiResult<Void>> handleHandlerMethodValidationException(
103
            HandlerMethodValidationException exception
104
    ) {
105
        Map<String, List<String>> errors = new LinkedHashMap<>();
106
        int unnamedCounter = 0;
107
        for (var result : exception.getValueResults()) {
108
            String paramName = result.getMethodParameter().getParameterName();
109
            String key;
110 1 1. handleHandlerMethodValidationException : negated conditional → NO_COVERAGE
            if (paramName != null) {
111
                key = fieldFieldToJsonPointer(paramName);
112
            } else {
113
                key = fieldFieldToJsonPointer("unknown_" + result.getMethodParameter().getParameterIndex());
114 1 1. handleHandlerMethodValidationException : Changed increment from 1 to -1 → NO_COVERAGE
                unnamedCounter++;
115
            }
116
            for (MessageSourceResolvable error : result.getResolvableErrors()) {
117
                String[] codes = error.getCodes();
118 3 1. handleHandlerMethodValidationException : negated conditional → NO_COVERAGE
2. handleHandlerMethodValidationException : changed conditional boundary → NO_COVERAGE
3. handleHandlerMethodValidationException : negated conditional → NO_COVERAGE
                String firstCode = codes != null && codes.length > 0 ? codes[0] : null;
119 1 1. lambda$handleHandlerMethodValidationException$3 : replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleHandlerMethodValidationException$3 → NO_COVERAGE
                errors.computeIfAbsent(key, k -> new ArrayList<>())
120
                        .add(resolveMessage(error.getDefaultMessage(), firstCode));
121
            }
122
        }
123 2 1. handleHandlerMethodValidationException : changed conditional boundary → NO_COVERAGE
2. handleHandlerMethodValidationException : negated conditional → NO_COVERAGE
        if (unnamedCounter > 0) {
124
            log.warn("Handler method validation: {} unnamed parameter(s); compile with -parameters to recover names",
125
                    unnamedCounter);
126
        }
127
128
        log.warn("Handler method validation failed: {}", errors);
129
130 1 1. handleHandlerMethodValidationException : replaced return value with null for com/project/auth/presentation/support/exception/ValidationExceptionHandler::handleHandlerMethodValidationException → NO_COVERAGE
        return ResponseEntity.status(ApiErrorHttpStatusMapper.map(PresentationErrorCode.INVALID_PARAMETER))
131
                .body(apiResultFactory.failure(
132
                        PresentationErrorCode.INVALID_PARAMETER.code(),
133
                        PresentationErrorCode.INVALID_PARAMETER.message(),
134
                        errors
135
                ));
136
    }
137
138
    /**
139
     * Bean Validation의 propertyPath를 JSON Pointer(RFC 6901) 형식으로 변환한다.
140
     * 예) {@code users[0].email} -> {@code /users/0/email}
141
     *
142
     * 서로 다른 파라미터가 우연히 같은 필드명으로 끝나도 충돌이 발생하지 않으며,
143
     * 클라이언트 입장에서 모호함이 없는 형식이다.
144
     * 메서드 파라미터 접두사(예: {@code findUser.id})는 출처 파라미터를 식별하므로
145
     * 첫 pointer 세그먼트로 보존한다.
146
     */
147
    private static String toJsonPointer(Path path) {
148
        StringBuilder builder = new StringBuilder();
149
        for (Path.Node node : path) {
150
            String name = node.getName();
151 1 1. toJsonPointer : negated conditional → NO_COVERAGE
            if (name == null) {
152
                continue;
153
            }
154
            builder.append('/').append(escapeJsonPointerSegment(name));
155
            Integer index = node.getIndex();
156 1 1. toJsonPointer : negated conditional → NO_COVERAGE
            if (index != null) {
157
                builder.append('/').append(index);
158 1 1. toJsonPointer : negated conditional → NO_COVERAGE
            } else if (node.getKey() != null) {
159
                builder.append('/').append(escapeJsonPointerSegment(String.valueOf(node.getKey())));
160
            }
161
        }
162 2 1. toJsonPointer : negated conditional → NO_COVERAGE
2. toJsonPointer : replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::toJsonPointer → NO_COVERAGE
        return builder.length() == 0 ? "/" : builder.toString();
163
    }
164
165
    private static String escapeJsonPointerSegment(String segment) {
166 1 1. escapeJsonPointerSegment : replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::escapeJsonPointerSegment → KILLED
        return segment.replace("~", "~0").replace("/", "~1");
167
    }
168
169
    /**
170
     * Spring {@code BindingResult.FieldError#getField()} 형식(예: {@code user.email},
171
     * {@code items[0].name})을 JSON Pointer(RFC 6901)로 변환한다.
172
     * ConstraintViolationException 핸들러와 키 형식을 통일하여, 응답 errors 맵의 키 규약을
173
     * 단일화한다(클라이언트가 두 가지 형식을 분기 처리할 필요가 없게 한다).
174
     */
175
    static String fieldFieldToJsonPointer(String field) {
176 2 1. fieldFieldToJsonPointer : negated conditional → KILLED
2. fieldFieldToJsonPointer : negated conditional → KILLED
        if (field == null || field.isEmpty()) {
177 1 1. fieldFieldToJsonPointer : replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::fieldFieldToJsonPointer → KILLED
            return "/";
178
        }
179
        StringBuilder builder = new StringBuilder();
180
        int index = 0;
181
        int length = field.length();
182 2 1. fieldFieldToJsonPointer : negated conditional → KILLED
2. fieldFieldToJsonPointer : changed conditional boundary → KILLED
        while (index < length) {
183
            char ch = field.charAt(index);
184 1 1. fieldFieldToJsonPointer : negated conditional → KILLED
            if (ch == '.') {
185 1 1. fieldFieldToJsonPointer : Changed increment from 1 to -1 → TIMED_OUT
                index++;
186
                continue;
187
            }
188 1 1. fieldFieldToJsonPointer : negated conditional → KILLED
            if (ch == '[') {
189
                int end = field.indexOf(']', index);
190 1 1. fieldFieldToJsonPointer : negated conditional → KILLED
                if (end == -1) {
191
                    builder.append('/').append(escapeJsonPointerSegment(field.substring(index)));
192
                    break;
193
                }
194 1 1. fieldFieldToJsonPointer : Replaced integer addition with subtraction → KILLED
                builder.append('/').append(escapeJsonPointerSegment(field.substring(index + 1, end)));
195 1 1. fieldFieldToJsonPointer : Replaced integer addition with subtraction → KILLED
                index = end + 1;
196
                continue;
197
            }
198
            int nextDot = field.indexOf('.', index);
199
            int nextBracket = field.indexOf('[', index);
200
            int next = minNonNegative(nextDot, nextBracket);
201 1 1. fieldFieldToJsonPointer : negated conditional → KILLED
            if (next == -1) {
202
                next = length;
203
            }
204
            builder.append('/').append(escapeJsonPointerSegment(field.substring(index, next)));
205
            index = next;
206
        }
207 2 1. fieldFieldToJsonPointer : negated conditional → KILLED
2. fieldFieldToJsonPointer : replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::fieldFieldToJsonPointer → KILLED
        return builder.length() == 0 ? "/" : builder.toString();
208
    }
209
210
    private static int minNonNegative(int a, int b) {
211 2 1. minNonNegative : changed conditional boundary → SURVIVED
2. minNonNegative : negated conditional → KILLED
        if (a < 0) {
212 1 1. minNonNegative : replaced int return with 0 for com/project/auth/presentation/support/exception/ValidationExceptionHandler::minNonNegative → TIMED_OUT
            return b;
213
        }
214 2 1. minNonNegative : changed conditional boundary → SURVIVED
2. minNonNegative : negated conditional → KILLED
        if (b < 0) {
215 1 1. minNonNegative : replaced int return with 0 for com/project/auth/presentation/support/exception/ValidationExceptionHandler::minNonNegative → TIMED_OUT
            return a;
216
        }
217 1 1. minNonNegative : replaced int return with 0 for com/project/auth/presentation/support/exception/ValidationExceptionHandler::minNonNegative → TIMED_OUT
        return Math.min(a, b);
218
    }
219
220
    private static String resolveMessage(String defaultMessage, String fallbackCode) {
221 2 1. resolveMessage : negated conditional → NO_COVERAGE
2. resolveMessage : negated conditional → NO_COVERAGE
        if (defaultMessage != null && !defaultMessage.isBlank()) {
222 1 1. resolveMessage : replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::resolveMessage → NO_COVERAGE
            return defaultMessage;
223
        }
224 2 1. resolveMessage : negated conditional → NO_COVERAGE
2. resolveMessage : negated conditional → NO_COVERAGE
        if (fallbackCode != null && !fallbackCode.isBlank()) {
225 1 1. resolveMessage : replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::resolveMessage → NO_COVERAGE
            return fallbackCode;
226
        }
227 1 1. resolveMessage : replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::resolveMessage → NO_COVERAGE
        return UNRESOLVED_VIOLATION_MESSAGE;
228
    }
229
}

Mutations

62

1.1
Location : lambda$handleValidationException$0
Killed by : none
replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleValidationException$0 → NO_COVERAGE

66

1.1
Location : lambda$handleValidationException$1
Killed by : none
replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleValidationException$1 → NO_COVERAGE

72

1.1
Location : handleValidationException
Killed by : none
replaced return value with null for com/project/auth/presentation/support/exception/ValidationExceptionHandler::handleValidationException → NO_COVERAGE

87

1.1
Location : lambda$handleConstraintViolationException$2
Killed by : none
replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleConstraintViolationException$2 → NO_COVERAGE

93

1.1
Location : handleConstraintViolationException
Killed by : none
replaced return value with null for com/project/auth/presentation/support/exception/ValidationExceptionHandler::handleConstraintViolationException → NO_COVERAGE

110

1.1
Location : handleHandlerMethodValidationException
Killed by : none
negated conditional → NO_COVERAGE

114

1.1
Location : handleHandlerMethodValidationException
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

118

1.1
Location : handleHandlerMethodValidationException
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : handleHandlerMethodValidationException
Killed by : none
changed conditional boundary → NO_COVERAGE

3.3
Location : handleHandlerMethodValidationException
Killed by : none
negated conditional → NO_COVERAGE

119

1.1
Location : lambda$handleHandlerMethodValidationException$3
Killed by : none
replaced return value with Collections.emptyList for com/project/auth/presentation/support/exception/ValidationExceptionHandler::lambda$handleHandlerMethodValidationException$3 → NO_COVERAGE

123

1.1
Location : handleHandlerMethodValidationException
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : handleHandlerMethodValidationException
Killed by : none
negated conditional → NO_COVERAGE

130

1.1
Location : handleHandlerMethodValidationException
Killed by : none
replaced return value with null for com/project/auth/presentation/support/exception/ValidationExceptionHandler::handleHandlerMethodValidationException → NO_COVERAGE

151

1.1
Location : toJsonPointer
Killed by : none
negated conditional → NO_COVERAGE

156

1.1
Location : toJsonPointer
Killed by : none
negated conditional → NO_COVERAGE

158

1.1
Location : toJsonPointer
Killed by : none
negated conditional → NO_COVERAGE

162

1.1
Location : toJsonPointer
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : toJsonPointer
Killed by : none
replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::toJsonPointer → NO_COVERAGE

166

1.1
Location : escapeJsonPointerSegment
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::escapeJsonPointerSegment → KILLED

176

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:empty_or_null_returns_root_pointer(java.lang.String)]
negated conditional → KILLED

2.2
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
negated conditional → KILLED

177

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:empty_or_null_returns_root_pointer(java.lang.String)]
replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::fieldFieldToJsonPointer → KILLED

182

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
negated conditional → KILLED

2.2
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
changed conditional boundary → KILLED

184

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
negated conditional → KILLED

185

1.1
Location : fieldFieldToJsonPointer
Killed by : none
Changed increment from 1 to -1 → TIMED_OUT

188

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:indexed_path_extracts_index_segment(java.lang.String, int)]
negated conditional → KILLED

190

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:indexed_path_extracts_index_segment(java.lang.String, int)]
negated conditional → KILLED

194

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:indexed_path_extracts_index_segment(java.lang.String, int)]
Replaced integer addition with subtraction → KILLED

195

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:indexed_path_extracts_index_segment(java.lang.String, int)]
Replaced integer addition with subtraction → KILLED

201

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
negated conditional → KILLED

207

1.1
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
negated conditional → KILLED

2.2
Location : fieldFieldToJsonPointer
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:result_escapes_tilde_in_segments(java.lang.String, java.lang.String)]
replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::fieldFieldToJsonPointer → KILLED

211

1.1
Location : minNonNegative
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:indexed_path_extracts_index_segment(java.lang.String, int)]
negated conditional → KILLED

2.2
Location : minNonNegative
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

212

1.1
Location : minNonNegative
Killed by : none
replaced int return with 0 for com/project/auth/presentation/support/exception/ValidationExceptionHandler::minNonNegative → TIMED_OUT

214

1.1
Location : minNonNegative
Killed by : com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest.[engine:jqwik]/[class:com.project.auth.presentation.support.exception.JsonPointerConversionPropertyTest]/[property:nested_indexed_path_combines_correctly(java.lang.String, int, java.lang.String)]
negated conditional → KILLED

2.2
Location : minNonNegative
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

215

1.1
Location : minNonNegative
Killed by : none
replaced int return with 0 for com/project/auth/presentation/support/exception/ValidationExceptionHandler::minNonNegative → TIMED_OUT

217

1.1
Location : minNonNegative
Killed by : none
replaced int return with 0 for com/project/auth/presentation/support/exception/ValidationExceptionHandler::minNonNegative → TIMED_OUT

221

1.1
Location : resolveMessage
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : resolveMessage
Killed by : none
negated conditional → NO_COVERAGE

222

1.1
Location : resolveMessage
Killed by : none
replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::resolveMessage → NO_COVERAGE

224

1.1
Location : resolveMessage
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : resolveMessage
Killed by : none
negated conditional → NO_COVERAGE

225

1.1
Location : resolveMessage
Killed by : none
replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::resolveMessage → NO_COVERAGE

227

1.1
Location : resolveMessage
Killed by : none
replaced return value with "" for com/project/auth/presentation/support/exception/ValidationExceptionHandler::resolveMessage → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.19.1