From f98f4b1d78165ea0538ec85492301cb09149bf1b Mon Sep 17 00:00:00 2001 From: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> Date: Tue, 1 Sep 2026 19:20:40 +0800 Subject: [PATCH] fix(review): require review score contract Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com> --- .../iflytek/skillhub/dto/SkillReviewRequest.java | 3 ++- .../controller/SkillReviewControllerTest.java | 13 +++++++++++++ web/src/api/generated/schema.d.ts | 2 +- web/src/features/social/skill-reviews.test.tsx | 5 +++++ web/src/i18n/locales/en.json | 2 ++ web/src/i18n/locales/ru.json | 4 ++++ web/src/i18n/locales/zh.json | 1 + 7 files changed, 28 insertions(+), 2 deletions(-) diff --git a/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillReviewRequest.java b/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillReviewRequest.java index c38e9fb0..1ca50751 100644 --- a/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillReviewRequest.java +++ b/server/skillhub-app/src/main/java/com/iflytek/skillhub/dto/SkillReviewRequest.java @@ -3,9 +3,10 @@ package com.iflytek.skillhub.dto; import jakarta.validation.constraints.Max; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; public record SkillReviewRequest( - @Min(1) @Max(5) short score, + @NotNull @Min(1) @Max(5) Short score, @NotBlank @Size(max = 2000) String reviewText ) {} diff --git a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillReviewControllerTest.java b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillReviewControllerTest.java index 15a9761c..b07f042b 100644 --- a/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillReviewControllerTest.java +++ b/server/skillhub-app/src/test/java/com/iflytek/skillhub/controller/SkillReviewControllerTest.java @@ -92,6 +92,19 @@ class SkillReviewControllerTest { verify(reviewAppService).upsert(eq(10L), eq("user-42"), eq((short) 4), eq("Useful"), any(), eq(Set.of())); } + @Test + void reviewScoreIsRequiredByTheApiContract() throws Exception { + var principal = principal("user-42", Set.of()); + + mockMvc.perform(put("/api/v1/skills/10/reviews/me") + .with(authentication(authToken(principal, "ROLE_USER"))) + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content("{\"reviewText\":\"Useful\"}")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(400)); + } + @Test void skillAdminCanHideReview() throws Exception { var principal = principal("admin", Set.of("SKILL_ADMIN")); diff --git a/web/src/api/generated/schema.d.ts b/web/src/api/generated/schema.d.ts index 37dce591..8fbd4236 100644 --- a/web/src/api/generated/schema.d.ts +++ b/web/src/api/generated/schema.d.ts @@ -3747,7 +3747,7 @@ export interface components { }; SkillReviewRequest: { /** Format: int32 */ - score?: number; + score: number; reviewText: string; }; ApiResponseSkillReviewMeResponse: { diff --git a/web/src/features/social/skill-reviews.test.tsx b/web/src/features/social/skill-reviews.test.tsx index cd82ff59..4a63d12f 100644 --- a/web/src/features/social/skill-reviews.test.tsx +++ b/web/src/features/social/skill-reviews.test.tsx @@ -143,6 +143,11 @@ describe('skill reviews', () => { expect(locale.skillReviews.hide).toBeTruthy() expect(locale.skillReviews.restore).toBeTruthy() } + expect(en.skillReviews.count_one).toBe('{{count}} review') + expect(en.skillReviews.count_other).toBe('{{count}} reviews') + expect(ru.skillReviews.count_one).toBeTruthy() + expect(ru.skillReviews.count_few).toBeTruthy() + expect(ru.skillReviews.count_many).toBeTruthy() }) it('lets an authenticated author clear existing text when the skill is not interactable', () => { diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 0add7966..234fb599 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1302,6 +1302,8 @@ "skillReviews": { "title": "User reviews", "count": "{{count}} reviews", + "count_one": "{{count}} review", + "count_other": "{{count}} reviews", "write": "Write a review", "edit": "Edit my review", "scoreLabel": "Your score", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 9324554b..3657874b 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1514,6 +1514,10 @@ "skillReviews": { "title": "Отзывы пользователей", "count": "Отзывов: {{count}}", + "count_one": "{{count}} отзыв", + "count_few": "{{count}} отзыва", + "count_many": "{{count}} отзывов", + "count_other": "{{count}} отзыва", "write": "Написать отзыв", "edit": "Изменить мой отзыв", "scoreLabel": "Ваша оценка", diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json index 64999f59..c8231fc9 100644 --- a/web/src/i18n/locales/zh.json +++ b/web/src/i18n/locales/zh.json @@ -1302,6 +1302,7 @@ "skillReviews": { "title": "用户评价", "count": "共 {{count}} 条评价", + "count_other": "共 {{count}} 条评价", "write": "写评价", "edit": "编辑我的评价", "scoreLabel": "你的评分",