Сообщение об ошибке:
Код: Выделить всё
org.springframework.web.util.pattern.PatternParseException:
Missing preceding open capture character before variable name{
...
Invalid mapping pattern detected: /recommendation}
^
Код: Выделить всё
public interface RecommendationService {
@PostMapping(
value = "/recommendation",
consumes = "application/json",
produces = "application/json")
Recommendation createRecommendation(@RequestBody Recommendation body);
@GetMapping(
value = "/recommendation",
produces = "application/json")
List getRecommendations(@RequestParam(value = "productId") int productId);
@DeleteMapping(value = "/recommendation")
void deleteRecommendation(@RequestParam(value = "productId") int productId);
}
Код: Выделить всё
@RestController
public class RecommendationServiceImpl implements RecommendationService {
private final ServiceUtil serviceUtil;
private final RecommendationRepository repository;
private final RecommendationMapper mapper;
public RecommendationServiceImpl(ServiceUtil serviceUtil, RecommendationRepository repository, @Qualifier("recommendationMapperImpl") RecommendationMapper mapper) {
this.serviceUtil = serviceUtil;
this.repository = repository;
this.mapper = mapper;
}
@Override
public Recommendation createRecommendation(Recommendation body) {
try {
RecommendationEntity entity = mapper.apiToEntity(body);
RecommendationEntity newEntity = repository.save(entity);
return mapper.entityToApi(newEntity);
} catch (DuplicateKeyException e) {
throw new InvalidInputException("Duplicate key, Product Id: " + body.getProductId() + ", Recommendation Id:" + body.getRecommendationId());
}
}
@Override
public List getRecommendations(int productId) {
if (productId < 1) {
throw new InvalidInputException("Invalid productId: " + productId);
}
List entityList = repository.findByProductId(productId);
List list = mapper.entityListToApiList(entityList);
list.forEach(e -> e.setServiceAddress(serviceUtil.getServiceAddress()));
return list;
}
@Override
public void deleteRecommendation(int productId) {
repository.deleteAll(repository.findByProductId(productId));
}
}
Код: Выделить всё
@RestController
public interface RecommendationRepository extends CrudRepository {
List findByProductId(int productId);
}
- Искал во всех файлах /recommendation}, используя как IDE, так и grep -ri "/recommendation}" .
Найдено только одно совпадение:
./target/surefire-reports/TEST-com.example.recommendation_service.RecommendationServiceApplicationTests.xml:/recommendation - Очистил и пересобрал проект (./mvnw clean install)
- Проверил точное совпадение всех сопоставлений конечных точек /recommendation (без опечаток)
- Подтверждено, что приложение работает успешно при прямом запуске (сбой только во время тестов)
- Экспериментально изменены пути конечных точек к /recommendation1, /recommendation2 и т. д. как в контроллере, так и в тестах.
- Сообщение об ошибке по-прежнему отображается /recommendation} (без цифр)
- Ошибка появляется только при запуске тестов
- При удалении тестового файла ошибка исчезает
- Ошибка появляется в XML-отчете о тестировании, а не в исходный код
Код: Выделить всё
@SpringBootTest(webEnvironment = RANDOM_PORT)
class RecommendationServiceApplicationTests extends MongoDbTestBase {
@Autowired
private WebTestClient client;
@Autowired
private RecommendationRepository repository;
@BeforeEach
void setupDb() {
repository.deleteAll();
}
@Test
void getRecommendationsByProductId() {
int productId = 1;
postAndVerifyRecommendation(productId, 1, OK);
postAndVerifyRecommendation(productId, 2, OK);
postAndVerifyRecommendation(productId, 3, OK);
assertEquals(3, repository.findByProductId(productId).size());
getAndVerifyRecommendationsByProductId(productId, OK)
.jsonPath("$.length()").isEqualTo(3)
.jsonPath("$[2].productId").isEqualTo(productId)
.jsonPath("$[2].recommendationId").isEqualTo(3);
}
@Test
void duplicateError() {
int productId = 1;
int recommendationId = 1;
postAndVerifyRecommendation(productId, recommendationId, OK)
.jsonPath("$.productId").isEqualTo(productId)
.jsonPath("$.recommendationId").isEqualTo(recommendationId);
assertEquals(1, repository.count());
postAndVerifyRecommendation(productId, recommendationId, UNPROCESSABLE_ENTITY)
.jsonPath("$.message").isEqualTo("Duplicate key, Product Id: 1, Recommendation Id:1");
assertEquals(1, repository.count());
}
@Test
void deleteRecommendations() {
int productId = 1;
int recommendationId = 1;
postAndVerifyRecommendation(productId, recommendationId, OK);
assertEquals(1, repository.findByProductId(productId).size());
deleteAndVerifyRecommendationsByProductId(productId, OK);
assertEquals(0, repository.findByProductId(productId).size());
deleteAndVerifyRecommendationsByProductId(productId, OK);
}
@Test
void getRecommendationsMissingParameter() {
getAndVerifyRecommendationsByProductId("", BAD_REQUEST)
.jsonPath("$.path").isEqualTo("/recommendation");
}
@Test
void getRecommendationsInvalidParameter() {
getAndVerifyRecommendationsByProductId("?productId=no-integer", BAD_REQUEST)
.jsonPath("$.path").isEqualTo("/recommendation");
}
@Test
void getRecommendationsNotFound() {
getAndVerifyRecommendationsByProductId("?productId=113", OK)
.jsonPath("$.length()").isEqualTo(0);
}
@Test
void getRecommendationsInvalidParameterNegativeValue() {
int productIdInvalid = -1;
getAndVerifyRecommendationsByProductId("?productId=" + productIdInvalid, UNPROCESSABLE_ENTITY)
.jsonPath("$.message").isEqualTo("Invalid productId: " + productIdInvalid);
}
private WebTestClient.BodyContentSpec getAndVerifyRecommendationsByProductId(int productId, HttpStatus expectedStatus) {
return getAndVerifyRecommendationsByProductId("?productId=" + productId, expectedStatus);
}
private WebTestClient.BodyContentSpec getAndVerifyRecommendationsByProductId(String productIdQuery, HttpStatus expectedStatus) {
return client.get()
.uri("/recommendation" + productIdQuery)
.accept(APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(expectedStatus)
.expectHeader().contentType(APPLICATION_JSON)
.expectBody();
}
private WebTestClient.BodyContentSpec postAndVerifyRecommendation(int productId, int recommendationId, HttpStatus expectedStatus) {
Recommendation recommendation = new Recommendation(productId, recommendationId, "Author " + recommendationId, recommendationId, "Content " + recommendationId, "SA");
return client.post()
.uri("/recommendation")
.body(just(recommendation), Recommendation.class)
.accept(APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(expectedStatus)
.expectHeader().contentType(APPLICATION_JSON)
.expectBody();
}
private WebTestClient.BodyContentSpec deleteAndVerifyRecommendationsByProductId(int productId, HttpStatus expectedStatus) {
return client.delete()
.uri("/recommendation?productId=" + productId)
.accept(APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(expectedStatus)
.expectBody();
}
}
Как может возникнуть ошибка анализа шаблона для /recommendation}, если в моем коде явно используется /recommendation без фигурных скобок? Где мне искать этот призрачный узор?